android handler leaks (內存泄漏)

android handler leaks

內存泄漏概念

內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由於某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重後果。

問題描述

當我們在activity中做一些異步任務或者延遲任務時很有可能導致內存泄漏,因爲如果這些任務沒有完成之前我們關閉了activity,按道理無論對用戶還是對系統來說正常情況下既然關閉了某個activity活動即不在需要它了那麼它也就不必再佔用內存空間了。但是當我們使用handler處理任務結果時,那麼那些任務必然持有對handler的引用,而handler是activity的成員變量,那麼任務未完成之前系統就沒法回收被關閉acitivity所所佔用的內存空間,這就導致了內存泄漏。
簡單來說,就是我們要處理當activity被關閉後能被回收而不佔內存空間。

In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well.To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

上面一段說明的最後一句揭示瞭解決方案,爲了防止外部類內存泄漏,就要聲明一個對外部類使用弱引用的靜態內部類;這裏外部類指activity。

自定義一個繼承handler的內部類

在activity中自定義一個靜態內部類(非靜態的會持有外部類的引用),對外部類activity使用弱引用持有。這樣就可以對activity回收了。

//demo
static class MyHandler extends Handler{

        WeakReference<Activity> weakReference;

        public MyHandler(Activity activity){
            weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    if(weakReference != null){
                        Activity activity = weakReference.get();
                        if(activity != null){
                            WebBrowserActivity webBrowserActivity = (WebBrowserActivity) activity;
                            webBrowserActivity.textView.setText("ffff");
                        }
                    }
                    break;
            }
        }
    }

    Handler handler = new MyHandler(this);

測試

可以寫MainActivity與SecondActivity兩個activity

MainActivity.java


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.textview).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "start", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(MainActivity.this, WebBrowserActivity.class));
            }
        });
    }
}

SecondActivity.java


public class WebBrowserActivity extends AppCompatActivity {

    static class MyHandler extends Handler{

        WeakReference<Activity> weakReference;

        public MyHandler(Activity activity){
            weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    if(weakReference != null){
                        Activity activity = weakReference.get();
                        if(activity != null){
                            WebBrowserActivity webBrowserActivity = (WebBrowserActivity) activity;
                            webBrowserActivity.textView.setText("ffff");
                        }
                    }
                    break;
            }
        }
    }

    Handler handler = new MyHandler(this);

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_browser);
        System.out.println(this); // 打印對象地址
        textView = findViewById(R.id.textview);

        handler.sendEmptyMessageDelayed(0, 60000);

        finish();
    }
}

MainActivity只是啓動SecondActivity,SecondActivity中用handler發送一條延遲消息後立即finish。

觀察heap中的對象

使用AndroidStudio中的AndroidProfiler工具(不會用可百度之)來查看內存中對象。
手動觸發GC然後看內存中對象是否被回收了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章