2017-01-13 76 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()'。是的,日誌是正常的。 –