转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/76083113
本文出自:【顾林海的博客】
前言
我们知道在应用启动时会开启一个主线程,也就是UI线程,主线程主要管理与用户交互的UI控件(UI展示,事件分发),如果在主线程中执行耗时操作会触发ANR(Application not responding)异常,意思是程序未响应,这时就需要开启子线程来执行耗时操作,这时又会引发到子线程更新UI的问题,这里要记住Android中主线程是不安全的,为了解决子线程能更新UI状态,因此在Android平台中引入了一个叫做Handler机制,可以通过在子线程通过Handler发送消息给主线程来进行UI的更新,并且在子线程中可以进行耗时操作。总的来说Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。
Handler的使用方法
1. 通过post(runnable)方法进行延迟消息的处理。
2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。
首先我们演示第一种方式,通过post(runnable)方法来进行消息的处理,具体代码如下:
package com.glh.intentservice;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView mInfoTextView;
private Button mDownloadButton;
private static Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initView() {
mInfoTextView = (TextView) findViewById(R.id.tv_info);
mDownloadButton = (Button) findViewById(R.id.btn_download);
}
private void initEvent() {
mDownloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mInfoTextView.setText("下载中...");
new DownloadThread(0).start();
}
});
}
class DownloadThread extends Thread {
private int mProcess = 0;
DownloadThread(int process) {
mProcess = process;
}
@Override
public void run() {
while (mProcess < 100) {
mProcess++;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Runnable runnable = new Runnable() {
@Override
public void run() {
mInfoTextView.setText("下载成功");
}
};
mHandler.post(runnable);
}
}
}
分析上面代码,我们在创建Handler时,是在主线程中创建的,也就是说这个Handler绑定在主线程中,代码中,创建继承自Thread类的DownloadThread的线程类,并在run方法中执行了一个耗时操作,这里可以看出执行完耗时操作后,创建了一个Runnable对象,并在Runnable中的run方法中进行UI的更新,最后通过Handler的post方法进行消息的处理。
运行效果:
最后看看sendMessage(message)方法使用,代码如下:
package com.glh.intentservice;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity {
private TextView mInfoTextView;
private Button mDownloadButton;
private MyHandler mHandler = new MyHandler(this);
static class MyHandler extends Handler {
private WeakReference<MainActivity> weakReference;
MyHandler(MainActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = weakReference.get();
if (null != activity) {
int what = msg.what;
switch (what) {
case 1:
activity.downloadSuccess((String) msg.obj);
break;
default:
break;
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initView() {
mInfoTextView = (TextView) findViewById(R.id.tv_info);
mDownloadButton = (Button) findViewById(R.id.btn_download);
}
private void initEvent() {
mDownloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mInfoTextView.setText("下载中...");
new DownloadThread(0).start();
}
});
}
private void downloadSuccess(String info) {
mInfoTextView.setText(info);
}
class DownloadThread extends Thread {
private int mProcess = 0;
DownloadThread(int process) {
mProcess = process;
}
@Override
public void run() {
while (mProcess < 100) {
mProcess++;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 1;
message.obj = "下载成功";
mHandler.sendMessage(message);
}
}
}
在代码中创建一个静态内部类MyHandler,为什么要创建静态内部类呢?这是因为,非静态内部类会持有外部类Activity的匿名引用,如果在子线程执行耗时操作,在耗时操作时,Activity退出,通过Handler发送消息,这时由于Activity不存在会引起Activity的内存泄露,因此这里使用静态内部类,并使用WeakReference弱引用。
代码中创建了一个线程,并执行了耗时操作,耗时操作完毕后,创建了Message对象,并给Message的what设置1,obj设置”下载成功”,这里的what属性用于在主线程中Handler的handleMessage方法中进行不同Message的处理,obj属性用于携带相关数据。最后通过Handler的sendMessage(message)方法发送消息给主线程,主线程的Handler的handleMessage方法中通过msg.what识别哪个message,并进行相应的处理。
运行效果:
Handler机制
在讲解Handler机制时,先来了解一下Looper、MessageQueue、Message所代表的含义。Handler主要包含两种作用,分别是发送消息到消息队列和处理消息;Looper用于为当前线程生成一个消息队列,并执行一个循环,不停的从消息队列中获取Message消息;MessageQueue就是一个先进先出的消息队列,提供了next方法用于获取Message消息,以及enqueueMessage方法将消息添加到消息队列中;Message代表消息。
在Android中新开启的线程是没有开启消息循环的(主线程除外),如果在子线程中开启消息循环就需要这样做:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
在run方法中主要三件事:
- 通过Looper.prepare实例化Looper对象,并生成一个消息队列。
- 创建Handler,并与Looper相关联。
- 通过Looper.loop开启消息循环,并处理消息。
先查看Looper,prepare()方法:
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));
}
在prepare方法中sThreadLocal是ThreadLocal对象,在创建Handler时通过sThreadLocal获取当前线程的Looper,这个在后面会提到,上面代码中会先判断sThreadLocal是否为null,如果不为null,就抛出异常,说明Looper.prepare()方法不能执行两次以上,也就是说一个线程中只有一个Looper实例,接着看Looper的构造器。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper构造器中创建了MessageQueue对象,mThread是指当前的线程。到这里Looper.prepare()方法执行完毕,总结Looper.prepare方法做了如下工作:
- 一个线程只能有一个Looper实例。
- 创建Looper时,会关联一个MessageQueue。
接着看Looper.loop()方法:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (; ; ) {
//没有消息时阻塞
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
try {
//通过Handler方法消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//消息的回收
msg.recycleUnchecked();
}
}
上面的代码进行了局部精简,在loop()方法中,先是通过myLooper()方法获取当前线程的Looper对象,在myLooper()方法中通过sThreadLocal.get()方法获取Looper对象,这里的sThreadLocal在讲解Looper.prepare()方法时已经讲过了。接着获取当前Looper的MessageQueue消息队列,并通过一个无限循环遍历消息队列,当消息队列中没有消息时处于阻塞状态,当消息队列中有消息时,取出消息,通过msg.target的dispatchMessage方法发送消息,这里的msg.target就是Handler,最后进行消息的回收。总结Looper.loop()方法做了如下工作:
- 获取当前线程Looper对象。
- 从当前线程中的Looper对象中获取消息队列。
- 开启无限循环,遍历消息队列。
- 在循环中通过msg.target进行消息的发送。
最后看看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;
}
mLooper是之前在Loop.prepare()方法执行时关联的Looper对象,这这段代码中会去判断mLooper是否空,如果为空抛出异常,说明创建Handler之前需要通过Looper.prepare()方法创建当前线程的Looper对象并创建一个MessageQueue消息队列,这样的话Handler才能与MessageQueue进行关联,这里面mQueue就是Looper创建的MessageQueue对象。
Handler创建完毕后就需要我们进行发送消息,这里主要分析前面两种Handler使用方法:
1. 通过post(runnable)方法进行延迟消息的处理。
2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。
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;
}
通过post(runnable)方法进行消息的处理时,Handler内部会调用sendMessageDelayed方法,而sendMessageDelayed方法的第一个参数就是Message对象,通过getPostMessage(runnable)方法进行包装,在getPostMessage方法中获取Message对象,并将Runnable对象赋值给Message的callback属性。
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
一直往下追溯,最终调用的是enqueueMessage方法,在enqueueMessage方法中,将当前的Handler赋值给Message的target属性,这里面的msg.target是不是很熟悉,在之前Looper.loop()方法中进行消息队列的遍历,最终就是通过它来发送消息的。这里的queue就是与当前线程关联的Looper对象的MessageQueue,执行到最后通过MessageQueue的enqueueMessage方法将Message放入消息队列中。
接着查看sendMessage(message)方法的实现原理:
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
查看到这里,大家就会察觉到,这里与上面通过post(runnable)方法的实现逻辑是一样,都是通过MessageQueue的enqueueMessage方法将Message放入消息队列中。当消息放入消息队列MessageQueue后,是如何获取Message并进行处理的呢?这里又回到了上面Looper.loop()方法中的无限循环遍历消息队列中的msg.target的dispatchMessage方法,从上面存放消息队列的分析中,我们已经知道了msg.target就是一个Handler,因此,我们查看Handler中的dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里面的callback就是Message中的Runnable对象,我们在上面讲解Handler的使用方法中讲的第一种方法,通过post(runnable)方法,这里面的runnable在post(runnable)源码中封装Message时,已经赋值给了Message的callback属性,回到上面的代码中,第一步判断Message的callback属性是否为空,不为空执行handleCallback(msg)方法,如果为空,判断当前Handler的mCallback属性是否为空,如果不为空,执行Callback 的handleMessage的方法,如果Handler的mCallback属性为空或者是Callback的handleMessage方法返回false,执行Handler中的方法handleMessage(msg),这里面的mCallback是一个内部Callback接口:
public interface Callback {
public boolean handleMessage(Message msg);
}
handleMessage方法是不是很熟悉,没错,它是在创建Handler时,如果实现了handleMessage方法,也就是说交由Handler来处理消息时,会执行这个方法。如果只是new Handler(),那么这个mCallback为空。
如果通过post(runnable)方法处理消息,这时会执行handleCallback(msg)方法:
private static void handleCallback(Message message) {
message.callback.run();
}
很简单,就是调用Runnable的run()方法,准确来说,post(runnable)并没有开启线程,只是纯粹的调用Runnable的run方法,这也说明了为什么通过Handler可以更新UI。
到此Handler机制原理解析完毕。