handler
handler大家应该都很熟悉了,毕竟只要是涉及到Android里面线程间的通讯,总会看到它的身影。我平时也经常使用Handler,今天整理笔记,就将之前记录下来的Handler的一些使用姿势发到这里来。
1.在子线程中创建Handler对象
这个可能比较少见,一般都是在主线程创建Handler对象。不过少见不代表我们以后不会遇到。
按照我们平时在主线程创建Handler对象的习惯,代码应该是下面这个样子:
private class otherThread extends Thread {
@Override
public void run() {
super.run();
Handler otherHandler = new Handler();
}
}
不过这样子只要一运行程序就可以看到下面的报错:
按照提示我们在子线程里面创建Handler对象之前调用looper.prepare();方法。这里或许有人会提出疑问,明明我们在主线程都不需要这样子做啊。很简单,因为Android的系统已经提前做了处理了。到activitythread中的main方法(APK的java静态入门)看一眼:
再点Looper.prepareMainLooper();进去看一眼:
这里的注释说明的很清楚了,这个是给当前线程初始化一个Looper。所以不是主线程创建Handler不需要调用Looper.prepare();方法,而是人家已经提前帮我们做了。所以在子线程创建Handler对象的正确姿势应该是下面这个样子:
private class otherThread extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
Handler otherHandler = new Handler();
Looper.loop();
}
}
2.Handler导致的内存泄露
这个估计会比较常见,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用只要一个不小心即有可能造成内存泄漏,如下示例:
public class TestActivity extends APPCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handlemessage(Message msg) {
//这里处理逻辑
}
};
@Override
protected void onCreate(Bundle savedinstanceState) {
super.onCreate(savedInstanceState);
setcontentView(R.layout.activity_test);
loadData();
}
private void loadData() {
//子线程访问网络获取数据中
................
Message message = Message.obtain();
mHandler.sendMessageAtTime(message, 5 * 1000);
..........
}
由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类TestActivity 的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,从而引发内存泄漏。所以我们应该这么写:
public class TestActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
loadData();
}
private void loadData() {
//子线程访问网络获取数据中
................
Message message = Message.obtain();
mHandler.sendMessageAtTime(message, 5 * 1000);
................
}
private static class MyHandler extends Handler {
//对context使用弱引用
private weakreference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
TestActivity activity = (TestActivity) reference.get();
//避免TestActivity已经被回收了,是一个null值
if (activity != null) {
//这里处理逻辑
}
}
}
}
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在GC可以顺利回收Handler持有的对象,这样就是避免了Activity的泄漏。但是,每次在Activity里面使用Handler都这么写会不会很累啊。其实还有一个更加简单方法,如下所示:
public class TestActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有Message.清空消息队列也就意味着这个Handler的对象被打回原形,GC可以很顺利一棍子将其打死。
所以,我们平时在Activity或Fragment使用Handler避免潜在的内存泄露的正确姿势就是onDestroy方法或onStop方法中调用一下Handler的removeCallbacksAndMessages(null);就可以了。
3.Handler的post方法也是经常用到的
有时候我们是会直接在子线程中调用主线程的Handler对象的post来修改UI。我们来捉下打印来看下它为什么可以修改UI吧:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "TestActivity";
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Log.e(TAG,"MainThread id:"+Thread.currentThread().getId());
setThreadAction();
}
private void setThreadAction() {
new otherThread().start();
}
private class otherThread extends Thread {
@Override
public void run() {
super.run();
Log.e(TAG,"otherThread id:"+Thread.currentThread().getId());
mHandler.post(new Runnable() {
@Override
public void run() {
Log.e(TAG,"the id:"+Thread.currentThread().getId());
}
});
}
}
}
看下打印的结果:
这就是说明了这里的Runnable的run()方法其实运行在主线程了,这也是我们为什么可以用它来修改UI的原因。或许有人会疑问Runnable明明是在子线程中创建的,怎么就变成了主线程呢?我们去瞄一下源码其实是可以发现Message里面有个Runnable参数对象,Handler的post方法最终就是给Message的这个Runnable参数赋值。
因此,mHandler.post(Runnable runnable)和mHandler.sendMessage(Message message)这两个方法在本质上就是Handler发送了Message。那么主线程的Handler发送的Message自然就在主线程中处理。
既然是主线程,那么当我们使用这个Handler的post方法时就不能执行任何耗时操作了,否则就会就会出现ANR异常。
类似的传递Runnable方法还有两个,View里面的post(Runnable runnable);和Activity的runOnUiThread(Runnable runnable),也去瞄一下他们源码好了。
View的post(Runnable runnable):
看到Handler的post方法就不用多说了。
Activity的runOnUiThread(Runnable runnable):
看样子又是不用多说什么了。
所以我们平时在使用Handler的post方法(或是方法内部封装该post方法)时的正确姿势就是:不能在里面执行任何耗时操作!
后记
到此这篇文章就结束了,Handler在日常Android开发中有着十分重要的地位,懂得如何正确使用是一件十分必要的事情。最后,纪念下以前做笔记时的渣渣绘图:
相关阅读
Handler还需要用到弱引用(WeakReference)吗?
转自:https://blog.csdn.net/alcoholdi/article/details/54948058 写的很好网上很多文章都说写Hanlder,需要用static声明为静态的
Message.obtain() 和Handler.obtainMessage()的区别
Message.obtain() 和Handler.obtainMessage()的区别 性能更优越 尽管Message的构造器是公开的,但是获取Message对象的最好
Android HandlerThread使用介绍以及源码解析
1. 前言 首先,看一下官方对HandlerThread的解释: Handy class for starting a new thread that has a looper. The looper can th
Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送Message或Runnable对象进行通信。在Android中UI修改
最近遇到一个委托的问题,+=这个符号,哇,真的是头皮发麻,以为是运算符,根本不知道是委托 -。-!! 看下图: 后面查了以后原来这种用法真的很