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然后看内存中对象是否被回收了。

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