场景: 在子线程完成某些任务的时候,需要更新UI 提示用户。
这里用Toast 提示作为例子
方法一:
在子线程中使用 Looper.prepare();
Looper.prepare();
Toast.makeText(activity,"some message",Toast.LENGTH_SHORT).show();
Looper.loop();
如果不这么做会报以下异常
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原理分析:
查看Toast的show方法 ,看到这一段
try{service.enqueueToast(pkg,tn,mDuration,displayId);
}catch(RemoteExceptione) {
// Empty
}
大概说明一下就是 Toast的show()方法在应用端使用INotificationManager给实现端发送消息。
那问题就出迎刃而解了,如果在非主线程使用Looper 是要先 Looper.prepare()的,Handler 消息机制又是依赖Looper去消费的。所以这种解法的原理就相当于在非主线程(UI线程) Looper.prepare()。
看到 Looper.prepare() 源码
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */
public static void prepare() {
prepare(true);
}
很清楚的看到 是初始化当前线程作为一个looper
再点进去看
private static void prepare(booleanquitAllowed) {
if(sThreadLocal.get()!=null) {
thrownewRuntimeException("Only one Looper may be created per thread");break; case FLAG2: Toast.makeText(activity, "some meeaagse", Toast.LENGTH_SHORT).show(); break; default: break; } } }
}
sThreadLocal.set(newLooper(quitAllowed));
}
可以看到往ThreadLoal中set了一个Looper对象,就能解释为什么是初始化当前线程了。
方法二:
使用Handler 进行线程间通讯,这也是最常用的做法。
static class MHandlder extends Handler {
private WeakReference<MainActivity> ma;
public MHandlder(MainActivity mainActivity) {
ma = new WeakReference<>(mainActivity);
}
@Override public void handleMessage(@NonNull Message msg) {
final MainActivity activity = ma.get();
if (activity == null) { return; }
switch (msg.what) {
case FLAG1:Toast.makeText(activity, "some meeaagse", Toast.LENGTH_SHORT).show(); break;
case FLAG2: Toast.makeText(activity, "some meeaagse", Toast.LENGTH_SHORT).show(); break;
default: break;
}
}
}
声明一个 MHandler类,继承Handler。这里提一下为何要使用静态类,和对外部类MainActivity使用弱引用。
为何使用static类
如果Handler不设置为static的,那么内部类被引用的时候外部类也必须被实例化,这不是我们想要的,有可能这个Activity已经可以交给GC回收了,但Handler如果还有消息没有消费的话,就会一直持有着这个Activity,导致这个Activity不能被GC。
为何使用弱引用
先解释一下什么是弱引用,弱引用就是引用的这个对象会在下一次GC进行之前被回收,比这个生命周期长一点的是软引用,会在系统OMM之前被GC。
上面说到,Handler不使用静态类会阻碍Activity 的GC,那如果不使用弱引用,使用强引用,那Handler一样会持有Activity。
然后在子线程中使用Handler通知主线程进行UI更新操作
mHandler=newDownloadHandlder(YOURACTIVITY.this);
Messagemsg=mHandler.obtainMessage();
msg.what=FLAG;
mHandler.sendMessage(msg);
顺便一提实例化Handler的时候没有指定某个线程的looper
publicHandler() {this(null,false);}
public Handler (@NullableCallbackcallback,booleanasync) {
.......
// myLooper 会从ThreadLocal中获取looper对象,也就是当前线程的looper对象
mLooper=Looper.myLooper();
......
}
如果是在主线程中实例化Handler 不指定其他线程的looper 那么就会使用主线程的looper,也就是向Handler消息的消费者是主线程。