什么是 Handler
handler 是通过发送和处理 Message 和 Runnable 对象来关联相对应线程的 MessageQueue。
每个 Android 应用在启动的时候,都会创建一个线程,这个线程成为主线程或者 UI 线程,Android 应用的所有操作默认都会在这个线程中执行。为了保证 UI 的流畅性,通常会将耗时操作(IO操作,网络请求等)放到子线程,如果在子线程中,我们又需要更新UI怎么办呢?Handler 的作用就显现出来了,虽然,它不仅仅是这样的作用。
A Handler allows you to send and process Message and Runnable
objects associated with(与...相联系/对应) a thread's MessageQueue.
Each Handler instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to(绑定到) the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue.
1、可以让对应的 Message 和 Runnable 在未来的某个时间点进行相应处理
2、让自己想要处理的耗时操作放在子线程,让更新 ui 的操作放在主线程
Handler 使用##
post(runnable)###
方法定义
// Causes the Runnable r to be added to the message queue.
// The runnable will be run on the thread to which this handler is attached.
// 如果 Looper 所在线程的消息队列(MessageQueue)已经退出,那么发送会失败
public final boolean post (Runnable r)
{
// 内部其实调用的也是 sendMessage 方法
return sendMessageDelayed(getPostMessage(r), 0);
}
// 将 runnable 作为 Message 的 callback 返回,无需我们再手动处理 handlMessage()
private static Message getPostMessage(Runnable r) {
// 从全局池中返回新的消息实例。允许我们在许多情况下避免分配新对象。
Message m = Message.obtain();
m.callback = r;
return m;
}
简单使用
public class HandlerWithPostActivity extends AppCompatActivity {
private final String TAG = getLocalClassName();
// 创建 Handler,用于处理消息和发送消息
private Handler mHandler = new Handler();
private TextView mTextView;
private void downLoad() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 模拟下载任务
Log.e(TAG, "down start");
try {
// 模拟耗时操作
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 模拟下载完成
Log.e(TAG, "downLoad end");
Runnable runnable = new Runnable() {
@Override
public void run() {
// 下载完成,更新 UI
HandlerWithPostActivity.this.mTextView.setText(R.string.text_down_load_success);
}
};
// 调用 post 方法,将 runnable 发送给在 UI 线程的 mHandler 进行处理,达到在 UI 线程更新 UI 的目的
mHandler.post(runnable);
}
});
thread.start();
}
}
sendMessage()###
public class HandlerWithMessageActivity extends AppCompatActivity {
private static final int WHAT_MESSAGE = 1;
private TextView mTextView;
// 正式环境不要这样操作,会引发内存泄漏,因为匿名内部类持有了 Activity 的引用
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息
switch (msg.what) {
case 1:
mTextView.setText("延迟5秒显示消息");
break;
default:
break;
}
}
};
private void showMessageDelay() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取消息,发送消息
Message message = Message.obtain();
message.what = WHAT_MESSAGE;
mHandler.sendMessage(message);
// 如果只是发送一个 what 的话,可以直接使用 sendMessage(WHAT_MESSAGE)
}
}).start();
}
}
Handler 机制原理##
Message####
创建一个可以发送给 Handler ,包含描述信息和任意对象数据的 Message,Message 默认 包含两个 int 类型实例变量 (arg0,arg1
)和一个任意类型对象变量 obj
,使得我们大部分情况下,无需额外创建变量。
官方推荐的获取 Message 对象的方式是使用:Message.obtain()
,或者是 Handler.obtainMessage()
,这两中方式取得的对象都是从 缓存池 中拿的,避免创建新对象。
源码阅读####
以 Handler 的构造方法为入口
/*
* Set this flag to true to detect anonymous, local or member classes
* that extend this Handler class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
public Handler(Callback callback, boolean async) {
// 如果我们实现的 Handler 是匿名类、成员类、局部类并且不是静态类,系统会提示我们可能会产生内存泄露,正常情况下
// 我们使用 Android Studio 编写出前面 SendMessage 方式的那种 Handler 的时候,就会亮黄线给予警告。
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());
}
}
// 初始化 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 初始化 MessageQueue
mQueue = mLooper.mQueue;
// 赋值包含 handleMessage()方法的接口的成员变量一个实现类对象
mCallback = callback;
mAsynchronous = async;
}
在 Looper 中是如何提供给 Handler Looper 对象和 MessageQueue对象的呢?
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
// 获取 Looper
return sThreadLocal.get();
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 设置 Looper
sThreadLocal.set(new Looper(quitAllowed));
}
从上面的代码我们知道了 Looper是如何创建并和当前线程产生关联的,从文档中得知,Looper的主要工作依赖于 loop()方法,那么 loop()方法如何工作呢?
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
// 轮询消息队列,调用 Handler 的dispatchMessage()分发消息
for (;;) {
Message msg = queue.next();
...
try {
// 分发消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
确认一下 Message.target 是否是 Handler
public final class Message implements Parcelable
{
Handler target;
Runnable callback;
}
继续跟踪 dispatchMessage(msg) 方法进行了什么操作
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
// 从上面我们知道 callback 就是一个 runnbale,那么这里就是对 runnable 进行处理
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
// @return True if no further handling is desired
// handleMessage()方法返回true 表明没有消息处理了
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleCallback(msg) 方法特别简单,就是执行了 Runnable 的 run() 方法。到这里,post(runnable)类型的消息已经被Looper 从 MessageQueue 中拿出来分发给了 Handler 并且已经处理完成。
private static void handleCallback(Message message) {
message.callback.run();
}
再看看 handleMessage(msg)方法,这就是一个空方法,我们在编写自己的 Handler 对象的时候通常都会实现一个匿名内部类,再在里面实现 handleMessage(msg)
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
到这里,post(Runnable),sendMessage(Message) 两种类型的消息都已经处理完毕了。
我们换个方向,从 sendMessage(Message msg) 方法这里进去看看消息发送是怎样的流程
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
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);
}
继续跟下去,看看 enqueueMessage()方法中都做了什么
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 这里设置了 Message 的 target 是当前 Handler
// 在后续Looper通过 loop()方法轮询 MessageQueue的时候,就是通过调用Handler 的 dispatchMessage(msg)方法
// 又将消息处理带回到了 Handler 中来。
// msg.target.dispatchMessage(msg);
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在 enqueueMessage()方法中,将 Message 存储到了 MessageQueue中,等待被Looper 轮询。
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
return true;
}
到此,整个 Handler 流程已经清晰明朗了。简单总结下:
- 我们创建 Handler 的时候,Handler 就在内部完成了 Looper 和 MessageQueue 的创建工作和初始化
- Handler 发送消息就是将消息放入了 MessageQueue 中,同时设置 target 为Handler
- Looper 则通过loop()方法轮询消息,然后通过 前面的 target 方法调用 Handler 的 dispatchMessage 方法分发消息
- 在dispatchMessage 方法中,如果是 Runnable类型的消息就直接 run(),如果是 Message 类型的消息,就调用我们自定义的 handleMessage() 方法
UI 线程和 Handler####
在 Android 应用启动时,会默认有一个主线程(UI线程) , 在这个线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程处理。为了保证主线程不退出,会将获取消息的操作放进一个死循环中,这样主线程就相当于一直在执行死循环,因此不会退出。
UI 线程的消息循环是在 ActivityThread.main() 方法中创建的,源码如下,略有删减:
public static void main(String[] args) {
...
// 创建消息循环的 Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
// UI 线程的 Handler
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 执行消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
执行完 ActivityThread.main()方法以后,程序就启动了并且会一直从消息队列中取消息,然后处理消息,使得系统运转起来,UI 线程中,将消息投递到消息队列和从消息队列中获取消息处理的依然是 Handler。
从前面的源码分析中我们知道,每一个 Handler 都有一个消息队列MessageQueue,而消息队列是封装在 Looper 中的,而Looper 又会关联一个线程(通过 ThreadLocal封装),最终就等于一个 Handler 关联一个线程。
默认情况下,消息队列只有一个,就是主线程消息队列,这个是在 ActivityThread.main()方法中通过 Looper.prepareMainLooper() 创建的(最终会调用 Looper()构造方法,里面有 mQueue = new MessageQueue(quitAllowed);)。这样,消息队列就和线程关联上了,所以不同线程,不能访问对方的消息队列。
所以说,为什么我们要更新UI,必须在 UI 线程创建 Handler ,因为 Handler 关联了消息队列,消息队列是在Looper 中创建,Looper 是关联具体的线程的。要使HandleMessage方法可以更新UI,就必须在UI线程创建 Handler ,才能访问到UI线程的消息队列和Looper,才能正常的发送消息和处理消息。
子线程中创建 Handler 为何会抛出异常####
如果我们在子线程中创建Handler 那么会遇到下面代码注释中的问题,为什么呢?
public void show() {
new Thread(new Runnable() {
Handler handler = null;
@Override
public void run() {
// Looper.prepare();
// "Can't create handler inside thread that has not called Looper.prepare()
handler = new Handler();
// Looper.loop();
}
}).start();
}
通过前面的分析我们知道,Handler 在创建的时候,会关联一个消息队列,用于给消息队发送消息,消息队列又是封装在 Looper 中的,所以我们需要一个 Looper,Looper 获取是通过 Looper.myLooper()方法获取的,这个方法的内部是 ThreadLocal.get(),那么这个Looper 是什么时候设置进 ThreadLocal 的呢?就是在 Looper.prepare() 方法中,在这里构建了一个新的Looper,创建了Looper 的消息队列,然后设置到了 ThreadLocal 中。
所以,我们要在子线程中正常的创建 Handler ,必须要在 创建 Handler 之前,调用 Looper .prepare()方法,准备好Looper才不会报错
如果我们有了Looper ,发送消息就可以了吗?不行,还需要调用 Looper.loop()方法需遍历消息,否则,我们的消息只是发送到消息队列中,没有进行任何处理是没有什么效果的。
为什么我们在主线程中创建 Handler 的时候,不需要写 Looper.prepare()/Looper.loop() 呢?从前面的分析我们已经知道了,这两个步骤已经在程序启动的时候,在 ActivityThread.main()方法中做好了,我们需要做的就是创建我们自己的 Handler 绑定上去。
Handler优化##
因为如果是直接在主线程创建成员变量 Handler mHandler = new Handler(){...}这样的话,Handler 是持有外部 Activity 的引用的,如果在某一个时候,Activity 被销毁了,但是 Handler 对应的 MessageQueue 中还有待处理的方法,那么就会引起内存泄露,为了避免这个问题,我们可以采用三个步骤:
1、用static声明handler,静态类不会引用外部类
2、如果Handler中必须用到Activity,那就用WeakReference去引用
3、在Activity结束或暂停的事件中,removeMessages或者removeCallbacksAndMessages将消息队列中的消息移除(避免满足上面两条后,当Activity关闭了,但是Handler还未处理到,造成内存泄露)
示例代码:
private Handler mHandler = new MessageHandler(this);
// 创建一个静态内部类,使用若引用持有外部 Activity
private static final class MessageHandler extends Handler {
private WeakReference<? extends AppCompatActivity> mReference;
public MessageHandler(AppCompatActivity activity) {
super();
mReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerWithMessageActivity activity = (HandlerWithMessageActivity) mReference.get();
switch (msg.what) {
case 1:
activity.updateTextView("更新 TextView 文字");
break;
default:
break;
}
}
}
...
Message message = Message.obtain();
message.what = WHAT_MESSAGE;
mHandler.sendMessage(message);
...
@Override
protected void onPause() {
super.onPause();
mHandler.removeCallbacksAndMessages(null);
}
网友评论