Android多线程开发之HandlerThread的使用

初次看到HandlerThread的名字,我们可能会联想到Handler和Thread这两个类,没错,它其实就是跟Handler和Thread有莫大的关系。HandlerThread继承自Thread,它本质上就是一个Thread,而且专门用来处理Handler的消息。

一、HandlerThread简介

看看官方对它的解释:

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()方法必须被调用。

在Android开发中,不熟悉多线程开发的人一想到要使用线程,可能就用new Thread(){…}.start()这样的方式。实质上在只有单个耗时任务时用这种方式是可以的,但若是有多个耗时任务要串行执行呢?那不得要多次创建多次销毁线程,这样导致的代价是很耗系统资源,容易存在性能问题。那么,怎么解决呢?

我们可以只创建一个工作线程,然后在里面循环处理耗时任务,创建过程如下:

Handler mHandler;
private void createWorkerThread() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
             mHandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    ......
                }
            };
            Looper.loop();
        }
    }.start();
}

在该工作线程中,
- 调用Looper.prepare()创建与当前线程绑定的Looper实例;
- 使用上面创建的Looper生成Handler实例;
- 调用Looper.loop()实现消息循环;

然后透过Looper的循环,在Handler的handlerMessage()中进行异步任务的循环处理。而这也正好是HandlerThread的实现。

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

那么我们看下HandlerThread有哪些特点:
- HandlerThread本质上是一个线程类,它继承了Thread
- HandlerThread有自己的内部Looper对象,通过Looper.loop()进行looper循环
- 通过获取HandlerThread的looper对象传递给Handler对象,然后在handleMessage()方法中执行异步任务
- 创建HandlerThread后必须调用HandlerThread.start()方法来启动线程

二、HandlerThread使用步骤

1、创建HandlerThread实例对象

HandlerThread handlerThread = new HandlerThread("Handler Thread");
//HandlerThread handlerThread = new HandlerThread("Handler Thread"Process.THREAD_PRIORITY_DEFAULT);

HandlerThread默认有两个构造函数,提供了线程名参数和线程优先级参数的设置。

2、启动HandlerThread线程

handlerThread.start();

通过start()方法就可以启动一个HandlerThread了,该线程会不断地循环运行。

3、通过Handler构建循环消息处理机制

Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //运行在工作线程(子线程)中,用于实现自己的消息处理
                return true;
            }
        });

通过将HandlerThread绑定的Looper对象传递给Handler作为参数,构建一个异步的Handler对象,为了能实现耗时任务的异步执行,我们重写了Handler的Callback接口的handleMessage()方法,当然也可以不重写该方法,而通过post()方法进行耗时任务操作。

Handler workderHandler = new Handler(handlerThread.getLooper());
workderHandler.post(new Runnable() {
    @Override
    public void run() {
        //运行在工作线程(子线程)中,用于实现自己的消息处理
    }
});

最后,我们就可以通过调用workerHandler以发送消息的形式发送耗时任务到工作线程HandlerThread中去执行,实际上就是在Handler.Callback里的handleMessage()中执行。

这里要注意,在创建Handler作为HandlerThread线程消息执行者的时候必须先调用start()方法,因为创建Handler所需要的Looper参数是从HandlerThread中获得的,而Looper对象的赋值又是在HandlerThread的run()方法中创建。

三、HandlerThread使用实例

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

/**
 * Created by Administrator on 2016/9/18.
 */
public class HandlerThreadActivity extends Activity implements Handler.Callback {
    private DBHandlerThread mDBHandlerThread;
    private Handler mUIHandler; //与UI线程相关联的Handler

    Button mBtnQuery;
    TextView mTextResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnQuery = (Button) findViewById(R.id.buttonQuery);
        mTextResult = (TextView) findViewById(R.id.result);
        mBtnQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //将异步耗时任务发送到HandlerThread中
                //Message msg = Message.obtain(null, DBHandlerThread.MSG_QUERY_FRIENDS);
                //mDBHandlerThread.getWorkerHandler().sendMessage(msg);
                mDBHandlerThread.queryFriends();
            }
        });
        mUIHandler = new Handler(this);
        initWorkerThread();
    }

    protected void initWorkerThread() {
        mDBHandlerThread = new DBHandlerThread("Handler Thread");
        mDBHandlerThread.setUIHandlerCallBack(mUIHandler);
        mDBHandlerThread.start(); //start()后会执行Thread的run()方法
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDBHandlerThread.setUIHandlerCallBack(null);
        mDBHandlerThread.quit();
        mDBHandlerThread = null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DBHandlerThread.MSG_QUERY_FRIENDS:
                // update UI
                break;
        }
        return false;
    }
}
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

/**
 * Created by Administrator on 2016/9/18.
 */
public class DBHandlerThread extends HandlerThread implements Handler.Callback {
    public static final int MSG_QUERY_FRIENDS = 100;

    private Handler mWorkerHandler; //与工作线程相关联的Handler
    private Handler mUIHandler; //与UI线程相关联的Handler

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

    public DBHandlerThread(String name, int priority) {
        super(name, priority);
    }

    public void setUIHandlerCallBack(Handler handler) {
        this.mUIHandler = handler;
    }

    public Handler getWorkerHandler() {
        return mWorkerHandler;
    }

    @Override
    protected void onLooperPrepared() {
        mWorkerHandler = new Handler(getLooper(), this);
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_QUERY_FRIENDS:
                //...查询数据库操作...
                Message message = Message.obtain(null, MSG_QUERY_FRIENDS);
                mUIHandler.sendMessage(message); //通知UI更新
                break;
        }
        return true;
    }

    public void queryFriends() {
        Message msg = Message.obtain(null, MSG_QUERY_FRIENDS);
        mWorkerHandler.sendMessage(msg);
    }
}

