Android 常用代码整理:Handler 的生产环境用法

说明:大部分内容都是参考别的文章,这里做整理是为了以后的编程有实用的模板,可以即需即用。

最简单的 Handler 写法:

private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    break;
                default:
                    break;
            }
        }
    };

提醒说 “This Handler class should be static or leaks might occur”(这个处理程序类应该是静态的,或者可能发生泄漏):

就算按照 IDE 的提示加上 “@SuppressLint(“HandlerLeak”)”,但实际上并没有解决问题:

@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                break;
            default:
                break;
        }
    }
};

Handler 造成的内存泄漏: Handler、Message 和 MessageQueue 都是相互关联在一起的,如果 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有;由于 Handler 属于 TLS(Thread Local Storage) 变量,生命周期和 Activity 是不一致的,很容易导致内存泄漏。

  • 线程局部存储(TLS):用于将某些数据和一特定线程关联起来,即,这些数据为关联线程所独有(私有)。
  • TLS 是一个机制,经由它,程序可以拥有全域变量,但处于「每一线程各不相同」的状态。也就是说,进程中的所有线程都可以拥有全域变量,但这些变量其实是特定对某个线程才有意义。例如,你可能有一个多线程程序,每一个线程都对不同的文件写文件(也因此它们使用不同的文件handle)。这种情况下,把每一个线程所使用的文件 handle 储存在 TLS 中,将会十分方便。当线程需要知道所使用的 handle,它可以从TLS 获得。重点在于:线程用来取得文件 handle 的那一段码在任何情况下都是相同的,而从TLS 中取出的文件handle 却各不相同。非常灵巧,不是吗?有全域变数的便利,却又分属各线程。

解决这种问题的做法:使用 static 的 Handler内部类,同时在实现内部类中持有 Context 的弱引用。

为什么要使用静态内部类:非静态内部类默认持有外部类的引用,如果该非静态内部类创建了一个静态的实例,因为该实例的生命周期和应用的生命周期一样长,外部类的引用会一直被静态实例持有,最终导致外部类的内存资源不能被正常回收。而避免这种情况的正确做法是,将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例。

避免习惯性地使用 static 变量:由于 static 变量会跟应用程序生命周期一致,当 Activity 退出后台被后台回收时,static 变量是不安全,所以也要管理好 static 变量的生命周期。

解决方案,抽取基类,方便编程【基本版】

import android.os.Handler;
import java.lang.ref.WeakReference;

public class UIHandler<T> extends Handler {

    private WeakReference<T> ref;

    public UIHandler(T cls) {
        // 指示指定对象变得可终结,可被回收
        ref = new WeakReference<T>(cls);
    }

    public T getRef() {
        return ref != null ? ref.get() : null;
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    private final MyHandler handler = new MyHandler(this);

    private static class MyHandler extends UIHandler<MainActivity> {

        public MyHandler(MainActivity cls) {
            super(cls);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 首先要判断是否已被回收
            MainActivity activity = getRef();
            if (activity != null) {
                // 处理 Message
                switch (msg.what) {
                    case 1:
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

本着我懒我最屌的原则,再抽取一下(不知道有没有坑?)【最终版】:

import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;

public abstract class UIHandler<T> extends Handler {

    private WeakReference<T> ref;

    public UIHandler(T cls) {
        // 指示指定对象变得可终结,可被回收
        ref = new WeakReference<T>(cls);
    }

    public T getRef() {
        return ref != null ? ref.get() : null;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 首先要判断是否已被回收
        T ref = getRef();
        if (ref != null) {
            // 处理 Message
            myHandleMessage(msg);
        }
    }

    public abstract void myHandleMessage(Message msg);
}
private final MyHandler handler = new MyHandler(this);

private static class MyHandler extends UIHandler<MainActivity> {

    public MyHandler(MainActivity cls) {
        super(cls);
    }

    @Override
    public void myHandleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                break;
            default:
                break;
        }
    }
}

顺便说一下,handleMessage(Message msg) 在源代码中是空实现的,如果不需要 UIHandler 中 handleMessage(Message msg) 的操作,可以在 UIHandler 的子类中,重写 handleMessage(Message msg),而不用 super.handleMessage(msg):


参考文章:
1、http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/ref/WeakReference.html
2、https://blog.csdn.net/tanlongzhiwen1/article/details/41478801
3、https://blog.csdn.net/alcoholdi/article/details/54948058
4、http://www.xuebuyuan.com/210093.html
5、http://www.xuebuyuan.com/3206319.html
6、http://www.cppblog.com/Tim/archive/2012/07/04/181018.html
7、https://www.cnblogs.com/wxishang1991/p/4882775.html
8、https://www.imooc.com/article/25134?block_id=tuijian_wz
9、https://blog.csdn.net/u013356254/article/details/52463636
10、《里面有一些表述是之前摘录别的文章的,来源已不明,请见谅》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值