Issue
I am trying to work with AsyncTask
using inner class. But I face a problem with Leak Memory. So I decided to write some test code in order to figure out where issue could be. In this code below i have tried to run a task that counts from 0 to 100. Than I left the activity while the task was running. I got an _InterruptedException_
and Activity
leaked(using Leak Canary), then my app was freezed until it crashed. I could not understand why, because the task was canceled, bevor I left the activity.
Here is my little sample code:
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();
}
}
}
Here is my log:
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)
Solution
After InterruptedException
your for
loop is still looping, when you are publishing your progress your thread is cancelled so you need to check whether your thread is cancelled or not.
When your application crashes you still have a WeakReference
of your TextView
that's why you have a memory leak. You need to clear your WeakReference
in onPostExecute
and onCancelled
.
Change your:
doInBackground
to
@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
to
@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
to
@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;
}
And add onCancelled
@Override
protected void onCancelled() {
// safe check
super.onCancelled();
if(_textView != null)
_textView.clear();
_textView = null;
}
Answered By - Haris Qurashi
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.