Handler定义
Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.
Handler提供了一种线程间通讯的机制,子线程可以用Handler向目标线程的消息队列发送Message或者Runnable。
Handler的使用情景:子线程希望在另外一条线程中完成一些处理,于是可以在目标线程中实现一个Handler,子线程通过这个handler发送Message通知它执行相应的操作。
使用Handler
Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) method
Handler提供了几个方法用于发送消息:sendMessage—— 发送Message对象,post——发送Runnable(其实最终Runnbale也会被作为message的callback封装到Message中)。Message会在handleMessage中被处理,而Runnable会被直接运行它的run()。
一个典型的例子:通过子线程修改应用的UI
子线程直接修改应用UI的错误示范:
public class MainActivity extends Activity implements OnClickListener{
TextView tvTest;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTest = (TextView) findViewById(R.id.tv_test);
tvTest.setOnClickListener(this);
}
@Override
public void onClick(View v) {
new WorkerThread().start();
}
class WorkerThread extends Thread{
@Override
public void run() {
tvTest.setText("from thread:" + getId()); //错误的做法,线程中直接更新textview
}
}
}
当WorkerThread试图要更改TextView的内容时,程序就会抛出异常:
11-15 03:09:21.881: E/AndroidRuntime(1661): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
11-15 03:09:21.881: E/AndroidRuntime(1661): at android.view.ViewRoot.checkThread(ViewRoot.java:2932)
11-15 03:09:21.881: E/AndroidRuntime(1661): at android.view.ViewRoot.requestLayout(ViewRoot.java:629)
11-15 03:09:21.881: E/AndroidRuntime(1661): at android.view.View.requestLayout(View.java:8267)
11-15 03:09:21.881: E/AndroidRuntime(1661): at android.view.View.requestLayout(View.java:8267)
异常原因是主线程non trhead-safe,UI的更新必须在主线程实现,而且android设置了检查机制来阻止我们从别的线程更新UI,如LOG中的checkThread方法。
解决办法:子线程通过主线程的Handler发送更新请求
public class MainActivity extends Activity implements OnClickListener{
TextView tvTest;
Handler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTest = (TextView) findViewById(R.id.tv_test);
tvTest.setOnClickListener(this);
mHandler = new Handler(){ //在主线程创建Handler实例
@Override
public void handleMessage(Message msg) { //消息的处理
tvTest.setText("update view");
}
};
} class WorkerThread extends Thread{
@Override
public void run() {
mHandler.sendEmptyMessage(0); //向handler发送一个消息
}
}
@Override
public void onClick(View v) {
new WorkerThread().start(); //开启线程
}
}
线程的消息传递是如何工作的?
上面的例子因为handler是在主线程中,主线程被创建的时候会默默地绑定一个looper,因此我们不需要去操作Looper,但是如果handler在子线程中被创建,那么我们就需要手动让Looper启动起来。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); // 线程绑定looper,如果当前线程没有looper,这里会自动new一个
mHandler = new Handler() {
public void handleMessage(Message msg) { //实现handleMessage,写入自己的处理
// process incoming messages here
}
};
Looper.loop(); //让Looper循环获取Message
}
}
总结一下操作的步骤:
1.在线程中绑定一个Looper:Looper.prepare();
2.创建一个class继承Handler并实现handleMessage方法;
3.启动Looper,开始从MessageQueue循环获取并分发接收到的Message:Looper.loop()。
Handler 的作用只有2个:向消息队列发送消息 和 处理分发到的Message,实现线程的通信还涉及几个关键的类: Looper, Message 和 MessageQueue
Message
就是消息的封装类,它有几个public字段用来标记和存放数据: what(int,用于识别,相当于id), arg1, arg2(2个用来存放数据的int) 和 obj(存放object),当然还可以用setData()存放一个bundle(开销会比较大)。
还有2个内部成员可以了解一下:
callback—— 存放Runnable
target—— 目标线程的Handler
MessageQueue
存放Message的队列。
Looper
MessageQueue和Handler之间的齿轮,负责获取Message并分发给Handler。
重点突出一下Looper,消息在队列和handler之间的传递就是looper 的loop()方法实现的,这是一个死循环,不停地查询MessageQueue获取仍未处理的Message,通知handler的dispatchMessage(),附上源码:
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
}
第一篇帖子。。 逻辑还很混乱。。希望大神们给点建议~