Android必读之HandlerThread

HandlerThread是Thread的子类,主要特点就是为我们主动封装了Looper,这样我们就可以和Handler结合在一起使用,利用Handler的消息机制原理为我们更加有序高效的管理Thread通信和其它逻辑,这也为什么我们通常在自定义的Thread中使用Handler的原因。
首先我们先了解一下自定义Thread如何使用Handler,下面是完整Kotlin代码:

class MyThread() : Thread() {
    var handler: Handler? = null;
    override fun run() {
        super.run()
        Looper.prepare();//准备looper
        initHandler();
        //处理耗时操作,比如请求API,大量数据读写等
        Log.d("MyThread", "MyThread-开始处理耗时逻辑");
        getWeather()
        //开始处理消息,确保在loop方法放在最后,因为这是无限循环,后面的代码不会执行
        //当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行
        //逻辑运行完成后调用quit或者quitSafely退出,否则会造成这个Thread会一直运行,从而造成内存泄露
        Looper.loop();
    }
    fun initHandler() {
        handler = Handler(object : Handler.Callback {
            override fun handleMessage(msg: Message?): Boolean {
                when (msg!!.what) {
                    0 -> {
                        Log.d("MyThread", "MyThread-Success");
                        handler!!.sendEmptyMessage(2)
                    }
                    1 -> {
                        Log.d("MyThread", "MyThread-Failed");
                        handler!!.sendEmptyMessage(2)
                    }
                    2 -> {
                        Log.d("MyThread", "MyThread-结束");
                        handler!!.looper.quitSafely();
                    }
                }
                return true;
            }
        })
    }
    fun getWeather() {
        var response = HttpUtils.getInstance().requestWeather();
        if (response != null) {
            var result: String = response.body()!!.string();
            Log.d("MyThread", result);
            handler!!.sendEmptyMessage(0)
        } else {
            handler!!.sendEmptyMessage(1)
        }
    }
}

新建的Thread中没有Looper,需要我们指定或者使用Looper.prepare()新建一个,看一下prepare的源码就知道了:

    public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//新建Looper
    }

如果没有指定Looper就创建Handler会报以下Exception:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:204)
        at android.os.Handler.<init>(Handler.java:132)

总结以上代码我们发现如果我们在自定义的Thread中使用Handler需要我们主动去实现Looper相关的功能,还是比较麻烦的,所以Android已经为我准备了HandlerThread类,已经封装好了Looper的逻辑,看一下Handler Thread中run方法的源码:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

Handler Thread使用起来也非常简单:

//var为可变变量 和val为常量相当于final
    val handlerThread = HandlerThread("MyHandler");
    var handler: Handler? = null;
    fun initHandlerThread() {
        handlerThread.start();
        val looper = handlerThread.looper;
        //handler运行在handlerThread线程中,不能直接操作UI
        handler = object : Handler(looper) {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                when (msg!!.what) {
                    0 -> {
                        getWeather()
                    }
                    1 -> {
                        Log.d("HandlerThread", "HandlerThread-退出");
                        handlerThread.quitSafely()//安全推送线程
                    }
                }
            }
        };
    }
    /**
     * 因为getWeather方法运行在handlerThread线程中,所以不用担心ANR,使用同步方式请求API
     */
    fun getWeather() {
        var response = HttpUtils.getInstance().requestWeather();
        if (response != null) {
            var result: String = response.body()!!.string();
            Log.d("HandlerThread", result);
        }
        handler!!.sendEmptyMessage(1)
    }

在使用HandlerThread时我们一定要注意需要我们及时退出Looper,否则容易造成内存泄露。Looper退出的方法有两个quit和quitSafely,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;
    }

再看一下Looper中的源码:

    public void quit() {
        mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }

都是调用MessageQueue的quit的方法,只不过一个传了false,一个传了true,区别就是quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于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);
        }
    }
    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

 

点赞

发表评论