Android App 內存泄露之Handler
Handler也是造成內存泄露的一個重要的源頭,主要Handler屬於TLS(Thread Local Storage)變量,生命週期和Activity是不一致的
,Handler引用Activity會存在內存泄露。
看一下如下代碼
- /**
- *
- * 實現的主要功能。
- * @version 1.0.0
- * @author Abay Zhuang <br/>
- * Create at 2014-7-28
- */
- public class HandlerActivity extends Activity {
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // ...
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mHandler.sendMessageDelayed(Message.obtain(), 60000);
- //just finish this activity
- finish();
- }
- }
是否您以前也是這樣用的呢。
沒有問題?
This Handler class should be static or leaks might occur (com.example.ta.HandlerActivity.1)
意思:class 使用靜態聲明否者可能出現內存泄露。
爲啥出現這樣的問題呢
Handler 的生命週期與Activity 不一致
- 當Android應用啓動的時候,會先創建一個UI主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理裏面的Message對象。主線程Looper對象在整個應用生命週期中存在。
- 當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯(沒有關聯會報錯的)。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。
handler 引用 Activity 阻止了GC對Acivity的回收
- 在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。
-
如果外部類是Activity,則會引起Activity泄露 。
當Activity finish後,延時消息會繼續存在主線程消息隊列中1分鐘,然後處理消息。而該消息引用了Activity的Handler對象,然後這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。
如何避免修?
- 使用顯形的引用,1.靜態內部類。 2. 外部類
- 使用弱引用 2. WeakReference
修改代碼如下:
- /**
- *
- * 實現的主要功能。
- *
- * @version 1.0.0
- * @author Abay Zhuang <br/>
- * Create at 2014-7-28
- */
- public class HandlerActivity2 extends Activity {
- private static final int MESSAGE_1 = 1;
- private static final int MESSAGE_2 = 2;
- private static final int MESSAGE_3 = 3;
- private final Handler mHandler = new MyHandler(this);
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mHandler.sendMessageDelayed(Message.obtain(), 60000);
- // just finish this activity
- finish();
- }
- public void todo() {
- };
- private static class MyHandler extends Handler {
- private final WeakReference<HandlerActivity2> mActivity;
- public MyHandler(HandlerActivity2 activity) {
- mActivity = new WeakReference<HandlerActivity2>(activity);
- }
- @Override
- public void handleMessage(Message msg) {
- System.out.println(msg);
- if (mActivity.get() == null) {
- return;
- }
- mActivity.get().todo();
- }
- }
上面這樣就可以了嗎?
當Activity finish後 handler對象還是在Message中排隊。 還是會處理消息,這些處理有必要?
正常Activitiy finish後,已經沒有必要對消息處理,那需要怎麼做呢?
解決方案也很簡單,在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和Runnable。
通過查看Handler的API,它有幾個方法:removeCallbacks(Runnable r)和removeMessages(int what)等。
代碼如下:
- /**
- * 一切都是爲了不要讓mHandler拖泥帶水
- */
- @Override
- public void onDestroy() {
- mHandler.removeMessages(MESSAGE_1);
- mHandler.removeMessages(MESSAGE_2);
- mHandler.removeMessages(MESSAGE_3);
- // ... ...
- mHandler.removeCallbacks(mRunnable);
- // ... ...
- }
如果上面覺的麻煩,也可以如下面:
- @Override
- public void onDestroy() {
- // If null, all callbacks and messages will be removed.
- mHandler.removeCallbacksAndMessages(null);
- }
Android App 內存泄露之Handler
Handler也是造成內存泄露的一個重要的源頭,主要Handler屬於TLS(Thread Local Storage)變量,生命週期和Activity是不一致的
,Handler引用Activity會存在內存泄露。
看一下如下代碼
- /**
- *
- * 實現的主要功能。
- * @version 1.0.0
- * @author Abay Zhuang <br/>
- * Create at 2014-7-28
- */
- public class HandlerActivity extends Activity {
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // ...
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mHandler.sendMessageDelayed(Message.obtain(), 60000);
- //just finish this activity
- finish();
- }
- }
是否您以前也是這樣用的呢。
沒有問題?
This Handler class should be static or leaks might occur (com.example.ta.HandlerActivity.1)
意思:class 使用靜態聲明否者可能出現內存泄露。
爲啥出現這樣的問題呢
Handler 的生命週期與Activity 不一致
- 當Android應用啓動的時候,會先創建一個UI主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理裏面的Message對象。主線程Looper對象在整個應用生命週期中存在。
- 當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯(沒有關聯會報錯的)。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。
handler 引用 Activity 阻止了GC對Acivity的回收
- 在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。
-
如果外部類是Activity,則會引起Activity泄露 。
當Activity finish後,延時消息會繼續存在主線程消息隊列中1分鐘,然後處理消息。而該消息引用了Activity的Handler對象,然後這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。
如何避免修?
- 使用顯形的引用,1.靜態內部類。 2. 外部類
- 使用弱引用 2. WeakReference
修改代碼如下:
- /**
- *
- * 實現的主要功能。
- *
- * @version 1.0.0
- * @author Abay Zhuang <br/>
- * Create at 2014-7-28
- */
- public class HandlerActivity2 extends Activity {
- private static final int MESSAGE_1 = 1;
- private static final int MESSAGE_2 = 2;
- private static final int MESSAGE_3 = 3;
- private final Handler mHandler = new MyHandler(this);
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mHandler.sendMessageDelayed(Message.obtain(), 60000);
- // just finish this activity
- finish();
- }
- public void todo() {
- };
- private static class MyHandler extends Handler {
- private final WeakReference<HandlerActivity2> mActivity;
- public MyHandler(HandlerActivity2 activity) {
- mActivity = new WeakReference<HandlerActivity2>(activity);
- }
- @Override
- public void handleMessage(Message msg) {
- System.out.println(msg);
- if (mActivity.get() == null) {
- return;
- }
- mActivity.get().todo();
- }
- }
上面這樣就可以了嗎?
當Activity finish後 handler對象還是在Message中排隊。 還是會處理消息,這些處理有必要?
正常Activitiy finish後,已經沒有必要對消息處理,那需要怎麼做呢?
解決方案也很簡單,在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和Runnable。
通過查看Handler的API,它有幾個方法:removeCallbacks(Runnable r)和removeMessages(int what)等。
代碼如下:
- /**
- * 一切都是爲了不要讓mHandler拖泥帶水
- */
- @Override
- public void onDestroy() {
- mHandler.removeMessages(MESSAGE_1);
- mHandler.removeMessages(MESSAGE_2);
- mHandler.removeMessages(MESSAGE_3);
- // ... ...
- mHandler.removeCallbacks(mRunnable);
- // ... ...
- }
如果上面覺的麻煩,也可以如下面:
- @Override
- public void onDestroy() {
- // If null, all callbacks and messages will be removed.
- mHandler.removeCallbacksAndMessages(null);
- }