2017-01-13 40 views
1

我试图用内部类与AsyncTask工作。但我面临着一个问题泄漏内存。所以我决定编写一些测试代码,以便找出问题所在。在下面的代码中,我试图运行一个从0到100计数的任务。比我在任务运行时离开活动。我得到了一个_InterruptedException_Activity泄露(使用泄露金丝雀),然后我的应用程序被冻结,直到它坠毁。我不明白为什么,因为任务被取消了,我留下了活动。运行时切换活动AsyncTask会泄漏内存吗?

这里是我的小示例代码:

public class MainActivity extends AppCompatActivity { 
TextView textView; 
BackgroundTask _task; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    textView = (TextView) findViewById(R.id.textView); 

    _task = new BackgroundTask(textView); 
    _task.execute(); 
} 

@Override 
protected void onPause() { 
    _task.cancel(true); 
    super.onPause(); 
} 

@Override 
protected void onResume() { 
    if (_task.isCancelled()){ 
     _task = new BackgroundTask(textView); 
     _task.execute(); 
    } 
    super.onResume(); 
} 

private class BackgroundTask extends AsyncTask<Void, Integer, String> { 
    private WeakReference<TextView> _textView; 

    public BackgroundTask(TextView textView) { 
     this._textView = new WeakReference<TextView>(textView); 
    } 

    @Override 
    protected String doInBackground(Void... params) { 
     for (int i = 0; i <= 100; i++) 
      try { 
       Thread.sleep(1000); 
       publishProgress(i); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     return "DONE"; 
    } 

    @Override 
    protected void onProgressUpdate(Integer... values) { 
     TextView textView = _textView.get(); 
     if (textView != null) { 
      textView.setText(values[0] + " ."); 
     } 
     Log.d("==> ",values[0]+" "); 
    } 

    @Override 
    protected void onPostExecute(String result) { 
     TextView textView = _textView.get(); 
      if (textView != null) { 
      textView.setText(result); 
    } 
     //MainActivity.this.isFinishing(); 
    } 
    } 

} 

这是我的日志:

D/==>: 0 
D/==>: 1 
D/==>: 2 
W/System.err: java.lang.InterruptedException 
W/System.err:  at java.lang.Thread.sleep(Native Method) 
W/System.err:  at java.lang.Thread.sleep(Thread.java:1031) 
W/System.err:  at java.lang.Thread.sleep(Thread.java:985) 
W/System.err:  at com.example.longluong.test_app.MainActivity$BackgroundTask.doInBackground(MainActivity.java:58) 
W/System.err:  at com.example.longluong.test_app.MainActivity$BackgroundTask.doInBackground(MainActivity.java:47) 
W/System.err:  at android.os.AsyncTask$2.call(AsyncTask.java:295) 
W/System.err:  at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
W/System.err:  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
W/System.err:  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
W/System.err:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
W/System.err:  at java.lang.Thread.run(Thread.java:818) 
I/art: Waiting for a blocking GC Explicit 
I/art: Starting a blocking GC Explicit 
I/art: Explicit concurrent mark sweep GC freed 343(27KB) AllocSpace objects, 0(0B) LOS objects, 82% free, 1267KB/7MB, paused 488us total 18.818ms 
I/art: hprof: heap dump "/storage/emulated/0/Download/leakcanary-com.example.longluong.test_app/0a1c4ebe-238f-4156-a317-ed8d454de769_pending.hprof" starting... 
I/art: hprof: heap dump completed (12MB) in 7.001s 
D/LeakCanary: In com.example.longluong.test_app:1.0:1. 
D/LeakCanary: * com.example.longluong.test_app.MainActivity has leaked: 
D/LeakCanary: * GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1') 
D/LeakCanary: * references com.example.longluong.test_app.MainActivity$BackgroundTask.this$0 
D/LeakCanary: * leaks com.example.longluong.test_app.MainActivity instance 
D/LeakCanary: * Retaining: 6.7 KB. 
D/LeakCanary: * Reference Key: 4db87806-d761-42c8-a874-9682d7477106 
D/LeakCanary: * Device: LENOVO Lenovo Lenovo TB2-X30F TB2-X30F 
D/LeakCanary: * Android Version: 6.0.1 API: 23 LeakCanary: 1.5 00f37f5 
D/LeakCanary: * Durations: watch=5055ms, gc=125ms, heap dump=7205ms, analysis=26304ms 
D/LeakCanary: * Details: 
D/LeakCanary: * Instance of java.lang.Thread 
D/LeakCanary: | static NANOS_PER_MILLI = 1000000 
D/LeakCanary: | static defaultUncaughtHandler = [email protected] (0x22c01180) 
D/LeakCanary: | static count = 902 
D/LeakCanary: | static MAX_PRIORITY = 10 
D/LeakCanary: | static $staticOverhead = byte[48]@1873454545 (0x6faaa5d1) 
D/LeakCanary: | static NORM_PRIORITY = 5 
D/LeakCanary: | static MIN_PRIORITY = 1 
D/LeakCanary: | contextClassLoader = [email protected] (0x22c02d80) 
D/LeakCanary: | daemon = false 
D/LeakCanary: | group = [email protected] (0x6f8e8818) 
D/LeakCanary: | hasBeenStarted = true 
D/LeakCanary: | id = 900 
D/LeakCanary: | inheritableValues = null 
D/LeakCanary: | interruptActions = [email protected] (0x22d84ba0) 
D/LeakCanary: | localValues = [email protected] (0x22d84bc0) 
D/LeakCanary: | lock = [email protected] (0x22c01100) 
D/LeakCanary: | name = [email protected] (0x22d83340) 
D/LeakCanary: | nativePeer = -1218480552 
D/LeakCanary: | parkBlocker = null 
D/LeakCanary: | parkState = 1 
D/LeakCanary: | priority = 5 
D/LeakCanary: | stackSize = 0 
D/LeakCanary: | target = [email protected] (0x22c001f0) 
D/LeakCanary: | uncaughtHandler = null 
D/LeakCanary: | shadow$_klass_ = java.lang.Thread 
D/LeakCanary: | shadow$_monitor_ = 0 
D/LeakCanary: * Instance of com.example.longluong.test_app.MainActivity$BackgroundTask 
D/LeakCanary: | static $staticOverhead = byte[16]@583275521 (0x22c41401) 
D/LeakCanary: | static serialVersionUID = 0 
D/LeakCanary: | static $change = null 
D/LeakCanary: | _textView = [email protected] (0x22d69fa0) 
D/LeakCanary: | this$0 = [email protected] (0x22c8d300) 
D/LeakCanary: | mCancelled = [email protected] (0x22d6c1a0) 
D/LeakCanary: | mFuture = [email protected] (0x22c020c0) 
D/LeakCanary: | mStatus = [email protected] (0x6f924e30) 
D/LeakCanary: | mTaskInvoked = [email protected] (0x22d6c1b0) 
D/LeakCanary: | mWorker = [email protected] (0x22c01120) 
D/LeakCanary: | shadow$_klass_ = com.example.longluong.test_app.MainActivity$BackgroundTask 
D/LeakCanary: | shadow$_monitor_ = 0 
D/LeakCanary: * Instance of com.example.longluong.test_app.MainActivity 
D/LeakCanary: | static $staticOverhead = byte[16]@584134657 (0x22d13001) 
D/LeakCanary: | static serialVersionUID = 0 
D/LeakCanary: | static $change = null 
D/LeakCanary: | _task = [email protected]8704 (0x22c001c0) 
D/LeakCanary: | textView = [email protected] (0x22d07800) 
D/LeakCanary: | mDelegate = [email protected] (0x22c0da60) 
D/LeakCanary: | mEatKeyUpEvent = false 
D/LeakCanary: | mResources = null 
D/LeakCanary: | mThemeId = 2131230877 
D/LeakCanary: | mCreated = true 
D/LeakCanary: | mFragments = [email protected] (0x22d6c1c0) 
D/LeakCanary: | mHandler = [email protected] (0x22d69fc0) 
D/LeakCanary: | mMediaController = null 
D/LeakCanary: | mNextCandidateRequestIndex = 0 
D/LeakCanary: | mOptionsMenuInvalidated = false 
D/LeakCanary: | mPendingFragmentActivityResults = [email protected] (0x22d69fe0) 
D/LeakCanary: | mReallyStopped = true 
D/LeakCanary: | mRequestedPermissionsFromFragment = false 
D/LeakCanary: | mResumed = false 
D/LeakCanary: | mRetaining = false 
D/LeakCanary: | mStopped = true 
D/LeakCanary: | mStartedActivityFromFragment = false 
D/LeakCanary: | mStartedIntentSenderFromFragment = false 
D/LeakCanary: | mActionBar = null 
D/LeakCanary: | mActionModeTypeStarting = 0 
D/LeakCanary: | mActivityInfo = android.content.pm.Activ[email protected] (0x22d66300) 
D/LeakCanary: | mActivityTransitionState = [email protected] (0x22d41ec0) 
D/LeakCanary: | mApplication = [email protected] (0x22d78040) 
D/LeakCanary: | mCalled = true 
D/LeakCanary: | mChangeCanvasToTranslucent = false 
D/LeakCanary: | mChangingConfigurations = false 
D/LeakCanary: | mComponent = [email protected] (0x22d6c1d0) 
D/LeakCanary: | mConfigChangeFlags = 0 
D/LeakCanary: | mCurrentConfig = [email protected] (0x22d6d100) 
D/LeakCanary: | mDecor = null 
D/LeakCanary: | mDefaultKeyMode = 0 
D/LeakCanary: | mDefaultKeySsb = null 
D/LeakCanary: | mDestroyed = true 
D/LeakCanary: | mDoReportFullyDrawn = false 
D/LeakCanary: | mEmbeddedID = null 
D/LeakCanary: | mEnableDefaultActionBarUp = false 
D/LeakCanary: | mEnterTransitionListener = [email protected] (0x6f8e9aa0) 
D/LeakCanary: | mExitTransitionListener = [email protected] (0x6f8e9aa0) 
D/LeakCanary: | mFinished = true 
D/LeakCanary: | mFragments = [email protected] (0x22d6c1e0) 
D/LeakCanary: | mHandler = [email protected] (0x22d78060) 
D/LeakCanary: | mHasCurrentPermissionsRequest = false 
D/LeakCanary: | mIdent = 115814648 
D/LeakCanary: | mInstanceTracker = [email protected] (0x22d6c1f0) 
D/LeakCanary: | mInstrumentation = [email protected] (0x22d37830) 
D/LeakCanary: | mIntent = [email protected] (0x22d41f00) 
D/LeakCanary: | mLastNonConfigurationInstances = null 
D/LeakCanary: | mMainThread = [email protected] (0x22c03100) 
D/LeakCanary: | mManagedCursors = [email protected] (0x22d78080) 
D/LeakCanary: | mManagedDialogs = null 
D/LeakCanary: | mMenuInflater = null 
D/LeakCanary: | mParent = null 
D/LeakCanary: | mReferrer = null 
D/LeakCanary: | mResultCode = 0 
D/LeakCanary: | mResultData = null 
D/LeakCanary: | mResumed = false 
D/LeakCanary: | mSearchEvent = null 
D/LeakCanary: | mSearchManager = null 
D/LeakCanary: | mStartedActivity = false 
D/LeakCanary: | mStopped = true 
D/LeakCanary: | mTemporaryPause = false 
D/LeakCanary: | mTitle = [email protected] (0x22d780a0) 
D/LeakCanary: | mTitleColor = 0 
D/LeakCanary: | mTitleReady = true 
D/LeakCanary: | mToken = [email protected] (0x22d780c0) 
D/LeakCanary: | mTranslucentCallback = null 
D/LeakCanary: | mUiThread = [email protected] (0x733cc258) 
D/LeakCanary: | mVisibleBehind = false 
D/LeakCanary: | mVisibleFromClient = true 
D/LeakCanary: | mVisibleFromServer = true 
D/LeakCanary: | mVoiceInteractor = null 
D/LeakCanary: | mWindow = [email protected] (0x22c4c0a0) 
D/LeakCanary: | mWindowAdded = true 
D/LeakCanary: | mWindowManager = [email protected] (0x22d780e0) 
D/LeakCanary: | mInflater = [email protected] (0x22d68340) 
D/LeakCanary: | mOverrideConfiguration = null 
D/LeakCanary: | mResources = [email protected] (0x22d37880) 
D/LeakCanary: | mTheme = [email protected] (0x22d78100) 
D/LeakCanary: | mThemeResource = 2131230877 
D/LeakCanary: | mBase = [email protected] (0x22d66380) 
D/LeakCanary: | shadow$_klass_ = com.example.longluong.test_app.MainActivity 
D/LeakCanary: | shadow$_monitor_ = 1266075474 
D/LeakCanary: * Excluded Refs: 
D/LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mNextServedView 
D/LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mServedView 
D/LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mServedInputConnection 
D/LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mCurRootView 
D/LeakCanary: | Field: android.os.UserManager.mContext 
D/LeakCanary: | Field: android.net.ConnectivityManager.sInstance 
D/LeakCanary: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always) 
D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always) 
D/LeakCanary: | Thread:main (always) 
D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always) 
D/LeakCanary: | Class:java.lang.ref.WeakReference (always) 
D/LeakCanary: | Class:java.lang.ref.SoftReference (always) 
D/LeakCanary: | Class:java.lang.ref.PhantomReference (always) 
D/LeakCanary: | Class:java.lang.ref.Finalizer (always) 
D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always) 
+0

