handler 可能引发泄露

来源:互联网 发布:mac pro使用教程 编辑:程序博客网 时间:2024/06/06 19:12

问题描述

曾几何时,我们用原来的办法使用Handler时会有下面一段温馨的提示:

<code class="hljs fsharp has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">This Handler <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> should be <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">or</span> leaks might occur</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

下面是更详细的说明(Android Studio上的警告,不知道Eclipse上是否相同)

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

大概意思就是:

一旦Handler被声明为内部类,那么可能导致它的外部类不能够被垃圾回收。如果Handler是在其他线程(我们通常成为worker thread)使用Looper或MessageQueue(消息队列),而不是main线程(UI线程),那么就没有这个问题。如果Handler使用Looper或MessageQueue在主线程(main thread),你需要对Handler的声明做如下修改: 
声明Handler为static类;在外部类中实例化一个外部类的WeakReference(弱引用)并且在Handler初始化时传入这个对象给你的Handler;将所有引用的外部类成员使用WeakReference对象。

解决方案一

上面的描述中基本上把推荐的修改方法明确表达了出来,下面的代码是我自己使用中的一个实现,请参考:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> CopyFileHandler mHandler;<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);    setContentView(R.layout.activity_appstart);    mHandler = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> CopyFileHandler(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);    startCopyDBThread();}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">startCopyFileThread</span>(){    Log.d(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"startCopyDBThread"</span>);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() {        <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//DO SOMETHING LIKE: copyDBFile();</span>            Message msg=mHandler.obtainMessage();            mHandler.sendMessage(msg);        }    }).start();}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">CopyFileHandler</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Handler</span> {</span>    WeakReference<AppStartActivity> mActivity;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">CopyFileHandler</span>(AppStartActivity activity) {        mActivity = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> WeakReference<>(activity);    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">handleMessage</span>(Message msg) {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> AppStartActivity activity = mActivity.get();       <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//handle you message here!</span>    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li></ul>

为什么会内存泄漏

那么为什么不这样做会引发内存泄漏呢? 
这与几个关键词有关:内部类、Handler的消息循环(Looper)、Java垃圾回收机制。 
需要强调一下,并不是每次使用Handler都会引发内存泄漏,这里面有一定的几率,需要满足特定条件才会引起泄漏。 
内部类会有一个指向外部类的引用。 
垃圾回收机制中约定,当内存中的一个对象的引用计数为0时,将会被回收。 
Handler作为Android上的异步消息处理机制(好吧,我大多用来进行worker thread与UI线程同步),它的工作是需要Looper和MessageQueue配合的。简单的说,要维护一个循环体(Looper)处理消息队列(MessageQueue)。每循环一次就从MessageQueue中取出一个Message,然后回调相应的消息处理函数。

如果,我是说如果,循环体中有消息未处理(Message排队中),那么Handler会一直存在,那么Handler的外部类(通常是Activity)的引用计数一直不会是0,所以那个外部类就不能被垃圾回收。很多人会遇到activity的onDestroy方法一直不执行就是这个原因。

另一个解决方案的尝试

警告描述中提到了Handler在worker thread中使用Looper或MessageQueue,我尝试了一下,请大家品鉴。

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Handler testHandler;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Thread</span> mThread <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Thread</span>() {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">void</span> run() {            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>d(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">TAG</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"mThread run"</span>);            Looper<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>prepare();            testHandler <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> Handler() {                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">void</span> handleMessage(Message msg) {                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"TAG"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"worker thread:"</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Thread</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>currentThread()<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getName());                    switch (msg<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>what) {                        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//handle message here</span>                    }                }            };            Looper<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">loop</span>();        }    };    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//start thread here</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Thread</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>State<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">NEW</span> <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">==</span> mThread<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getState()) {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>d(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">TAG</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"mThread name: "</span> <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span> mThread<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getName());        mThread<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>start();    }    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//send message here</span>    testHandler<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>sendEmptyMessage(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>

参考: 

0 0
原创粉丝点击