Android Handler Memory Leak
2016-04-01 22:27
661 查看
转载自https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks
Android Handler Memory Leaks
ByDmytro Voronkevych on 28 Aug 2014 -
android
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 with
OutOfMemory.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
Runnablehas beenposted to the
Handlerwith 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$0is referenced by
callback, which is then referenced bya chain of
next’s of
Messageback 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
Runnableor
Messageinto
Handler, it’s then stored in listof
Messagecommands referenced from
LooperThreaduntilthe 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 ofthis$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
mContextinside
mTextViewof our
DoneRunnableclass.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
WeakReferenceto all objects manipulated from
Handler/
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 in
textViewwill bechanged to ‘Done’. No need to browse the code to realise that.
Writing this much boilerplate code is very tedious, especially if
postDelayedis 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 accept
nullas argument. It will remove all
Runnablesand
Messagesposted to a particular handler. Let’s use it in
onDestroy.
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 on
activity/
fragmentdestroy.
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 introducingWeakHandler-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 using
android.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 ofWeakHandleris to keep
Runnables/
Messageshard-referenced while
WeakHandleris 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
Handlerand
WeakHandlerto post anonymous runnables:
Looking at the top diagram,
Activitykeeps a reference to
Handler,which posts
Runnable(puts it into queue of Messages referenced from Thread).Everything is fine except the indirect reference from
Runnableto
Activity.While
Messageis in the queue, all graphs can’t be garbage-collected.
By comparison, in the bottom diagram
Activityholds
WeakHandler, which keeps
Handlerinside. When we ask it to post
Runnable, it is wrapped into
WeakRunnableand posted. So the
Messagequeue keeps reference only to
WeakRunnable.
WeakRunnablekeeps weak reference to the desired
Runnable,so the
Runnablecan be garbage-collected.
Another little trick is that
WeakHandlerstill keeps a hard reference to thedesired
Runnable, to prevent it from being garbage-collectedwhile
WeakRunnableis 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
UsingpostDelayedin Android requires additional effort.To achieve it we came up with three different methods:
Use a static inner
Runnable/
Handlerwith
WeakReferenceto owner class
Clear all messages from
Handlerin
onDestroyof
Activity/
Fragment
Use
WeakHandlerfrom 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 -
WeakHandlershould not be used
without hard reference from outside.
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories