运行AsyncTask时切换活动可能会泄漏内存?

时间:2017-01-13 11:42:14

标签: android memory-leaks android-asynctask

我正在尝试使用内部类AsyncTask。但是我遇到了泄漏记忆的问题。所以我决定写一些测试代码,以找出问题所在。在下面的代码中,我试图运行一个从0到100的任务。比我在任务运行时离开了活动。我得到_InterruptedException_Activity泄露(使用泄漏金丝雀),然后我的应用程序被冻结,直到它崩溃。我无法理解为什么,因为任务被取消了,我离开了活动。

这是我的小样本代码:

 
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 = com.android.internal.os.RuntimeInit$UncaughtHandler@583012736 (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 = dalvik.system.PathClassLoader@583019904 (0x22c02d80)
D/LeakCanary: |   daemon = false
D/LeakCanary: |   group = java.lang.ThreadGroup@1871611928 (0x6f8e8818)
D/LeakCanary: |   hasBeenStarted = true
D/LeakCanary: |   id = 900
D/LeakCanary: |   inheritableValues = null
D/LeakCanary: |   interruptActions = java.util.ArrayList@584600480 (0x22d84ba0)
D/LeakCanary: |   localValues = java.lang.ThreadLocal$Values@584600512 (0x22d84bc0)
D/LeakCanary: |   lock = java.lang.Object@583012608 (0x22c01100)
D/LeakCanary: |   name = java.lang.String@584594240 (0x22d83340)
D/LeakCanary: |   nativePeer = -1218480552
D/LeakCanary: |   parkBlocker = null
D/LeakCanary: |   parkState = 1
D/LeakCanary: |   priority = 5
D/LeakCanary: |   stackSize = 0
D/LeakCanary: |   target = java.util.concurrent.ThreadPoolExecutor$Worker@583008752 (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 = java.lang.ref.WeakReference@584490912 (0x22d69fa0)
D/LeakCanary: |   this$0 = com.example.longluong.test_app.MainActivity@583586560 (0x22c8d300)
D/LeakCanary: |   mCancelled = java.util.concurrent.atomic.AtomicBoolean@584499616 (0x22d6c1a0)
D/LeakCanary: |   mFuture = android.os.AsyncTask$3@583016640 (0x22c020c0)
D/LeakCanary: |   mStatus = android.os.AsyncTask$Status@1871859248 (0x6f924e30)
D/LeakCanary: |   mTaskInvoked = java.util.concurrent.atomic.AtomicBoolean@584499632 (0x22d6c1b0)
D/LeakCanary: |   mWorker = android.os.AsyncTask$2@583012640 (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 = com.example.longluong.test_app.MainActivity$BackgroundTask@583008704 (0x22c001c0)
D/LeakCanary: |   textView = android.support.v7.widget.AppCompatTextView@584087552 (0x22d07800)
D/LeakCanary: |   mDelegate = android.support.v7.app.AppCompatDelegateImplV23@583064160 (0x22c0da60)
D/LeakCanary: |   mEatKeyUpEvent = false
D/LeakCanary: |   mResources = null
D/LeakCanary: |   mThemeId = 2131230877
D/LeakCanary: |   mCreated = true
D/LeakCanary: |   mFragments = android.support.v4.app.FragmentController@584499648 (0x22d6c1c0)
D/LeakCanary: |   mHandler = android.support.v4.app.FragmentActivity$1@584490944 (0x22d69fc0)
D/LeakCanary: |   mMediaController = null
D/LeakCanary: |   mNextCandidateRequestIndex = 0
D/LeakCanary: |   mOptionsMenuInvalidated = false
D/LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@584490976 (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.ActivityInfo@584475392 (0x22d66300)
D/LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@584326848 (0x22d41ec0)
D/LeakCanary: |   mApplication = com.example.longluong.test_app.LeakyApp@584548416 (0x22d78040)
D/LeakCanary: |   mCalled = true
D/LeakCanary: |   mChangeCanvasToTranslucent = false
D/LeakCanary: |   mChangingConfigurations = false
D/LeakCanary: |   mComponent = android.content.ComponentName@584499664 (0x22d6c1d0)
D/LeakCanary: |   mConfigChangeFlags = 0
D/LeakCanary: |   mCurrentConfig = android.content.res.Configuration@584503552 (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 = android.app.SharedElementCallback$1@1871616672 (0x6f8e9aa0)
D/LeakCanary: |   mExitTransitionListener = android.app.SharedElementCallback$1@1871616672 (0x6f8e9aa0)
D/LeakCanary: |   mFinished = true
D/LeakCanary: |   mFragments = android.app.FragmentController@584499680 (0x22d6c1e0)
D/LeakCanary: |   mHandler = android.os.Handler@584548448 (0x22d78060)
D/LeakCanary: |   mHasCurrentPermissionsRequest = false
D/LeakCanary: |   mIdent = 115814648
D/LeakCanary: |   mInstanceTracker = android.os.StrictMode$InstanceTracker@584499696 (0x22d6c1f0)
D/LeakCanary: |   mInstrumentation = android.app.Instrumentation@584284208 (0x22d37830)
D/LeakCanary: |   mIntent = android.content.Intent@584326912 (0x22d41f00)
D/LeakCanary: |   mLastNonConfigurationInstances = null
D/LeakCanary: |   mMainThread = android.app.ActivityThread@583020800 (0x22c03100)
D/LeakCanary: |   mManagedCursors = java.util.ArrayList@584548480 (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 = java.lang.String@584548512 (0x22d780a0)
D/LeakCanary: |   mTitleColor = 0
D/LeakCanary: |   mTitleReady = true
D/LeakCanary: |   mToken = android.os.BinderProxy@584548544 (0x22d780c0)
D/LeakCanary: |   mTranslucentCallback = null
D/LeakCanary: |   mUiThread = java.lang.Thread@1933361752 (0x733cc258)
D/LeakCanary: |   mVisibleBehind = false
D/LeakCanary: |   mVisibleFromClient = true
D/LeakCanary: |   mVisibleFromServer = true
D/LeakCanary: |   mVoiceInteractor = null
D/LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@583319712 (0x22c4c0a0)
D/LeakCanary: |   mWindowAdded = true
D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@584548576 (0x22d780e0)
D/LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@584483648 (0x22d68340)
D/LeakCanary: |   mOverrideConfiguration = null
D/LeakCanary: |   mResources = android.content.res.Resources@584284288 (0x22d37880)
D/LeakCanary: |   mTheme = android.content.res.Resources$Theme@584548608 (0x22d78100)
D/LeakCanary: |   mThemeResource = 2131230877
D/LeakCanary: |   mBase = android.app.ContextImpl@584475520 (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)

1 个答案:

答案 0 :(得分:1)

InterruptedException for循环仍然循环后,当您发布进度时,您的线程将被取消,因此您需要检查您的线程是否被取消。

当您的应用程序崩溃时,您仍然有WeakReference TextView这就是内存泄漏的原因。您需要清除WeakReferenceonPostExecute中的onCancelled

改变你的:

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;
}