大家好,又见面了,我是你们的朋友全栈君。
前言
Android 多线程还有HandleThread,看名字就可以能感觉到得到,会是handler和Thread的综合使用。那到底是怎么样的呢,现在就跟随Android的源码来看看他的工作原理是什么样的。
我们先看看他的类注解:先看看官方对他的介绍:【 Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.】简单的翻译就是这是一个很方便启动一个拥有looper的的类,这个Looper在这以后是用来创建handler类的,注意:thread的start()方法一定要被调用。
理解HandleThread
看到介绍可能觉得HandleThread有点厉害,有looper还能创建handler。我们抱着这样的心情去看HandleThread比较难受,所以,我们要换一下思路,这个HandleThread就是个Thread,这个Thread执行工作任务,我们通过handler给这个Thread安排工作。【从使用角度就相当于我们自己起一个工作线程,然后在线程中自己prepareLooper一样。只不过HandleThread已经把这些做好了,还封装了优先级设置,安全退出等一些辅助功能,让我们开发人员使用起来更加方便】
handler我们知道可以用来线程间通信,之前在Handler工作流程梳理里面分析过【具体在本篇就不细说】,我们这里要知道,Handler可以把Message发送到MessageQueue中,通过Looper.loop()循环将Message取出并分派到到相应的Handler去处理。同一个线程中只有一个MessageQueue和Looper, Message的处理是串行的。当有Thread使用了Handler,那么通过Handler给Thread安排任务也是串行执行的,就是一个执行完才执行下一个,所以这个HandleThread不适多耗时任务,这样任务的执行相互收影响比较严重。对于任务量小,使用频繁的任务来说就比较友好,可以使用一个线程来实现线程池的效果,节省了资源。
源码分析
看了上面的理解描述后,我们就可以猜到HandleThread代码逻辑应该不复杂,代码量也不会很大。事实就是这样,加上注解,HandleThread的代码在160多行。
/frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...
}
HandleThread继承自Thread,首先我们就看到了该有的Looper对象,然后从它的构造函数看,可以设置线程优先级和给线程命名。在使用时如果不设置优先级,会默认设置为Process.THREAD_PRIORITY_DEFAULT;
HandleThread主要的逻辑就在run()方法里面
1.run()
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在run()里。Looper.prepare()来初始化当前线程的一个Looper。然后拿到一个looper对象 赋值给mLooper,呃呃呃。这里为啥要notifyAll(),在Java中wait()和notify/notifyAll是一起使用的,主要用在多线程中,,用在这里又是为什么?我们在HandleThread搜索一下wait()果然有:
1.2 getLooper()
public Looper getLooper() {
...
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
在getLooper()方法里,当looper还没有创建时间,需要当前线程保持wait,这是为什么呢?我估计是当线程启动,在没有获取到looper之前,当前线程是空闲的,也没任务做,就先释放资源,让其他线程先执行run()方法来获取Looper对象,然后将looper赋值给mLooper,【当我看到这里时是很疑惑的,这个在文章后面来介绍】;
再回到run()方法中;获mLooper赋值后,随后调用notifyAll(),【此时其他线程的getLooper()方法也会被唤醒,就可以利用looper去创建handler。然后再handleMessage()中实现处理任务的业务代码】;然后接下里就主要是进入loop循环了。这就之前分析handler看到的线程最后的状态是一毛一样,那就是随后线程都会进入到loop循环中。
以上就是HandleThread的主要原理了。
———————————————————-华丽丽的分割线————————————————————————–
疑惑
现在要来说说我在分析那个mlooper被赋值时遇到的疑惑,还记得我们在Handler工作流程梳理事看到Looper.getLooper()最后是从ThreadLoacl中取出的,在Android_ThreadLoacl原理一篇中,我们知道threadLocal保存的变量是与线程相关联的,所以不同线程get到的Looper是不一样的。
疑点来了啊,注意听。假如线程A运行到HandleThread的getLooper()方法,如果此时mlooper为空。那么线程A就会wait,阻塞在这里;那么后来线程B运行到HandleThread的run()方法,从线程B中获取到线程B的looper,然后赋值给mLooper;之后线程A就可以被唤醒,返回mLooper;那么这时候线程A返回的looper其实是在线程B中创建的looper,我就很乱了,,,不应该looper和线程相关的吗?在这里怎么可以这么玩?
反正我是很乱的,问了同事也还没问个明白。有一点肯定的,源码中这么玩肯定是没错的,我需要一个可以让自己信服的理由。
既然没问道直接的答案,按我自己试试,然后,我写了个demo,在主线程中获取主线程的MainLooper,然后在子线程创建Handler时直接传入MainLooper。最后发现, 子线程是可以使用主线程的looper。恩恩,终于,用事实说明了,多线是可以公用looper的。好了,HandleThread中的疑惑解决了;
接下来,我就有了新疑惑,我需要继续搞明白,为什么多线程可以共用looper。这和我之前的理解不一样,我之前的理解是线程中的looper和线程相关,当前线程只能使用当前线程的Looper。
解惑
答案还是得要在源码里面找,经过观察发现HandleThread和我们一般的在子线程中创建Handler的方式是不同的;
- 子线程中直接我们一般是这样的:
/ *
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
* /
- 使用HandelrThread时是这样的
private HandlerThread mMyhandleThread = new HandlerThread("MyHandleThread");
Handler mHandler;
mMyhandleThread.start();
mHandler = new Handler(mMyhandleThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// TODO
}
};
我感觉问题还是在Handler上。那我们就去看看Handler 的构造函数:
/frameworks/base/core/java/android/os/Handler.java
// 1.我们一般手动在子线程就是这样创建Handler的
public Handler() {
this(null, false);
}
...
public Handler(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;
}
//------------------------华丽丽的分割线------------------------
// 2.我们一般使用HandlerThread时是这样创建Handler的
public Handler(Looper looper) {
this(looper, null, false);
}
...
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
通过对不同方式创建Handler时调用的构造方法,我们发现 ,handler的如果在构造函数中指定了Looper,就直接使用传进来的looper对象,如果在构造事没有传入looper对象,则此时就需要使用本线程自己的looper;至于messageQueue只是和Looper相关,MessageQueue和handler并没有直接联系。
结论
所以我们得出结论,多线程可以使用同一个Looper,相应的也就是使用了相同的MessageQueue。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126973.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...