知识点
HandlerThread,顾名思义,Handler+Thread,就是让子线程也能有一套和MainThread一样的Handler消息机制。
- HandlerThread本质上是一个线程类,它继承了Thread
- HandlerThread有自己的内部Looper对象,可以进行looper循环
- 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
- 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象
MainThreadHandler的消息传递机制
HandlerThread的消息传递机制
- 用户启动一个应用,系统内部建立一个进程。
- 进程启动主线程Main Thread。
- Main Thread通过Looper建立一个消息队列Message Queue。
- 消息队列是存在于主线程中的,在主线程中开始无限循环。
- 每当有新的Message进来,消息队列就开始处理,如果没有,就执行return继续等待。
区别:
- 创建实例对象HandlerThread
- 启动HandlerThread的主线程Thread()
- 创建一个Handler,该Handler使用的是HandlerThread的looper,并传入一个自定义的的消息处理机制来处理
- 处理完毕调用MainThread创建的另外一个Handler去处理结果
- 【HandlerThread(子线程也可以有消息传递机制)】平时使用Handler的时候系统内部已经帮我们创建好主线程和启动主线程了,而使用HandlerThread需要我们手动去创建一个属于HandlerThread的主线程并且去启动它
- 使用Handler的时候使用的是HandlerThread的looper而不是MainThread的looper
- 不使用handleMessage来处理,而是使用一个实现了Handler.Callback接口的自定义的处理方法
- HandlerThread不能操作UI,所以处理完毕后还是需要使用主线程的Handler去处理UI
如图所示:
文章图片
一个点击下载图片的例子
文章图片
package com.bourne.android_common.ServiceDemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import com.bourne.android_common.R;
import com.bourne.common_library.utils.Logout;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class HandlerThreadActivity extends AppCompatActivity {
class ImageBean {
private String url;
private Bitmap bitmap;
public String getUrl() {
return url;
}public void setUrl(String url) {
this.url = url;
}public Bitmap getBitmap() {
return bitmap;
}public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}/**
* 图片地址集合
*/
private String url[] = {
"http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg",
"http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg",
"http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg",
"http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg"
};
private ImageView imageView;
private HandlerThread handlerThread;
private Thread loadImageThread;
private int count = 0;
/**
* 处理UI
*/
Handler mainThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Logout.e("次数:" + msg.what);
ImageBean imageBean = (ImageBean) msg.obj;
imageView.setImageBitmap(imageBean.getBitmap());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
imageView = (ImageView) findViewById(R.id.imageView);
createHandlerThread();
}/**
* 通过HandlerThread的方式加载
* @param view
*/
public void loadByHandlerThread(View view) {
Handler.Callback callBack = new loadImageCallBack();
Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), callBack);
for (int i = 0;
i < url.length;
i++) {
handlerThreadHandler.sendEmptyMessageDelayed(i, 1000 * i);
}
}/**
* 通过Thread的方式加载
* @param view
*/
public void loadByThread(View view) {
if (loadImageThread != null && !loadImageThread.isInterrupted()) {
loadImageThread.interrupt();
}
count = 0;
loadImageThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
Thread.sleep(1000);
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[count]);
ImageBean imageBean = new ImageBean();
imageBean.setBitmap(bitmap);
imageBean.setUrl(url[count]);
Message message = new Message();
message.what = count;
message.obj = imageBean;
count++;
mainThreadHandler.sendMessage(message);
//最后一张时停止加载
if (count >= url.length) {
loadImageThread.interrupt();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Logout.e("加载完毕,停止线程");
}
}
});
loadImageThread.start();
}/**
* 处理下载图片
*/
class loadImageCallBack implements Handler.Callback {@Override
public boolean handleMessage(Message msg) {
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[msg.what]);
ImageBean imageBean = new ImageBean();
imageBean.setBitmap(bitmap);
imageBean.setUrl(url[msg.what]);
Message message = new Message();
message.what = msg.what;
message.obj = imageBean;
mainThreadHandler.sendMessage(message);
return false;
}
}/**
* 创建一个HandlerThread
*/private void createHandlerThread() {
//创建实例对象
handlerThread = new HandlerThread("downloadImage");
handlerThread.start();
}/**
* 下载图片的网络请求
*
* @param urlString
* @return
*/
private Bitmap downloadUrlBitmap(String urlString) {
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
Bitmap bitmap = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
bitmap = BitmapFactory.decodeStream(in);
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
}@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
handlerThread.quit();
}
}
可以看到我们在onCreate的时候创建了一个HandlerThread,并开启它的线程。点击按钮的时候创建一个Handler,取HandlerThread的Looper,并完成线程的操作。操作完成之后,发消息给UI线程并改变UI。
布局文件
源码解析
完整代码:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
/**
*
*/
public class HandlerThread extends Thread {
/**
* 线程优先级
*/
int mPriority;
int mTid = -1;
/**
* 当前线程持有的Looper对象
*/
Looper mLooper;
public MyHandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public MyHandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}/**
*必要时可以自己去重写
*/
protected void onLooperPrepared() {
}@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
//Looper对象将被创建
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
//设置进程优先级
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开启looper循环语句
Looper.loop();
mTid = -1;
}/**
* 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;
}synchronized (this) {
while (isAlive() && mLooper == null) {
try {
//等待唤醒
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}/**
* Quits the handler thread's looper.
* * Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
*
* 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.
*
* 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.
*
*
* @return True if the looper 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.
* * 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.
*
* 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.
*
* 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.
*
*
* @return True if the looper 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;
}/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
/**
* 线程优先级
*/
int mPriority;
int mTid = -1;
/**
* 当前线程持有的Looper对象
*/
Looper mLooper;
public MyHandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public MyHandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}/**
*必要时可以自己去重写
*/
protected void onLooperPrepared() {
}
这里初始化了Handler的对象,设置了优先级,mLooper是持有的Looper对象,onLooperPrepared是一个空实现,必要是可以自己重写。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
//Looper对象将被创建
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
//设置进程优先级
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开启looper循环语句
Looper.loop();
mTid = -1;
}
run()方法中调用了Looper.prepare(),Loop.loop(),prepare()负责创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。
loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
/**
* 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;
}synchronized (this) {
while (isAlive() && mLooper == null) {
try {
//等待唤醒
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
Looper的创建是在子线程中执行的,而Handler通过getLooper去获取mLooper的时候又是在主线程,怎么保证它们可以同步呢,原因是通过唤醒机制:
getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果启动了会判断Looper对象有没有被创建,如果都都没有满足则会继续等待,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,返回mLooper对象给Handler使用。
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;
}
??从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
??
??当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
??
参考文章
- Android 多线程之HandlerThread 完全详解
- Android HandlerThread 完全解析
推荐阅读
- 多渠道|Android 组件化在公用Module里实现多渠道打包配置
- Android基础|EventBus源码分析之订阅-发布模型
- Android基础|Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)
- android ViewId自动注解使用详解(ViewInject)
- Android|Android属性动画 Property animation
- JNI与底层调用-1
- #|FutureTask 使用场景介绍
- #|静态内部类创建单例的实现和优点
- #|View的三大流程是什么,加以简单说明