我正在尝试使用内部类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)
答案 0 :(得分:1)
InterruptedException
for
循环仍然循环后,当您发布进度时,您的线程将被取消,因此您需要检查您的线程是否被取消。
当您的应用程序崩溃时,您仍然有WeakReference
TextView
这就是内存泄漏的原因。您需要清除WeakReference
和onPostExecute
中的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;
}