我们已经知道Handler发送消息,处理消息的机制了。ActivityThread在main方法中,为当前线程创建一个looper,一个messagequeue,并且通过调用Looper的loop方法,开启死循环不断地从messagequeue里面去取消息。当handler通过sendmessage的方法把消息添加到消息队列中,主线程的死循环就会拿到消息,进而做一系列的事情。
我们日常创建Handler的方法:
public class HandlerActivityJava extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handler_layout);
new Thread(new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(0);
}
});
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
testLog();
}
};
}
通常是在主线程中调用handler的改造函数,创建一个匿名内部类。那么在子线程中该如何new 一个handler并且让他正常工作呢?
我们先看看在主线程直接new出handler,handler的构造函数:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
发现有两行重要的代码:
//拿到当前线程绑定的Looper
mLooper = Looper.myLooper();
//拿到当前线程的messagequeue
mQueue = mLooper.mQueue;
我们知道这两个东西在app刚刚启动的时候会自动为当前线程绑定一个looper和messagequeue。但是我们现在在子线程里面去new一个handler,这两个东西就需要我们自己去new出来并且与当前线程绑定。
private Handler threadHandler;
public void method2() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
threadHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
Looper.loop();
}
}).start();
}
在这个版本里面,我们在子线程里面自己为当前线程创建了一个Looper,Messagequeue。并且为当前线程启动了一个死循环,不断从消息队列里面去取消息。但是发现一个问题,如果我不想通过匿名内部类的方式创建Handler的话,我该怎么办?
public void method3(){
Thread thread = new Thread(new Runnable() {
Looper looper;
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
public Looper getLooper(){
return looper;
}
});
thread.start();
Handler handler1 = new Handler(thread.getLooper());
}
thread.getLooper方法报错,thread是一个匿名内部类,外部不会持有匿名内部类的引用。
public class MyHandlerThread extends Thread {
private Looper looper;
MyHandlerThread(String name) {
super(name);
}
@Override
public void run() {
super.run();
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
public Looper getLooper() {
return looper;
}
}
直接创建一个MyHandlerThread类,把getLooper暴露出去。使用的时候:
public void method4(){
MyHandlerThread thread = new MyHandlerThread("liaoliao");
thread.start();
Handler handler1 = new Handler(thread.getLooper());
Handler handler2 = new Handler(thread.getLooper());
}
可是这样会有什么问题?
这边有一个并发问题,因为MyHandlerThread是一个子线程,handler1和handler2又是在主线程里面调用的getLooper方法,谁也不能保证在调用getLooper的时候,MyHandlerThread的start方法已经执行完成了,looper已经完成赋值了。
使用HandlerThread:
public void method5(){
HandlerThread thread = new HandlerThread("liaoliao");
thread.start();
Handler handler1 = new Handler(thread.getLooper());
Handler handler2 = new Handler(thread.getLooper());
}
为什么HandlerThread不会有上面提到的线程问题呢?
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
boolean wasInterrupted = false;
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
/*
* We may need to restore the thread's interrupted flag, because it may
* have been cleared above since we eat InterruptedExceptions
*/
if (wasInterrupted) {
Thread.currentThread().interrupt();
}
return mLooper;
}
run方法和getLooper方法都使用了synchronized方法锁住了this,也就是当前HandlerThread对象。
当run方法里面的同步代码块先执行的时候,getLooper方法的同步代码块被上锁了,必须等到mLooper被赋值完成后才会拿到锁。
当getLooper里面的同步代码块先执行的时候,检测到mLooper是null,直接执行wait方法,当前主线程挂起,释放cpu资源,释放锁。run方法里面的同步代码块会立即执行,mLooper被赋值后,执行notifyAll方法,wait被重新唤醒,继续执行,最后获取mLooper返回。
wait方法挂起,会释放cpu资源,把锁释放给其他人用
sleep方法挂起,不会释放cpu资源,还会占有锁
可能上层同时有多个地方都在getLooper,多个线程都在调用getLooper方法,所以需要notifyAll
wait的时候会立刻把锁交出去,让自己处于阻塞状态
notify需要等待synchronized包裹的所有代码执行完毕,才会执行通知wait方法继续执行。
这与应用场景有关,如果上层对HandlerThread做了一层封装,也使用synchronized(this)。notifyAll,这时候wait的地方会继续执行,拿到的looper还是空的,所以while循环再判断一次。