Handler 引起的內(nèi)存泄露
先看一組簡單的代碼
- public class SampleActivity extends Activity {
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // ...
- }
- }
- }
當我們這樣寫在一個Activity
中時,Android Lint
會提示我們這樣一個 warning: In Android, Handler classes should be static or leaks might occur.
。
意思說:在Android中,Handler 類應(yīng)該是靜態(tài)的否則可能發(fā)生泄漏。
為什么會是這樣呢?
了解一下Handler
- 當Android程序***次創(chuàng)建的時候,在主線程同時會創(chuàng)建一個
Looper
對象。Looper
實現(xiàn)了一個簡單的消息隊列,一個接著一個處理Message
對象。程序框架所有主要的事件(例如:屏幕上的點擊時間,Activity
生命周期的方法等等)都包含在Message
對象中,然后添加到Looper
的消息隊列中,一個一個處理。主線程的Looper
存在整個應(yīng)用程序的生命周期內(nèi)。 - 當一個
Handler
對象在主線程中創(chuàng)建的時候,它會關(guān)聯(lián)到Looper
的 message queue 。Message
添加到消息隊列中的時候Message
會持有當前Handler
引用,當Looper
處理到當前消息的時候,會調(diào)用Handler#handleMessage(Message)
. - 在
java
中,no-static
的內(nèi)部類會 隱式的 持有當前類的一個引用。static
的類則沒有。
在什么地方引起了內(nèi)存的泄露呢?再看看下面一段代碼
- public class SampleActivity extends Activity {
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // ...
- }
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 發(fā)送一個10分鐘后執(zhí)行的一個消息
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() { }
- }, 600000);
- // 結(jié)束當前的Activity
- finish();
- }
- }
當Activity
結(jié)束后,在 Message queue 處理這個Message
之前,它會持續(xù)存活著。這個Message
持有Handler
的引用,而Handler
有持有Activity
(SampleActivity)的引用,這個Activity
所有的資源,在這個消息處理之前都不能也不會被回收,所以發(fā)生了內(nèi)存泄露。
解決辦法,看下面一段代碼
- public class SampleActivity extends Activity {
- /**
- * 使用靜態(tài)的內(nèi)部類,不會持有當前對象的引用
- */
- private static class MyHandler extends Handler {
- private final WeakReference<SampleActivity> mActivity;
- public MyHandler(SampleActivity activity) {
- mActivity = new WeakReference<SampleActivity>(activity);
- }
- @Override
- public void handleMessage(Message msg) {
- SampleActivity activity = mActivity.get();
- if (activity != null) {
- // ...
- }
- }
- }
- private final MyHandler mHandler = new MyHandler(this);
- /**
- * 使用靜態(tài)的內(nèi)部類,不會持有當前對象的引用
- */
- private static final Runnable sRunnable = new Runnable() {
- @Override
- public void run() { }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 發(fā)送一個10分鐘后執(zhí)行的一個消息
- mHandler.postDelayed(sRunnable, 600000);
- // 結(jié)束
- finish();
- }
- }
ok,結(jié)束
NOTE: 好多人有擔心弱引用的Activity
回收的情況,這個完全不用擔心的,因為我們在這個界面的時候,這個Activity
是不會被回收的,想想 如果我們的這個Activity被回收了,我們的這個界面是怎么存在的呢?
NOTE2: 各位,我是參照AsyncTask
,查看文檔和自己的理解,存在我解釋不了的地方,還望見諒,如有錯誤還望見諒。
NOTE3:具體怎么防止泄漏我也說不清楚,但是Handler
下面有一段源碼
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }