HandlerThread 使用详解(实例 + 源码)

上一篇:

IntentService 深度解析

上一篇我们从源码中看到IntentService内部的线程机制,是采用IntentService来实现的,这里我们就来深度解析下HandlerThread。推荐先看一下Handler机制详解(实例 + 源码)

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.

大意就是说HandlerThread,是一个具有Looper的线程,这个Looper可以用于创建Handler;另外start()必须执行,因为Handler毕竟是个Thread,调用start()会走到run()中,而HandlerThread恰恰是在run中创建的Looper。

先看个源码吧!毕竟很简单!

HandlerThread源码

  • name指定线程名字

  • priority代表优先级。优先级范围为-20到19,默认为0,优先级越高,获得的CPU资源更多,反之则越少。-20代表优先级最高,反之19最低。注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!

  • onLooperPrepared() 可以做一些初始化工作,在Looper.loop()之前调用

  • 当调用thread.start()后,会执行run方法,会通过Looper.prepare创建当前线程的Looper对象和与之对应的MessageQueue;Looper.myLooper() 获取当前线程的Looper对象;Looper.loop()循环进行消息分发

  • getLooper()获取当前线程的 Looper,如果线程尚未启动或者已经dead,就返回 null;如果线程已经启动但Looper 还没初始化完成,这个方法会阻塞直到looper已经准备好,并返回Looper对象

  • quit() / quitSafely() 只是对Looper的quit() / quitSafely()进行了一层封装

1、Handler是在哪个线程定义的,handler.handleMessage就是在哪个线程执行的。也就是说子线程中定义的Hanlder,收到消息,在handleMessage(){}中能够执行耗时任务。

这个不难理解吧,因为Looper.loop()是在子线程中执行的,会取到子线程的Looper对象和MessageQueue对象,进行遍历取出执行msg.target.dispatchMessage(),所以Handler.handleMessage()也会在线程中执行。

2、另外我们根据Looper创建的handler对象和Looper有什么紧密联系不?

首先在子线程中,我们根据子线程的looper创建handler对象;我们如果不指定Looper对象,会有一个默认的主线程的Looper对象,所以不管怎么样,通过handler.getLooper()都会返回一个Looper对象。 其次handler的两个主要方法:sendMessage()和handleMessage(),sendMessage与MessageQueue和Message有关,handleMessage就只与Message有关啦!所以并没有太多要紧的联系。

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

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    //指定线程的优先级,注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    //子类可以重写,在这里做一些执行前的初始化工作
    protected void onLooperPrepared() {
    }

    @Override
    public void run() { //当调用thread.start()后,会执行run方法
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); //Looper 已经创建,唤醒等待获取 Looper 的线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    //获取当前线程的 Looper,如果线程尚未启动或者已经dead,就返回 null
    //如果线程已经启动但Looper 还没初始化完成,这个方法会阻塞直到looper已经准备好,并返回Looper对象
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        synchronized (this) {
            while (isAlive() && mLooper == null) { //循环等待,直到mLooper != null
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

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

    public int getThreadId() {
        return mTid;
    }
}

实例讲解

这里只是为了实现方便,可能会存在内存泄露问题。

大致场景是:

1、主线程叫子线程去做事 (定义一个子线程的Handler,就是threadHandler啦,threadHandler.sendMessage)

2、子线程执行完毕,通知主线程 (threadhandler.handleMessage处理耗时任务)

3、主线程收到消息,更新UI或者其它的

public class HandlerThreadActivity extends AppCompatActivity {

    private Button downloadBtn;
    private TextView displayTv;
    private int count = 1;

    //UI线程的Handler,可以更新UI
    private Handler mUIHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Log.e("zhen", "主线程Handle收到任务 " + msg.what +
                    "的反馈,很满意! thread: " + Thread.currentThread().getId());
            displayTv.setText("主线程Handle收到消息 "+ msg.what);
            return true;
        }
    }) ;

    private HandlerThread mHandlerThread; //创建一个带Looper的线程
    private Handler mHandler; //以子线程的Looper为入参创建一个子线程的Handler


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

        initView();
        intHandler();
    }

    private void intHandler() {
        mHandlerThread = new HandlerThread("zhen");
        mHandlerThread.start(); //必须要HandlerThread.start之后才能创建Handler,不然Looper.mQueue为空

        mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.e("zhen","子线程里handle收到任务" + msg.what +
                        ",并开始处理 threadId: " + Thread.currentThread().getId());
                try {
                    int progress = 0;
                    do{
                        Thread.sleep(2000); //mHandler的handleMessage是在子线程中执行的
                        progress += 20;
                        Log.d("zhen","子线程工作进度" + progress + "% ....");
                    } while (progress < 100);
                    Log.e("zhen","子线程任务" + msg.what +
                            "执行完,通知UI线程 threadId: " + Thread.currentThread().getId());
                    mUIHandler.sendEmptyMessage(msg.what);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        };
    }


    private void initView() {
        downloadBtn = findViewById(R.id.downloadBtn);
        displayTv = findViewById(R.id.displayTv);

        downloadBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendEmptyMessage(count);
                Log.e("zhen","主线程给子线程handle发出任务" + count +
                        ",开始工作啦! threadId: " + Thread.currentThread().getId());
                count++;
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
    }
}

1、点击downloadBtn一次:

HandlerThread 使用详解(实例 + 源码)

2、点击downloadBtn两次:

是不是很类似IntentService的效果,在子线程中顺序执行(因为是一次执行完一个msg,再去执行下一个msg啦)

另外是不是感觉这日志有3D效果,看的眼睛疼。

HandlerThread 使用详解(实例 + 源码)