四、HandlerThread源码解析

HandlerThread的源码不多,先看下它的构造函数:

/**
 * 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.
 */
public class HandlerThread extends Thread {
    int mPriority; //线程优先级
    int mTid = -1; //当前线程id
    //当前线程持有的Looper对象
    Looper mLooper;

    public HandlerThread(String name) {
        //调用父类默认的方法创建线程
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
   //带优先级参数的构造方法
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    ...............
}

从代码中得知,HandlerThread带有两个构造函数,可传递两个参数,一个参数是name,指的是线程的名称,另一个参数是priority,指的是线程优先级。线程的优先级的取值范围为-20到19。优先级高的获得的CPU资源更多,反之则越少。-20代表优先级最高,19最低。我们可以根据自己的需要去设置线程的优先级,也可以采用默认的优先级,HandlerThread的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为0。该优先级是再run()方法中设置的,我们看它的run()方法:

public class HandlerThread extends Thread {
  /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid(); //获得当前线程的id
        Looper.prepare(); //准备循环条件
        //通过锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            //唤醒等待线程
            notifyAll();
        }
        //设置当前线程的优先级
        Process.setThreadPriority(mPriority);
        //在线程循环之前做一些准备工作(子类可实现也可不实现)
        onLooperPrepared();
        //启动loop
        Looper.loop();
        mTid = -1;
    }
}

run()方法主要是通过Looper.prepare()和Looper.loop()构造了一个循环线程。这里要注意,在创建HandlerThread对象后必须调用其start()方法才能进行run()方法体的执行。

在Looper.prepare()执行后,Looper对象会被创建,然后通过同步锁机制,将Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程。接着为线程赋予优先级,然后执行onLooperPrepared()方法,该方法是一个空实现,留给我们必要的时候去重写的,主要用来做一些初始化工作。最后通过执行Looper.loop()在线程中启动消息队列。

我们看到在run()方法中进行了唤醒等待线程,为什么要这么做呢?答案就在getLooper()方法中:

/**
 * This method returns the Looper associated with this thread. If this thread not been started
 * or for any reason is isAlive() returns false, this method will return null. If this thread 
 * has been started, this method will block until the looper has been initialized.  
 * @return The looper.
 */
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(); //等待,直到另一个线程调用notify()或notifyAll()来唤醒它
           } catch (InterruptedException e) {
           }
       }
   }
   return mLooper;
}

该方法用来获取当前子线程HandlerThread所关联的Looper对象实例。首先判断HandlerThread线程是否存活,如果没有存活就直接返回null,否则继续执行,进入同步块并判断Looper对象是否为空以及线程是否启动,若都满足,则调用wait()方法进入阻塞阶段,直到Looper对象被成功创建并且通过notifyAll()方法唤醒该等待线程,最后才返回该Looper对象。

Looper对象的创建是在run()方法进行的,也即在子线程中执行的,而getLooper()方法是在UI线程中调用的,若不使用等待唤醒机制,我们就无法保证在UI线程中调用getLooper()方法时Looper对象已经被创建,会面临一个同步的问题,所以HandlerThread就通过等待唤醒机制来解决该同步问题

HandlerThread既然是一个循环线程,那么怎么退出呢?有两种方式,分别是不安全的退出方法quit()和安全的退出方法quitSafely():

/**
 * Quits the handler thread's looper.
 * <p>
 * Causes the handler thread's looper to terminate without processing any
 * more messages in the message queue.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p class="note">
 * Using this method may be unsafe because some messages may not be delivered
 * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 * that all pending work is completed in an orderly manner.
 * </p>
 *
 * @return True if the looper has been asked to quit or false if the
 * thread had not yet started running.
 *
 * @see #quitSafely
 */
public boolean quit() {  
   Looper looper = getLooper();  
   if (looper != null) {  
       looper.quit();  
       return true;  
   }  
   return false;  
}  
/**
 * Quits the handler thread's looper safely.
 * <p>
 * Causes the handler thread's looper to terminate as soon as all remaining messages
 * in the message queue that are already due to be delivered have been handled.
 * Pending delayed messages with due times in the future will not be delivered.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p>
 * If the thread has not been started or has finished (that is if
 * {@link #getLooper} returns null), then false is returned.
 * Otherwise the looper is asked to quit and true is returned.
 * </p>
 *
 * @return True if the looper has been asked to quit or false if the
 * thread had not yet started running.
 */
public boolean quitSafely() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quitSafely();
        return true;
    }
    return false;
}

通过查看开发者文档知道,quit()方法在API Level 5时被添加,而quitSafely()方法在API Level 18时才被添加。

quit()方法,主要是把MessageQueue中所有的消息全部清空,无论是延迟消息还是非延迟消息

quitSafely()方法只会清空MessageQueue中所有的延迟消息,并将所有的非延迟消息继续分发出去,最后等到Handler处理完后才停止Looper循环

这里的延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的消息。

五、HandlerThread应用场景

HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作数据库、文件等,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

这里单线程+异步队列模型的使用场景还举几个例子:
1. 应用刚启动时的数据初始化操作,如果开启多个线程同时执行,有可能争夺UI线程的CPU执行时间,造成卡顿,而使用该模型,通过设置优先级就可以将同步工作顺序的执行,而又不影响UI的初始化;

2.从数据库中读取数据展现在ListView中,通过Handler的postAtFrontOfQueue方法,快速将读取操作加入队列前端执行,必要时返回给主线程更新UI;

3.HandlerThread应用在IntentService中,IntentService是一个需要长时间执行任务Service,优先级要比线程高。

发布了31 篇原创文章 · 获赞 55 · 访问量 12万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览