声明:本文是一篇对Handler相关内容的整理(经过相当一段时间,几次内容增减),有相当部分内容来源网络,其中融入部分作者本身的理解,并加以整理。如有涉及到哪位老师的原作,在此深表感谢!
目录
目录
Handler + Looper + Message:生产者 + 消费者 + 仓库(任务队列)
5.Message(任务,产品)(Intent, Runnable, Message)
Handler + Looper + Message:生产者 + 消费者 + 仓库(任务队列)
作者浅见。
Handler和Looper是多对一的关系,Looper和MessageQueue是一对一的关系。一个线程中,只有一个Looper 和 MessageQueue,但可以有多个Handler。Handler发送消息时,本身作为关联的target存入消息,Looper获取消息时,最终调用消息关联的Handler进行处理。
1.Handler(生产者add)
Handler:线程间通讯机制,是由Looper和MessageQueue来构建消息机制的。Handler发送消息给MessageQueue,并处理Looper分发过来的消息。
线程间通讯:
Handler在某线程把Message或者Runnable发送到目标线程的MessageQueue,然后由目标线程Looper对MessageQueue循环调度,再召唤发送消息的Handler进行处理。这个时候,处理过程:
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
将在Looper所属线程(目标线程)执行,即完成线程切换。
1.1.Handler创建对象
每个Handler对象都会绑定一个Looper对象,每个Looper对象对应一个消息队列(MessageQueue)。如果在创建Handler时不指定与其绑定的Looper对象,系统默认会将当前线程的Looper绑定到该Handler上。
在主线程中(系统会为主线程自动创建Looper对象,开启消息循环)可以直接使用new Handler() 创建Handler对象,自动与主线程的Looper对象绑定。
new Handler() 等价于new Handler(Looper.myLooper())。Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
在非主线程中直接这样创建Handler则会报错,因为Android系统默认情况下非主线程中没有开启Looper,而Handler对象必须绑定Looper对象。这种情况下,需先在该线程中手动开启Looper(Looper.prepare()-->Looper.loop()),然后将其绑定到Handler对象上;或者通过Looper.getMainLooper(),获得主线程的Looper,将其绑定到此Handler对象上。
创建Handler对象,还可以通过传入Callback接口类型方式创建。
public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
public boolean handleMessage(Message msg);
}
1.2.Handler发送消息
Handler发送的消息都会加入到Looper的MessageQueue中。Handler只有一个消息队列,即MessageQueue。
关于:
handler.post(new Runnable() {
@Override
public void run() {
}
});
通过post()传进去的Runnable对象将会被封装成消息对象后传入MessageQueue;使用Handler.sendMessage()将消息对象直接加入到消息队列中。
使用post()将Runnable对象放到消息队列中后,当Looper轮循到该Runnable执行时,实际上并不会单独开启一个新线程,而仍然在当前Looper绑定的线程中执行,Looper只是调用了该线程对象的run()而已。如,在子线程中定义了更新UI的指令,若直接开启将该线程执行,则会报错;而通过post()将其加入到主线程的Looper中并执行,就可以实现UI的更新。
使用sendMessage()将消息对象加入到消息队列后,当Looper轮询到该消息时,就会调用Handler的handleMessage()来对其进行处理。再以更新UI为例,使用这种方法的话,就先将主线程的Looper绑定在Handler对象上,重载handleMessage()来处理UI更新,然后向其发送消息就可以了。
如果Handler对象与其调用者在同一线程中,如果在Handler的消息处理方法中设置了延时操作,则调用线程也会堵塞,因为Looper轮循是线性的,所以Handler处理Message的过程也是线性的。
1.3.Handler处理消息
Handler 在处理消息时,会有三种情况:
if :msg.callback 不为空
这在使用 Handler.postXXX(Runnable) 发送消息的时候会发生,直接调用 Runnable 的 run() 方法。
else if :mCallback 不为空
如果构造Handler时候以 Handler.Callback 为参数构造 Handler 时会发生,CallBack接口方法作为消息处理方法替代Handler.handleMessage() 执行处理操作。
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
return false;
}
});
else :最后就调用 Handler.handleMessage() 方法
需要我们在 Handler 子类里重写
1.4.Handler移除消息
public final void removeCallbacks(Runnable r){
mQueue.removeMessages(this, r, null);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
2.HandlerThread
2.1.定义:
HandlerThread 是一个包含 Looper 的 Thread,我们可以直接使用这个 Looper 创建 Handler。避免在子线程中对Looper手动繁琐的操作(Looper.prepare() ——> Looper.loop()),让我们可以直接在线程中使用 Handler 来处理异步任务。
HandlerThread 本身是一个Thread,需要start()启动。
HandlerThread 在run() 方法中为本线程创建了Looper(Looper.prepare()),调用 onLooperPrepared 后开启了循环(Looper.loop())
HandlerThread 需要在子类中重写 onLooperPrepared,做Looper启动前的初始化工作
HandlerThread 可以指定优先级,注意这里的参数是 Process.XXX 而不是 Thread.XXX
HandlerThread = Thread + Looper,适合在子线程中执行耗时的、可能有多个任务的操作的场景,比如说多个网络请求操作,或者多文件 I/O 等等。
2.2.适用场景:
为某个任务 / 回调单独开启线程,并提供由Handler + Looper提供任务(Message)调度机制。
个人理解:HandlerThread是一个可以通过Looper + Message 实现在子线程(Looper 和 MessageQueue在子线程,所以Looper循环消息,触发消息处理方法也是在子线程)中依次循环执行任务的线程类。
2.3.源码:
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;
}
/**
* 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 HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* 子类需要重写的方法,在这里做一些执行前的初始化工作
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
//调用 start() 后就会执行的 run()
@Override
public void run() {
mTid = Process.myTid();
//创建了 Looepr
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//Looper 已经创建,唤醒阻塞在获取 Looper 的线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();//开始循环
mTid = -1;
}
/**
* 获取当前线程的 Looper
* 如果线程不是正常运行的就返回 null
* 如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason 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();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* @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.
* @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;
}
}
2.4.DEMO:
使用 HandlerThread 实现子线程完成多个下载任务。
DownloadThread,它有两个 Handler 类型的成员变量,一个是用于在子线程传递、执行任务,另一个用于外部传入,在主线程显示下载状态:
public class DownloadThread extends HandlerThread implements Handler.Callback {
private final String KEY_URL = "url";
public static final int TYPE_START = 1;
public static final int TYPE_FINISHED = 2;
/**
* 外部传入,通知主线程显示下载状态
*/
private Handler mUIHandler;
/**
* 内部创建,子线程传递、执行任务
*/
private Handler mWorkerHandler;
/**
* download list
*/
private List<String> mDownloadUrlList;
public DownloadThread(final String name) {
super(name);
}
/**
* 执行初始化任务
*/
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mWorkerHandler = new Handler(getLooper(), this);
if (mUIHandler == null) {
throw new IllegalArgumentException("No UIHandler!");
}
// 将接收到的任务消息挨个添加到消息队列中
for (String url : mDownloadUrlList) {
Message message = mWorkerHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(KEY_URL, url);
message.setData(bundle);
mWorkerHandler.sendMessage(message);
}
}
public void setDownloadUrls(String... urls) {
mDownloadUrlList = Arrays.asList(urls);
}
/**
* 获取主线程 Handler
*/
public Handler getUIHandler() {
return mUIHandler;
}
/**
* 注入主线程 Handler
*/
public DownloadThread setUIHandler(final Handler UIHandler) {
mUIHandler = UIHandler;
return this;
}
/**
* 子线程中执行任务,完成后发送消息到主线程
*/
@Override
public boolean handleMessage(final Message msg) {
if (msg == null || msg.getData() == null) {
return false;
}
String url = (String) msg.getData().get(KEY_URL);
//