Android HeadlerThread

  • HandlerThread 类功能介绍
  • 示例
  • 源码浅析
  • 总结

HandlerThread 类功能介绍

/**
 * 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.
 */
public class HandlerThread extends Thread {...}

首先HandlerThread就是一个继承了Thread类的线程类。那这个类有什么特点呢,注释中介绍到:
可以很方便的帮我们新建一个带有looper的线程。而且这个looper可以用来创建handler对象。但是start()方法还是必须要调用的。

写过线程间通信的朋友应该知道,线程的run方法中需要去处理一下looper的操作,而HandlerThread帮我们完成了这一步。

了解了这些就来使用吧

示例

  1. 创建一个继承自HandlerThread的类
public class MyHandlerThread extends HandlerThread {
        private Handler mHandler;
        
        public MyHandlerThread(String name) {
            super(name);
        }
        
        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            if (mHandler == null) {
                mHandler = new Handler(this.getLooper()){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.i("name", "msg: ");
                    }
                };
            }
        }

        @Override
        public void run() {
            Log.i("name", "threadName1: " + Thread.currentThread().getName());
            super.run();
        }
        @Override
        public synchronized void start() {
            super.start();
            Log.i("thread", "start");
        }
        /**
         * {@link #start} 之后调用
         *
         * @return
         */
        public Handler getmHandler() {
            return mHandler;
        }
    }

这里重写了onLooperPrepared方法,目的是looper开始轮询之前,实例化Handler对象,在示例化Handler的时候传入了当前线程的looper。所以Handler的handleMessage方法运行在子线程中。

  1. 接下来就很简单了创建HandlerThread并开启线程
 	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        ButterKnife.bind(this);
        handlerThread = new MyHandlerThread("handlerThread");
        handlerThread.start();
    }

他毕竟还是一个线程类,不调用start方法线程是无法进入就绪状态的。

  1. 需要的地方写发送消息
 	@OnClick(R.id.send)
    public void onViewClicked() {
        Message obtain = Message.obtain();
        handlerThread.getmHandler().sendMessage((obtain));
    }

这三步就完成了UI线程向子线程中发送消息,如果子线程在执行完操作后返回给UI线程,只需要拿到一个UI线程的Handler即可。

  1. 子线程向UI线程发送消息
	//实例化一个UIhandler
  private Handler uiHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i("name", "msg: uiHandler");
        }
    };
...
  	@OnClick(R.id.send)
    public void onViewClicked() {
        Message obtain = Message.obtain();
        //让消息携带UIhandler过去
        obtain.obj=uiHandler;
        handlerThread.getmHandler().sendMessage((obtain));
    }
    ...
     mHandler = new Handler(this.getLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.i("name", "msg: MyHandlerThread");

                        if (msg.obj instanceof Handler){
                            Message obtain = Message.obtain();
                            //取出UIhandler,发送消息
                            ((Handler) msg.obj).sendMessage(obtain);
                        }
                    }
                };

上面四步,完成了UI线程和子线程的双向通信。

  1. 不需要的时候退出任务
    比如退出界面的时候,需要退出HandlerThread。
    HandlerThread有两个退出的方法
//清空队列中所有的消息
  public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
 //清空队列中所有的延迟消息,派发完未发送非延迟的消息
 public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
//最终调用的是MessageQueue里的quit方法
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
            //安全
                removeAllFutureMessagesLocked();
            } else {
            //不安全
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

下面是copy:

从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
  当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。

源码浅析

上面已经介绍了对出的两个方法。

  • 构造方法
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;
    }

上面两个构造方法分别指定了线程名和优先级级别。继续点下去,回到Thread 的init方法,新建一个Thread对象

  /**
     * Initializes a Thread.
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     */
    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        Thread parent = currentThread();
        if (g == null) {
            g = parent.getThreadGroup();
        }

        g.addUnstarted();
        this.group = g;

        this.target = target;
        this.priority = parent.getPriority();
        this.daemon = parent.isDaemon();
        setName(name);

        init2(parent);

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;
        //实例化的时候就先让线程id+1
        tid = nextThreadID();
    }
    ...
     private void init2(Thread parent) {
        this.contextClassLoader = parent.getContextClassLoader();
        this.inheritedAccessControlContext = AccessController.getContext();
        if (parent.inheritableThreadLocals != null) {
        	//给当前线程创建ThreadLocalMap对象,并且继承parent的ThreadLocalMap中的数据
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                    parent.inheritableThreadLocals);
        }
    }

初始化一个线程的基本属性。有兴趣可以了解下类加载器ClassLoader和ThreadLocalMap。

  • run方法
    start之后接下来就是run方法了
  @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

	/**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

run方法主要就是进行Looper的设置。HandlerThread另外提供了一个onLooperPrepared方法,方便我们在loop(),做一些准备工作。

	/**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

其实HandlerThread的内部有一个handler对象,不过对外设置了hide。

总结:

  1. 利用HandlerThread实现线程间通信,我们不需要再去关系Looper的一些调用和设置。
  2. 只需要处理好Handler对象的消息处理逻辑即可。
  3. 记得在适合的地方退出HandlerThread,避免内存泄露。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值