也许是因为您使用的是来自Activity的View, – Jonas452

+0

发布错误代码。你是否按下按钮或回家离开活动? –

+0

你想执行任何后台任务,如果我移动到第二个活动,那么该任务不应该被取消吗? –

回答

1

InterruptedExceptionfor循环仍在循环,当你发布你的进步你的线程被取消等等你需要检查你的线程是否被取消。

当你的应用程序崩溃时,你的TextView仍然有WeakReference这就是为什么你有内存泄漏。你需要清除WeakReferenceonPostExecuteonCancelled

更改:

doInBackground

@Override 
protected String doInBackground(Void... params) { 
    for (int i = 0; i <= 100; i++){ 
     if(isCancelled()) 
      break; 
     try { 
      Thread.sleep(1000); 
      // After 1 second may be thread is cancelled 
      if(!isCancelled()) 
       publishProgress(i); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
      break; 
     } 
    } 
    return "DONE"; 
} 

onProgressUpdate

@Override 
protected void onProgressUpdate(Integer... values) { 
    if(_textView != null){ 
     TextView textView = _textView.get(); 
     if (textView != null) { 
      textView.setText(values[0] + " ."); 
     } 
     Log.d("==> ",values[0]+" "); 
    } 
} 

onPostExecute

@Override 
protected void onPostExecute(String result) { 
    // safe check 
    if(_textView != null){ 
     TextView textView = _textView.get(); 
     if (textView != null) { 
      textView.setText(result); 
     _textView.clear(); 
    } 
    _textView = null; 
} 

并添加onCancelled

@Override 
protected void onCancelled() { 
    // safe check 
    super.onCancelled(); 
    if(_textView != null) 
     _textView.clear(); 
    _textView = null; 
} 
+0

感谢您的回答。但是你确定onProgressUpdate()中的_textView.clear()是否在正确的位置?因为我的textView不会改变 –

+0

在离开活动后,我仍然在我的日志中发现** W/System.err:java.lang.InterruptedException **。这是正常的吗? –

+1

对不起,从'onProgressUpdate'中移除'_textView.clear()'。是的,日志是正常的。 –