Handler消息机制二——子线程下如何使用Handler
关于Handler,在哪个线程里面new一个Handler,那么这个Handler对象就运行在哪个线程下,比如,我们通常在主线程里面new一个Handler,那么这个Handler对象就运行在主线程中。同理的如果我们在子线程里面new一个Handler,那么Handler对象就运行在子线程中。不同的是,我们不能直接在子线程里面new一个Handler,因为:
在应用App启动的时候,会在执行程序的入口ActivityThread.class类中主函数public static void main(String[] args)里面创建一个Looper对象:Looper.prepareMainLooper(),然后Looper.loop();完成Looper对象的创建。实际上Looper.prepareMainLooper()方法还是调用了Looper的prepare()方法完成Looper对象的创建。因此在主线程中通过关键字new创建的Handler对象之前,Looper对象已经存在并始终存在。
在子线程中,直接new Handler:
new Thread(new Runnable() {
@Override
public void run() {
//Looper.prepare();//Looper初始化
//Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
mHandler = new Handler(Looper.myLooper());
//Looper.loop();//死循环
//注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
}
}).start();
则运行时会报错,因为在子线程中通过关键字new创建的Handler对象时,子线程里面的Looper对象是没有被创建的,不存在的,直接使用的话,会报异常:
java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference
at android.os.Handler.<init>(Handler.java:233)
at android.os.Handler.<init>(Handler.java:137)
at com.haoyue.demolist.base.MainActivity$2.run(MainActivity.java:52)
at java.lang.Thread.run(Thread.java:833)
大致意思是说,Looper.mQueue对象为空,而第52行:
mHandler = new Handler(Looper.myLooper());
正是我们在创建Handler对象的地方。
如果要在子线程中创建Handler对象,那么就需要在创建前添加代码:Looper.prepare();来解决问题:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//Looper初始化
//Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
mHandler = new Handler(Looper.myLooper());
Looper.loop();//死循环
//注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
}
}).start();
完整的测试代码:
public class MainActivity extends BaseActivity {
Handler mHandler;
int count = 5;
Handler mainHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//Looper初始化
//Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
mHandler = new Handler(Looper.myLooper());
Looper.loop();//死循环
//注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
}
}).start();
childThread();
}
private void childThread() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//Looper初始化
//Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
mHandler = new Handler(Looper.myLooper());
Looper.loop();//死循环
//注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
}
}).start();
//主线程 注意此处在主线程延时一秒才给子线程发消息是因为子线程创建需要时间, 立刻发消息Handler还么没准备好, 会抛出异常.
Log.i(TAG, "count: ---> " + " ThreadName: " + Thread.currentThread().getName());
//MainThread是我自定义的一个类, 内部缓存了一主线程Handler
mainHandler.postDelayed(new Runnable() {
@Override
public void run() {
//使用子线程中的mHandler给子线程发一个延时消息
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (count >= 0) {
//打印当前线程名
Log.i(TAG, "count: ---> " + count + " ThreadName: " + Thread.currentThread().getName());
count--;
//继续给子线程发消息
mHandler.postDelayed(this, 1000);
} else if (count >= -10) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mHandler.getLooper().quitSafely();
} else {
mHandler.getLooper().quit();
}
Log.i(TAG, "quit: count: ---> " + count + " ThreadName: " + Thread.currentThread().getName());
count--;
//继续给子线程发消息
mHandler.postDelayed(this, 1000);
}
}
}, 1000);
}
}, 1000);
}
}
同理的,要在子线程中使用Toast,也需要这样处理:
newThread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(MainActivity.this, "子线程显示", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();
否则,会报异常:
Can't create handler inside thread that has not called Looper.prepare()
注:在SDK27版本下:
newThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "子线程显示", Toast.LENGTH_SHORT).show();
}
}).start();
上述Toast没有反应,但是不抛异常。不知道是否新版的SDK做了更改,后续再阅读源码分析这个问题。
上一篇: 线段树orz!
推荐阅读
-
Android多线程(二)消息处理机制---Handler、Message、Looper源码原理解析
-
Handler消息机制二——子线程下如何使用Handler
-
Android多线程----异步消息处理机制之Handler使用详解
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
深入理解Handler(二) --- 从源码角度来理解Android线程间消息传递机制
-
【黑马Android】(05)短信/查询和添加/内容观察者使用/子线程网络图片查看器和Handler消息处理器/html查看器/使用HttpURLConnection采用Post方式请求数据/开源项目_html/css_WEB-ITnose
-
【黑马Android】(05)短信/查询和添加/内容观察者使用/子线程网络图片查看器和Handler消息处理器/html查看器/使用HttpURLConnection采用Post方式请求数据/开源项目_html/css_WEB-ITnose
-
Android:Handler消息机制(二)——如何使用Handler消息机制