为什么要使用HandlerThread

关注公众号“码农帮派”,查看更多系列技术文章:

 

在android-sdk/sources/android-x下可以看到源码。

 

在android.os的包下,有一个HandlerThread的类,继承自Thread,是Google提供的线程类。

 

package android.os;

public class HandlerThread extends Thread {}

 

 

为什么要提供这类呢,下面对此进行分析:

 

      在Android开发中,当我们进行耗时操作,如网络请求时,为了不阻塞UI线程,会讲耗时操作在子线程中进行,等耗时操作完成之后,在子线程中通过Handler发送Message到UI线程进行UI的更新,那么为什么子线程可以通过Handler可以将Message跨线程发送到UI Thread呢?

 

     查看Handler的源码,我们可以发现,Activity在ActivityThread中被创建,在ActivityThread的main()方法中,会调用Looper.prepareMainLooper()的方法,在Looper类中,prepareMainLooper()的过程是new Looper(),然后将创建的Looper对象保存在当前线程(Activity Thread/ UI Thread)的ThreadLocal变量中,而我们会发现,new Looper()的过程中,同时会创建一个MessageQueue,并与当前的Looper关联在一起,在prepareMainLooper()之后,紧接着会有mLooper.loop()的过程,即启动了该Looper对象。

      当我们在UIThread中new Handler()的过程中,其中最重要的步骤,就是currentThread.sThreadLocal.get(),这一步拿到了当前线程(UIThread)的Looper对象mLooper,并进行了this.mQueue = mLooper.mQueue的过程,就是将Looper的MessageQueue对象绑定到Handler上,Handler有一个内部属性mQueue( MessageQueue对象 )。这样主要的流程就结束了,当子Thread通过Handler对象sendMessage()的时候,在Handler内部源码可以看到,内部是通过dispatchMessage(Message msg)的方法,最终将Message加入到了Handler的内部属性mQueue中,而new Handler()的时候, 该Handler对象的内部属性mQueue已经指向了Looper对象的MessageQueue,而该Looper对象来自于当前线程(UIThread)的ThreadLocal变量,也就是说,子线程中handler.sendMessage()的过程,实质上就是把一个Message对象加入到了UIThread的MessageQueue中,那UIThread如何去处理这些加入到它的MessageQueue中的Message呢?MessageQueue的创建伴随着Looper的创建,而Looper在被ActivityThread创建的之后,立即调用了该Looper对象的loop()方法,该方法的源码可以看到,是启动了一个死循环,阻塞的从MessageQueue中取出Message,当取出的Message msg != null时,便拿出Message对象的target,调用它的dispatchMessage(Message)的方法:

msg.target.dispatchMessage(msg);

     那这个target是什么呢?翻看源码,可以发现,target的类型是Handler,也就是说,loop()函数在UIThread中建立了一个轮训的机制,不停的读取MessageQueue中的Message,当读到之后,就会将读到的Message对象发送给Handler,而Handler对象就会调用new Handler()过程中重写的方法,比如handlerMessage(Message msg)方法,这样就将一个Message信息从子线程最终传递到了UIThread中。

 

     上面就是子Thread如何通过Handler与UIThread通信的过程,即Handler的机制,那么问题来了,上面的过程从始至终都是子线程向UIThread发送信息,并没有反过来的过程,也就是UIThread如何给子线程发送Handler信息,其实我们翻看Handler的源码会发现,Handler的构造函数有很多,其中一个是new Handler()最为常用,但是还有一个是new Handler(Looper mLooper),其实在使用无参构造函数的时候,在绑定Handler的mQueue的时候会有一个currentThread.sThreadLocal.getLooper()的过程,就是获取当前线程的Looper对象,当我们在new Handler()的时候要是传入一个Looper对象,就会将Handler的mQueue指向传入的Looper对象的MessageQueue了,那么我们要是传入子线程的Looper对象,那么在UIThread中handler.sendMessage(msg)时,发送的Message对象最终会发送到子线程的MessageQueue中,而子线程同样可以looper.loop()的方式循环的读取Message,并在子线程中处理。

 

这里需要说明,Looper,MessageQueue以及Message这一套体系来自于Android系统,而Thread来自于java,所以Thread中并没有Looper这一套内容,必须我们自己写入。

 

那么按照上面的思路,我们的代码,就可以如下:

 

package com.handlerThread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

/**
 * Created by maxiaodong on 16/7/25.
 */
public class ThirdActivity extends Activity
{

    Handler threadHandler;
    MyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        thread = new MyThread();
        thread.setName("我是子thread");
        thread.start();

        try
        {
            Thread.sleep(1000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        threadHandler.sendEmptyMessage(0x1);
    }

    private class MyThread extends Thread
    {

        @Override
        public void run()
        {
            Looper.prepare();
            // CODE
            threadHandler = new Handler(Looper.myLooper())
            {
                @Override
                public void handleMessage(Message msg)
                {
                    Log.v("tag", Thread.currentThread().getName());
                }
            };
            Looper.loop();
        }
    }
}

 

 

 

打印:

 

注意,我们在UIThread中通过Handler向子线程发送Handler Message的时候,首先让UIThread-sleep了1s,那么要是没有这个sleep,会如何呢?

在UIThread向子线程发送Handler Message的时候,出现了一个null reference,这是由于子线程的Looper是在其run()函数中创建的,当调用子线程的start()方法,子线程并不一定立即运行,这要看CPU是否刚好分配给它时间片段,要是分配到了,那就会运行子线程run()中的方法,但要是没有CPU时间片段,那么它就需要等待,要是在子线程还没有运行run()方法的时候,UIThread先获得CPU时间片段,那么UIThread就会在子线程之前首先运行到threadHandler.sendEmputyMessage(0x1)的地方,而这个方法会向handler的指向的MessageQueue中发送消息,此时子线程还没有创建自己的Looper,那么自然也还没有MessageQueue,所以就出现了上面的情况。

 

那么如何解决呢,使用HandlerThread,为什么HandlerThread就可以避免上面的Exception呢,看源码:

 

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }

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

 

上面就很明白了,当我们需要获取HandlerThread的Looper对象的时候,要是mLooper == null,则会让当前线程wait()一下,而在HandlerThread中,当mLooper被成功创建的时候,会调用notify()唤醒线程,这样就会让之前wait()的线程唤醒了。

 

而且,我们会发现HandlerThread的run()方法中,为我们实现了Looper对象的创建过程:

 

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

 

 

 

所以,一般的Thread对象中,我们可以删除run()方法中的super.run(),但是HandlerThread的子类中,一定不要删除run()方法中的super.run(),可以节省我们手动创建Looper的过程。

 

使用HandlerThread实现UIThread向子线程发送消息的过程如下:

 

package com.threadHandler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;

/**
 * Created by maxiaodong on 16/7/25.
 */
public class ThirdActivity extends Activity
{

    Handler threadHandler;
    MyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        thread = new MyThread("我是子thread");
        thread.start();

        threadHandler = new Handler(thread.getLooper())
        {
            @Override
            public void handleMessage(Message msg)
            {
                Log.v("tag", Thread.currentThread().getName());
            }
        };

        threadHandler.sendEmptyMessage(0x1);
    }

    private class MyThread extends HandlerThread
    {

        public MyThread(String name)
        {
            super(name);
        }

        @Override
        public void run()
        {
            super.run();
        }
    }

}

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值