Android Handler Memory Leak
来源:互联网 发布:苹果mac怎么删除照片 编辑:程序博客网 时间:2024/06/05 13:30
转载自https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks
Android Handler Memory Leaks
Android uses Java as a platform for development.This helps us with many low level issues including memory management,platform type dependencies, and so on.However we still sometimes get crashes withOutOfMemory.So where’s the garbage collector?
I’m going to focus on one of the cases where big objects in memory can’t becleared for a lengthy period of time. This case is not ultimately a memory leak -objects will be collected at some point - so we sometimes ignore it.This is not advisable as it can sometimes lead to OOM errors.
The case I’m describing is the Handler leak, which is usually detected as awarning by Lint.
Basic Example
This is a very basic activity. Notice that this anonymous Runnable
has beenposted to the Handler
with a very long delay. We’ll run it and rotate thephone couple of times, then dump memory and analyze it.
We have seven activities in memory now. This is definitely not good.Let’s find out why GC is not able to clear them.
The query I made to get a list of all Activities remaining in memory was createdin OQL (Object Query Language), which is very simple, yet powerful.
As you can see, one of the activities is referenced by this$0
.This is an indirect reference from the anonymous class to the owner class.This$0
is referenced bycallback
, which is then referenced bya chain ofnext
’s of Message
back to the main thread.
Any time you create a non-static class inside the owner class,Java creates an indirect reference to the owner
Once you post Runnable
or Message
into Handler
, it’s then stored in listofMessage
commands referenced from LooperThread
untilthe message is executed. Posting delayed messages is a clear leak for at leastthe time of the delay value. Posting without delay may cause a temporary leakas well if the queue of messages is large.
Static Runnable Solution
Let’s try to overcome a memory leak by getting rid of this$0
, by convertingthe anonymous class to static.
Run, rotate and get the memory dump.
What, again? Let’s see who keeps referring to Activities
.
Take a look at the bottom of the tree - activity is kept as a referenceto mContext
inside mTextView
of our DoneRunnable
class.Using static inner classes is not enough to overcome memory leaks, however.We need to do more.
Static Runnable With WeakReference
Let’s continue using iterative fixes and get rid of the reference to TextView,which keeps activity from being destroyed.
Note that we are keeping WeakReference to TextView, and let’s run, rotate anddump memory.
Be careful with WeakReferences. They can be null at any moment,so resolve them first to a local variable (hard reference) and then checkto null before use.
Hooray! Only one activity instance. This solves our memory problem.
So for this approach we should:
- Use static inner classes (or outer classes)
- Use
WeakReference
to all objects manipulated fromHandler
/Runnable
If you compare this code to the initial code, you might find a big difference inreadability and code clearance. The initial code is much shorter and muchclearer, and you’ll see that eventually, text intextView
will bechanged to ‘Done’. No need to browse the code to realise that.
Writing this much boilerplate code is very tedious, especially if postDelayed
is set to a short time, such as 50ms. There are better and clearer solutions.
Cleanup All Messages onDestroy
Handler class has an interesting feature - removeCallbacksAndMessages
-which can acceptnull
as argument. It will remove all Runnables
andMessages
posted to a particular handler. Let’s use it inonDestroy
.
Let’s run, rotate and dump memory.
Good! Only one instance.
This approach is way better than the previous one, as it keeps code clear andreadable. The only overhead is to remember to clear all messages onactivity
/fragment
destroy.
I have one more solution which, if you’re lazy like me, you might like even more. :)
Use WeakHandler
The Badoo team came up with the interesting idea of introducing WeakHandler
-a class that behaves as Handler
, but is way safer.
It takes advantage of hard and weak references to get rid of memory leaks.I will describe the idea in detail a bit later, but let’s look at the code first:
Very similar to the original code apart from one small difference -instead of usingandroid.os.Handler
, I’ve used WeakHandler
.Let’s run, rotate and dump memory:
Nice, isn’t it? The code is cleaner than ever, and memory is clean as well! :)
To use it, just add dependency to your build.gradle:
repositories { maven { repositories { url 'https://oss.sonatype.org/content/repositories/releases/' } }}dependencies { compile 'com.badoo.mobile:android-weak-handler:1.0'}
And import it in your java class:
import com.badoo.mobile.util.WeakHandler;
Visit Badoo’s github page, where you can fork it, or study it’ssource code https://github.com/badoo/android-weak-handler
WeakHandler. How it works
The main aim of WeakHandler
is to keep Runnables
/Messages
hard-referenced whileWeakHandler
is also hard-referenced. Once it can beGC-ed, all messages should go away as well.
Here is a simple diagram that demonstrates differencesbetween using normal Handler
and WeakHandler
to post anonymous runnables:
Looking at the top diagram, Activity
keeps a reference toHandler
,which posts Runnable
(puts it into queue of Messages referenced from Thread).Everything is fine except the indirect reference fromRunnable
to Activity
.While Message
is in the queue, all graphs can’t be garbage-collected.
By comparison, in the bottom diagram Activity
holdsWeakHandler
, which keepsHandler
inside. When we ask it to postRunnable
, it is wrapped intoWeakRunnable
and posted. So theMessage
queue keeps reference only toWeakRunnable
.WeakRunnable
keeps weak reference to the desiredRunnable
,so the Runnable
can be garbage-collected.
Another little trick is that WeakHandler
still keeps a hard reference to thedesiredRunnable
, to prevent it from being garbage-collectedwhileWeakRunnable
is active.
The side-effect of using WeakHandler is that all messages and runnablesmay not be executed if WeakHandler has been garbage-collected.To prevent that, just keep a reference to it from Activity.Once Activity is ready to be collected,all graphs with WeakHandler will collected as well.
Conclusions
Using postDelayed
in Android requires additional effort.To achieve it we came up with three different methods:
- Use a static inner
Runnable
/Handler
withWeakReference
to owner class - Clear all messages from
Handler
inonDestroy
ofActivity
/Fragment
- Use
WeakHandler
from Badoo as a silver bullet
It’s up to you to choose your preferred technique.The second seems very reasonable, but needs some extra work.The third is my favourite, obviously, but it require some attention as well -WeakHandler
should not be used without hard reference from outside.
- Android Handler Memory Leak
- Android memory leak detection
- Android memory leak analysis
- Android memory leak
- Android memory leak detect
- Android Handler leak
- Android memory leak using MAT
- android webview memory leak 线索
- android memory leak analysis tools
- Android Memory/Resource Leak总结
- Android Memory/Resource Leak总结
- memory leak checking for android
- Android native memory leak debug
- android memory leak ==java memory leak
- Memory Leak
- memory leak
- Memory leak
- HOWTO: Debug Memory Leak in Android
- hdu 4027 Can you answer these queries?
- 1005 banknotes
- 一道String面试题
- Android MediaPlayer 音乐播放
- 2010年哈尔滨工业大学计算机研究生机试真题
- Android Handler Memory Leak
- hdu 4052 线段树扫描线、奇特处理
- Clone使用方法详解
- Android Studio安装过程以及导入eclipse工程遇到的问题
- 【JQuery】addClass()和css()
- Why can't MEX find a supported compiler in MATLAB R2015b after I upgraded to Xcode 7.0?
- leetcode_108 Convert Sorted Array to Binary Search Tree
- javaEE
- FZU 2110Star(计算几何)