handlerThread使用场景分析及源码解析

本文通过具体示例解析了Android中HandlerThread的使用场景及其工作原理,介绍了如何利用HandlerThread简化子线程Handler的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 这篇博客将结合Android7.0源码,解析HandlerThread的使用场景及实现原理。

1.为什么要有handlerThread组件出现?

   在Android中我们使用消息机制进行线程间的消息传递,如果是向主线程传递消息,我们构造主线程的handler,并使用他发送消息。使用起来还是很方便的。但是如果是要子线程传递消息,就需要构建子线程的handler. 这样使用起来就比较麻烦了!比如下面这样:

 public class Main2Activity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        new Thread(new Runnable() {
            @Override
            public void run() {

                Looper.prepare();

                Handler handler=new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        //处理子线程的消息
                    }
                };

                Looper.loop();
            }
        }).start();
    }
}

我们得在子线程中构建Looper,并调用Looper的loop方法才能正确的构建出子线程的handler对象。这样用也没什么,但代码看着实在是很不舒服呀。 所以android给我们提供了HandlerThread组件,封装了子线程中构建looper的繁琐操作。

2.HandlerThread的使用场景

   在介绍HandlerThread之前,我们先想一个它的使用场景,通过使用场景,结合使用示例。我们才能在分析它的源码的时候有一个好的切入点,而且还可以加深我们对于handlerThread的一些印象; 可以想象一下这样的一个场景,例如我之前研发了一款股票交易软件,因为股票的行情数据都是实时变化的。所以我们软件需要每隔一定时间向服务器请求行情数据。
代码示例如下:

public class MainActivity extends AppCompatActivity {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
    TextView updateTimeTv;
    Handler mUIHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //更新ui
            updateTimeTv.setText((String)msg.obj);
            //1秒后再次请求服务器,并更新数据
            timeMessagehandler.sendEmptyMessageAtTime(0,1000);
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        updateTimeTv = (TextView)findViewById(R.id.tv);
        initThreadHandler();
    }

    HandlerThread handlerThread;
    Handler timeMessagehandler;
    private void initThreadHandler() {
        //构建HandlerThread
        handlerThread=new HandlerThread("updateTimeThread");
        //启动handler
        handlerThread.start();
        //构建子线程的handler对象
        timeMessagehandler=new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                //请求服务端时间
                try {
                    //模拟耗时请求
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //网络请求成功,通过主线程更新ui
                Message message=Message.obtain();
                message.obj= df.format(new Date());
                mUIHandler.sendMessage(message);
                System.out.println("handlerTimeMessage"+Thread.currentThread().getName());
            }
        };

        //向子线程发送消息,通知它发送网络请求
        timeMessagehandler.sendEmptyMessage(0);
    }

}   

我们分析一下上面的代码实现,一步一步来解析HandlerThread的使用,略过mUIHandler和view的初始化代码不表。我们可以看到上面的代码先构建了一个HandlerThread对象,然后调用了它的start方法。 这里我们可以提前剧透一下。HandlerThread对象对象其实继承了Thread对象。所以调用它的start方法就会执行它的run方法。 然后我们用handlerThread.getLooper()方法构建一个timeMessagehandler对象。 从上面的代码看timeMessagehandler对象的handleMessage中可以执行耗时操作,也就说明该方法是执行在子线程中的。那handlerThread.getLooper()方法所获取的方法也就肯定是子线程的Looper对象了。 这部分不清楚的可以看我另一篇文章,android消息机制源码分析http://blog.youkuaiyun.com/nightcurtis/article/details/77539971。 所以我们就可以timeMessagehandler对象向子线程发送消息执行耗时任务, 然后通过主线程的mUIHandler对象发消息处理ui的显示。 这些代码就太简单这里就不说了。 结合这段使用代码实例,我们可以知道我们下面这短短的3行代码就能够完成之前构建线程,初始化looper等等操作才能完成的构造子线程handler的这样一个需求。这是怎么做到的呢!

      handlerThread=new HandlerThread("updateTimeThread");
        //启动handler
      handlerThread.start();
        //构建子线程的handler对象
      timeMessagehandler=new Handler(handlerThread.getLooper());

3.HandlerThread的源码解析

   通过HandlerThread的使用场景解析我们知道通过短短3行代码就可以完成之前初始化looper,构建handler对象,并调用loop才能正确创建的子线程handler对象。 这里面的秘密是什么呢? 结合HandlerThread的源码。我们来解析一下:

   public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }


   public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }


   protected void onLooperPrepared() {
    }

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


    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的源码很简单。HandlerThread继承了Thread对象,所以我们构造HandlerThread对象的时候就是构建了一个子线程对象。 调用线程对象的start方法,就会执行HandlerThread的run函数。 我们看到在run方法中有 Looper.prepare(),Looper.loop()两个方法。其实和我们在子线程中构建looper的方法完全相同。 真正值得注意的是,发现在run方法中和looper的getLooper中有一个线程通信的处理。 这个是为什么呢? 想想之前我们是主线程中先构建handlerThread对象,然后调用它的start方法,去初始化looper对象。 然后就调用handlerThread的getLooper()方法去初始化一个子线程的Handler对象。 我们知道 handlerThread的start方法是执行在子线程中的,而handlerThread的getLooper()方法是执行在主线程中的。我们不能确认他们谁先执行,如果是getLooper()方法先执行,而这个时候子线程的looper还没有创建。构建handler就会抛出异常。 所以我们需要确保getLooper()方法在Looper创建成功后执行。所以在这里添加了线程通信的处理。

4.总结

其实分析源码后我们能够发现HandlerThread的源码特别简单,但我们解析它的源码不止是为了分析它的源码。更重要的是要学习它的一下封装的思想。 还有就是了解它的原理后,以后遇到使用了HandlerThread的组件心里上会有征服感。哈哈。 比如了解了HandlerThread的原理后再去看IntentService的原理。 真是不能再简单了。 有么有!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值