说明:大部分内容都是参考别的文章,这里做整理是为了以后的编程有实用的模板,可以即需即用。
最简单的 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、《里面有一些表述是之前摘录别的文章的,来源已不明,请见谅》