您的位置:首页 > 移动开发 > Android开发

Android 并发二三事之 Handler 机制的妙用 HandlerThread

2016-11-17 11:25 525 查看
Android 并发第五篇

本篇会讲解如何利用 HandlerThread 实现异步操作。

HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。

对于提交的任务(或者说是信息 Message)依次处理。

所以在介绍 HandlerThread 原理以及如果使用之前,会首先说一个 Handler 异步机制。

当然 Handler, Looper, Message 之间的关系相信很多人都已经很熟悉了,这里会只着重介绍和本节相关的内容。

一 、Handler 机制:

1、 我们都知道,在子线程中通知主线程更新UI界面,需要使用Handler。

一般我们就直接在 Activity 中直接 初始化一个Handler 对象,像这样:

Handler uiHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};


重写 handlerMessage() 方法,然后利用 uiHandler 对象在子线程中发送消息。

2、 或者我们也可以直接在主线程直接 new 一个 Handler 对象:

Handler handler = new Handler();


但在子线程中 new Handler()需要这样:

Handler handler = new Handler(Context.getMainLooper());


然后在子线程中:

handler.post(new Runnable() {
@Override
public void run() {
//执行在主线程中
Log.d(TAG, "run on UI  Thread Id : "+Thread.currentThread().getId());
}
});


Handler 源码:

public final boolean post(Runnable r)
{
return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}


在handler 中会将Runnable 对象赋值给 message.callback 属性,封装成Message,调用 sendMessageDelaye() 将消息发送出去。

sendMessageDelaye() 方法最后在辗转几次后最终会调用sendMessageAtTime() 将消息放到消息队列中。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}


而 Handler.sendMessage()的源码为:

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}


所以本质上,无论是利用Handler.sendMessage(),还是 Handler.post() 都是将消息添加到消息队列之中。

那么为什么在子线程中需要传入 MainLooper , 而主线程却不需要呢?

首先我们是要在子线程中通知主线程,那么我们便需要代码执行在UI 线程中。

如果在子线程中直接:

Handler handler = new Handler();


会抛出异常:

Can’t create handler inside thread that has not called Looper.prepare()

我们可以看一下源码:

Handler 源码:

public Handler() {
this(null, false);
}

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}


我们能够看到,无参的构造方法,会调用public Handler(Callback callback, boolean async) 。

在这个方法中,调用 Looper.myLooper(); 获取 Looper 对象,之所以抛出异常,一定是其为null了。

那么为什么没有获取到Looper对象呢?

接下来看Looper源码:

public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}


从 ThreadLocal 变量中获取当前线程的值,那么这个值是在哪里设置的呢?

public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}


在Looper.prepare() 中设置的。

也就是说在子线程中直接 new Handler() 对象,需要先调用Looper.prepare() 方法。

而在主线程中是不需要的,因为在应用初始化时,已经调用 Looper.prepare() 了。

而Looper 中还有一个方法:Looper.loop() 方法

Looper.loop() 内包含一个死循环,不断的从队列中获取消息,如果没有消息时,会阻塞。

Looper.loop() 调用了 Handler.dispatchMessage() 方法:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

private static void handleCallback(Message message) {
message.callback.run();
}


在dispatchMessage() 方法中会调用 handleMessage() 方法,或者调用 handleCallback() 方法处理我们利用Handler post Runnable

所封装的消息。

3 、所以通过以上总结

我们知道如果Looper.loop()是在主线程中调用的,那么我们重写的 handlerMessage() 方法

和封装在消息中的 Runnable 都会在主线程中执行。

反过来说,如果Looper.prepare() 以及 Looper.loop() 是在子线程中调用的,那么基于子线程的Looper,所创建的Handler

所发送的消息都将会执行在子线程中,HandlerThread 便是利用了这个原理。

二 、HandlerThread

1 、我们首先看一下 HandlerThread 如何使用:

private void requestWithHandlerThread() {
//初始化一个 HandlerThread 对象
HandlerThread handlerThread = new HandlerThread("HandlerThread");
//调用start() 方法
handlerThread.start();
Log.d(TAG, "Main : "+Thread.currentThread().getId());
Log.d(TAG, "HandlerThread : "+handlerThread.getId());
//初始化一个Handler 对象,利用 HandlerThread 中的 Looper 对象
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行在子线程中
Log.d(TAG,"Thread : "+Thread.currentThread().getId() +"   "+msg.obj);

}
};
Message message = Message.obtain();
message.obj = "From Message";
handler.sendMessage(message);
handler.post(new Runnable() {
@Override
public void run() {
//执行在子线程中
Log.d(TAG, "post : "+Thread.currentThread().getId());
}
});
}


2 、结果:

11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: Main : 1
11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: HandlerThread : 26599
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: Thread : 26599   From Message
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: post : 26599


在这里 HandlerThread 需要和 Handler 一起配合使用,HandlerThread 提供一个在子线程中创建的 Looper 。

按照之前的推论,Looper.prepare(), 以及 Looper.loop() 都是执行在子线程中,那么在处理消息时也必然执行在子线程中。

所以其实现了异步的效果。

3 、接下来看一下 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;
}

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


4 、总结

可以看到 HandlerThread 本身是一个 Thread 所以其 run() 方法会执行在子线程中。在 run() 方法中首先调用了Looper.prepare(),

用于初始化消息队列以及Looper对象,紧接着调用 Looper.loop() 开始从消息队列中轮询,一旦有消息便将消息取出处理。

因为整个过程都执行在子线程中,所以当我们用在子线程中创建的Looper作为参数传给Handler时,其处理消息的代码就会执行在子线程中了。

以上便是 HandlerThread 的原理,主要还是利用 Handler,Message, Looper 之间的关系。

三 、自定义 HandlerThread:

当我们了解了其原理之后,其实我们也可以自定义自己 HandlerThread , 在线程之中处理消息。

现在我们自定义一个MyHandlerThread 同样继承 Thread。

1 、代码如下:

public class MyHandlerThread extends Thread {

private Handler asyncHandler;
private Looper mLooper;
public MyHandlerThread() {

}

@Override
public void run() {

Looper.prepare();
mLooper = Looper.myLooper();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
asyncHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
handlerThreadMessage(msg);
}
};
Looper.loop();

}

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

//退出
public void quit() {
Looper looper = getLooper();
if(looper != null) {
looper.quit();
}
}

/**
* 发送消息
* @param message
*/
public void sendMessage(Message message) {
if(asyncHandler != null) {
Log.d("test","sendMessage");
asyncHandler.sendMessage(message);
}
}

/**
* 处理消息
* @param message
*/
private void handlerThreadMessage(Message message) {
Log.d("test","Message : "+message.obj+" Thread " +Thread.currentThread().getId());
}
}


2、用法也很简单:

定义变量:

private MyHandlerThread handlerThread;


在onCreate() 中初始化:

handlerThread = new MyHandlerThread();
handlerThread.start();


在需要异步时调用:

private void updateData() {
Message message = Message.obtain();
message.obj = "更新数据";
handlerThread.sendMessage(message);
}


这样我们便实现自定义 HandlerThread ,其中我们还可以根据需求封装不同发送消息的方法。

并且我们还将提交任务的代码和在子线程中处理任务的代码分开了,两块代码利用 MessageQueue 相连接,

那么这是不是也算是一种生产者消费者模式呢? 因为Handler 机制本身也算是一种生产者消费者模式啊。

四、

下一篇会讲解 Android 中另外一个可以实现异步的类: IntentService 。

IntentService 本身当然是一个 Service , 但是它可以做到完成任务后自动退出,下一篇一起看看其是怎么做到的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