Android HandlerThread源码解析

九路 等级 706 0 0

在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环。那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时的任务或者写日志的功能。这就是HandlerThread的作用 Android Handler消息机制源码解析

1 使用方法如下

在MainActivity中添加一个HandlerThread的变量,如下:

public class MainActivity extends AppCompatActivity {
    HandlerThread thread = new HandlerThread("test");
    Handler handler;

在 onCreate()函数中开启线程,获取线程的looper,如下:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //1 开启线程
        thread.start();

        //2 获取线程对应的looper,并用这个looper构造出一个Handler
        //3 并重写Handler的handleMessage()方法
        handler = new Handler(thread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what == 100){
                    Log.e("TAG","线程名=" + Thread.currentThread().getName());
                    Log.e("TAG","接收到的数据为:" + msg.obj.toString());
                }
            }
        };

        findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("TAG","线程名:" + Thread.currentThread().getName());
                Message message = handler.obtainMessage();
                message.what = 100;
                message.obj = "hello world";
                handler.sendMessage(message);
            }
        });
    }

点击事件,输出如下:

2018-11-24 12:49:06.575 13589-13589/com.fax E/TAG: 线程名:main
2018-11-24 12:49:06.576 13589-13612/com.fax E/TAG: 线程名=test
2018-11-24 12:49:06.576 13589-13612/com.fax E/TAG: 接收到的数据为:hello world

由上面可以看到,我们新建了一个Handler,对应的looper是从HandlerThread实例thead获取的,我们在点击事件中,获取一个消息并用handler分发,主线程发送的消息,在子线程中处理了。

比如我们有这样一个需求: 在用户使用APP的时候,需要记录用户的行为,需要把日志记录到本地文件中,等到一定的时机我们再统一一次性把文件上传到我们的服务器。

那么我们就可以开一个线程,在后台等待写日志的任务的消息到来,收到消息后就把日志顺序的写入到文件中。这时就可以用HandlerThread,省去了我们自己开线程,写任务队列,完成消息循环,这些HandlerThread都帮我们封装好了。下面我们来分析HandlerThread的源码。

2 HandlerThread源码分析

首付在使用的时候,我们直接 new 了一个HandlerThread对象 HandlerThread thread = new HandlerThread("test");

HandlerThread类定义如下:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    ......
}

HandlerThread从字面意思上看,是一个和Handler结合起来用的Thread。 再看HandlerThread的构造函数:

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

构造函数仅仅是对线程的优先级和名字进行赋值。 接着往下看,我们调用了 thread.start() ,由于HandlerThread是一个继承Thread,所以会调用run()方法,源码如下:

  @Override
    public void run() {
        //1 保存线程的id,没什么好说的
        mTid = Process.myTid();

        //2 主要是这句,调用了Looper.prepare()
        // 由上篇Handler源码分析可知,这里创建了一个Looper对象
        Looper.prepare();

        //3 获取当前线程对应的Looper对象,保存起来
        // 加锁是为了防止多线程问题
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);

        //4 在循环之前有一个回调,空实现
        onLooperPrepared();

        //5 进行消息循环
        Looper.loop();
        mTid = -1;
    }

通过上面可知: 1 HandlerThread就是一个线程类,在run()方法的开头调用了Looper.prepare()来创建一个线程对应的Looper对象,并保存起来。 2 在线程的最后面调用了Looper.loop()对消息进行循环。

所以如果外面想要用的话,HandlerThread必须有一个对外的方法,来返回当前线程对应的Looper对象,找一下源码,果然有一个getLooper()方法:源码如下:

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

返回当前线程的Looper实例,这样外面想用这个的时候,就可以调用getLooper()获取Looper对象,然后再创建一个Handler对象,并把looper传入,这样就可以在其它线程中发送消息,在当前创建的子线程中处理了。

既然这样,那么有没有这样一个方法,直接返回对应的Handler呢,里面就保存了Looper对象。还真有这样一个方法,如下:

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

看这个方法,new 了一个Handler对象,并调用getLooper()把当前的Looper对象传入了,并返回了当前这个Handler对象

但是我们注意到,这个方法是@hide,就是我们在外面并不能调用这个方法,为什么Google已经写了这个方法但是又把这个方法给隐藏起来了不让我们调用呢?

