子线程没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:CalledFromWrongThreadException为了实现子线程中操作UI界面,Android中引入了Handler消息传递机制。
什么是Handler?
handler通俗一点讲就是用来在各个线程之间发送数据的处理对象。在任何线程中,只要获得了另一个线程的handler,则可以通过 handler.sendMessage(message)方法向那个线程发送数据。基于这个机制,我们在处理多线程的时候可以新建一个thread,这个thread拥有UI线程中的一个handler。当thread处理完一些耗时的操作后通过传递过来的handler向UI线程发送数据,由UI线程去更新界面。
handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
(1)安排Message或Runnable 在某个主线程中某个地方执行;(2)安排一个动作在不同的线程中执行。
Handler的一种经典的使用方法,也是更新UI最常用的方法。
代码:
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView txt;
private static final int WORKTHREAD = 1;
private Handler mainThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == WORKTHREAD) {
final String data = (String) msg.obj;
txt.setText(data);
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.btn);
txt = (TextView) findViewById(R.id.txt);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//开启一个工作线程
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = WORKTHREAD;
message.obj = "来自子线程数据!";
mainThreadHandler.sendMessage(message);
}
}).start();
}
});
}
}
在平时的开发中有时会碰到主线程子线程发送消息,这时候来考虑这个问题。
代码:
public class MainActivity extends AppCompatActivity {
private TextView txt;
private Button btn_main_start;
Handler workHandler;
private static final int MAIN_THREAD = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intiViews();
//开启子线程接收主线程数据
final WorkThread workThread = new WorkThread();
workThread.start();
btn_main_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//主线程发送数据
Message message = Message.obtain();
message.what = MAIN_THREAD;
message.obj = "来自主线程数据!";
workHandler.sendMessage(message);
}
});
}
private void intiViews() {
txt = (TextView) findViewById(R.id.txt);
btn_main_start = (Button) findViewById(R.id.btn_main_start);
}
class WorkThread extends Thread {
public void run() {
Looper.prepare();
workHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MAIN_THREAD) {
final String data = (String) msg.obj;
txt.setText(data);
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
}
};
Looper.loop();
}
}
}
(1)为什么主线程向子线程发送消息时要调用手动下列代码:
Looper.prepare();
workHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//do something
}
};
Looper.loop();
(2)为什么new Handler()构造方法中没有使用当前线程的Looper而是使用了主线程Looper?
如果子线程涉及到更新UI的操作需要使用主线程的Looper,如果没有涉及到跟新UI的操作而只是简单的处理消息可以使用当前线程的Looper.
为了对Handler作进一步的了解,接下来我们分析一下源码。
先来看一下Handler的构造方法:
Public constructors |
|
---|---|
Handler() Default constructor associates this handler with the |
|
Handler(Handler.Callback callback) Constructor associates this handler with the |
|
Handler(Looper looper) Use the provided |
|
Handler(Looper looper, Handler.Callback callback) Use the provided |
我们打开源码就会发现Handler(),Handler(Handler.Callback callback)以及Handler(boolean async)最终都会调用下面这个构造方法:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
//如果Handler为匿名内部类、局部内部类或者非静态成员内部类(即除了静态内部类外的所有内部类)立即警告“潜在的内存泄漏”
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,默认使用当前线程的Looper
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;
}
这个构造方法中做了两件事: ①检测“潜在的内存泄漏”(与该博文无太多关系,如果想了解更多请看:
Handler引发内存泄漏)②关联当前线程的Looper.
再看其他两个构造方法:Handler(Looper looper)和Handler(Looper looper, Callback callback)。这两个构造方法最终调用的是下面的构造方法:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从以上两段代码可以看出:Handler构造方法需要先关联一个Looper,
接下来再来探一探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);
}
到这里就算是结束了,它首先获取当前Looper所关联的MessageQueue,然后将Message存储到相应的MessageQueue()中。关于enqueueMessaage()方法参见前篇博客: MessageQueue工作原理。
Handler发送消息已经清楚了,接下来再来看看Handler如何处理消息。
在使用Handler的时候我们每次都要调用(主线程除外)Looper.loop()方法,这个方法作用:当MQ中有消息要执行时调用该条Message所属Handler的dispatchMessage(msg)方法处理消息(详细见:Looper工作原理),ok,我们打开dispatchMessage(msg)源码:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
该方法中首先判断Message是否存在其接口CallBack,如果有就调用handleCallback(msg),否则判断时候Handler接口Callback是否为null,如果不为null就调用CallBack.handleMessage(msg)方法,否则调用Handler的handleMessage(
msg)方法。而
CallBack.handleMessage(msg)方法和Handler的handleMessage(msg)方法都需要开发者自己手动处理。