Android HandlerThread全面解析

2018-11-20  本文已影响0人  CyanStone

在对Android异步消息处理机制源码剖析Android AsyncTask源码剖析后,下面对Android中的另一个和异步消息机制相关的类-HandlerThread进行全面解析。

为什么要有HandlerThread?

 我们已经知道,主线程与子线程之间的通信,可以依靠Handler,Looper和MessageQueue来实现。一个线程中可以有唯一的Looper对象,负责消息循环;一个Looper中都有唯一的MessageQueue,管理者消息队列;Handler在发消息前,必须与一个Looper进行绑定,也就与MessageQueue进行了绑定。一切准备就绪后,可以通过Handler把消息Message发送出去,Message会在MessageQueue中入队,然后等待Looper把消息取出并交给发送消息体的Handler进行消息的处理。
 如果是主线程向子线程进行通信,在创建Handler之前,则必须在子线程中调用Looper.prepare()方法为线程初始化一个Looper对象。一旦忘记调用Looper.prepare()方法,在创建Handler的时候会抛出异常。

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
    }
   ...
}

 所以Google为了避免开发者忘记调用Looper.prepare()而导致的异常,对Thread类进行了封装,使得Thread类在初始化的时候便有了一个Looper,这便是我理解的HandlerThread产生的由来。如有不对的地方,欢迎指正交流。


HandlerThread的源码剖析

 HandlerThread源码非常的少,只是对Thread进行了简单的封装,如果对Android异步消息机制了解的同学,理解它的源码非常的简单。下面来看HandlerThread的源码。

public class HandlerThread extends Thread {
    int mPriority;  //线程执行优先级
    int mTid = -1;  //调用线程的标识符
    Looper mLooper;  //线程内部的Looper对象,一个线程只有一个Looper对象
    private @Nullable
    Handler mHandler; //与Looper绑定的Handler对象

    public HandlerThread(String name) {
        super(name);
        //设置优先级
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
   //Looper开启循环前调用,子类可按需覆写
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        //返回调用线程的标识符
        mTid = Process.myTid();
        //初始化线程本地变量Looper
        Looper.prepare();
        synchronized (this) {
            //从Looper中拿到本线程的Looper对象
            mLooper = Looper.myLooper();
            notifyAll(); //唤醒线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();  //回调方法,在loop之前,子类可按需覆写实现
        Looper.loop();
        mTid = -1;
    }
    //返回looper对象
    public Looper getLooper() {
        //如果线程不是可用状态,则返回null
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        //如果线程被start了,但是mLooper还没有初始化,则让线程进入等待状态,直到mLooper对象被初始化后线程被唤醒
        synchronized (this) {
            while (isAlive() && (mLooper == null)) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    @NonNull
    //懒加载初始化Handler的单例对象,该handler与此线程获取的Looper进行绑定
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    //退出,执行的是looper.quit,其实执行的是Looper中MessageQueue.quit方法
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    //同上述方法,执行的是MessageQueue.quit方法
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
    //返回调用线程的标识
    public int getThreadId() {
        return mTid;
    }
}

以上便是HandlerThread的全部源码,非常的简短,下面做以下几点说明:


HandlerThread使用场景

Android异步消息处理机制源码剖析文章中,主线程向子线程进行通信,给了一个case:

     //创建子线程
class WorkThread extends Thread {
    private Looper looper; //取出该子线程的Looper
    public void run() {
        Looper.prepare(); //创建该子线程的Looper
        looper = Looper.myLooper(); //取出该子线程的Looper
        Looper.loop(); //只要调用了该方法才能不断循环取出消息
    }
}

private Handler mHandler;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    thread = new WorkThread();
    thread.start();
    //创建Handler时把looper做入参,把Handler与Looper绑定在一起
    mHandler = new Handler(thread.looper) {
        public void handleMessage(android.os.Message msg) {
            //在子线程中处理消息的逻辑
        };
    };
    mHandler.senMessage(Message.obtain());
}

上述例子很简单,它其实跟HandlerThread的实现思路是一致的,所以可以改成以下方式:

private private Handler mHandler;
class WorkThread extends HandlerThread {
    @Override
    public void run() {
      super.run();
      //执行业务操作
    }

    @Override
    protected void onLooperPrepared() {
       //按需选择是否覆写,在loop之前想做的操作
    }
}

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    HandlerThread thread = new WorkThread();
    thread.start();
    //通过ThreadHander去获取Looper来创建Handler
    mHandler = new Handler(thread.getLooper()) {
        public void handleMessage(android.os.Message msg) {
            //在子线程中处理消息的逻辑
        };
    };
    mHandler.senMessage(Message.obtain());

另外,如果不需要Handler对消息进行处理,也可以调用以下方式直接获取HandlerThread中的Handler发送消息:

    HandlerThread thread = new WorkThread();
    thread.start();
    //通过ThreadHander去获取Looper来创建Handler
    mHandler = thread.getThreadHandler();
    mHandler.senMessage(Message.obtain());

 在Android异步消息处理机制源码剖析中分析知道,在Looper中获取消息后,调用Handler.diapatchMessage()方法进行处理,处理的优先级顺序依次是:Message的callback(Runnable对象) -> Handler的Callback(实现了handleMessage方法的Callback接口实现类对象) -> Handler自身的handleMessage()方法。所以如果用HanderThread中的Handler想要处理Message的话,可以用以下两种方式:

    HandlerThread thread = new WorkThread();
    thread.start();
    //通过ThreadHander去获取Looper来创建Handler
    mHandler = thread.getThreadHandler();
    mHandler.setCallback(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg){
            //处理消息的逻辑
        }
    });
    mHandler.senMessage(Message.obtain());
    HandlerThread thread = new WorkThread();
    thread.start();
    //通过ThreadHander去获取Looper来创建Handler
    mHandler = thread.getThreadHandler();
    final Message msg = Message.obtain(mHandler, new Runnable(){
          @Override
          public void run() {
                //执行消息的处理,可以通过msg去获取传递的值进行逻辑处理
          }
    });
    mHandler.senMessage();

参考链接

上一篇下一篇

猜你喜欢

热点阅读