个人猜测是因为我们调用getThreadHandler()的前提是得先调用start()方法,有了Looper对象后才能调用这个方法,要不获取到的Handler里面是没有Looper实例的,也就没法完成消息循环,所以Google把这个方法给隐藏了。

所以我们还是像上面的那样用法,先start(),再获取Looper对象,再创建Handler对象。

那么线程有运行的时候,也应该有退出的时候,当前有,我们看quit()方法:

 public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

这就是HandlerThread的源码,下篇我们讲IntentService的源码,和HandlerThread结合起来用的。

收藏
评论区

相关推荐

Android Handler消息机制源码解析
好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础。下面我们分析一下Handler的源码实现。 Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,Message Handler : 获取消息,发送消息,以及处理消息的类 Mes
Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环。那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时的任务或者写日志的功能。这就是HandlerThread的作用 Android Handler消息机制源码解析(https://www.cnblogs.co
4.1 手写Java PriorityQueue 核心源码
本章先讲解优先级队列和二叉堆的结构。下一篇代码实现 从一个需求开始 假设有这样一个需求:在一个子线程中,不停的从一个队列中取出一个任务,执行这个任务,直到这个任务处理完毕,再取出下一个任务,再执行。 其实和 Android 的 Handler 机制中的 Looper 不停的从 MessageQueue 中取出一个消息然后处理是一样的。 不过这个需
每日一问(一)Handler相关知识
1、Handler负责发送Message和处理Mesage2、Message就是消息载体,可用what区分,也可传递对象3、Message Queue消息队列,存储Message4、Looper循环取出Message Queue里的Message交给Handler处理。5、一个线程只有一个Looper和Message Queue,子线程中使用Handler一
Android HandlerThread和IntentService
HandlerThread HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它实现也很简单,就是在run中通过Looper.prepare()来创建消息队列,并且通过Looper.loop()来开启消息循环,这样再实际使用中就允许在HandlerThread中创建Handle了。 public cla
Android HandlerThread详解
概述 == Android HandlerThread使用,自带Looper消息循环的快捷类。 详细 == #### 代码下载:[http://www.demodashi.com/demo/10628.html](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fwww.demodashi
Android Looper,Handler,Message分析
**Looper** Looper的构造方法是private的,不可以在外部生成Looper的实例. 最重要的几个变量: ThreadLocal :静态的,保存所有线程及其所关联的Looper实例.可以理解为这是一个Map,保存了线程\->Looper的所有键值对.这里可以获取到所有的线程及其关联的Looper对象.当我们需要生成一个支持消息循环特性的
Android中AsyncTask的使用
原文 https://blog.csdn.net/liuhe688/article/details/6532519 在Android中实现异步任务机制有两种方式,Handler和AsyncTask。 Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细
Android中的Handler, Looper, MessageQueue和Thread
前几天,和同事探讨了一下Android中的消息机制,探究了消息的发送和接收过程以及与线程之间的关系。虽然我们经常使用这些基础的东西,但对于其内部原理的了解,能使我们更加容易、合理地架构系统,并避免一些低级错误。 对于这部分的内容,将分成4小节来描述: 1.职责与关系 2.消息循环 3.线程与更新 4.几点小结 \-----------------
Android消息循环分析
我们的常用的系统中,程序的工作通常是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的。 消息驱动的原理就是: 1\. 有一个消息队列,可以往这个队列中投递消息; 2\. 有一个消息循环,不断从消息队列中取出消息,然后进行处理。 在Android中通过Looper来封装消息循环,同时在其中封装了一个消息队
Android的消息处理机制(图+源码分析)——Looper,Handler,Message
作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想。android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了。这不,前几天为了了解android的消息处理机制,我看了**Looper,Handler,
Android里面所说的Looper
Looper即:有消息循环的线程。 在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个 Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的机制Handle,有消 息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有对应的处理,消
HandlerThread
andlerThread类实现了Looper的循环处理消息的功能 HandlerThread handlerThread = new HandlerTread("handler\_thread"); //使用HandlerThread的getLooper方法之前必须先调用start方法 handlerThread.start(); //取出h
Handler更新UI的几种方式
Handler、loop、MessageQueue的工作原理 Message:Handler接收和处理的消息对象 Looper:每个线程只能拥有一个looper.它的loop方法负责读取MessageQueue中的消息,读到信息之后就把消息返回给handler处理 MessageQueue:消息队列。程序创建Looper对象时,会在它的构造器中创建