Android_HandlerThread 源码梳理

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;
    }
    ...
}

HandleThreadj继承自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循环中。

扫描二维码关注公众号,回复: 8632134 查看本文章

以上就是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。

发布了41 篇原创文章 · 获赞 35 · 访问量 4304

猜你喜欢

转载自blog.csdn.net/weixin_38140931/article/details/103239847
今日推荐