深入了解android中的消息机制Handler
什么是Handler?
handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制.
我们可以使用它发送消息,也可以通过它处理消息.
我们为什么要使用Handler?
Android在设计的时候,就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制,就没有办法更新UI,而且还会抛出异常信息.
例如:大家都知道,更新UI的操作一般都是放在main线程中,当我们需要在子线程中更新UI时,就需要使用到了Handler,虽然在子线程更新Ui的方法有好几种,但内部实现原理基本都是通过Handler发送消息处理的,不要着急,下面会提到.
Handler的使用:
sendMessage()方法的使用:
package com.hnthgys.mytext;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tv;
//创建main线程的Handler
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText("我是通过handler发送消息更新的");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textview);
//开启子线程
new Thread(){
@Override
public void run() {
super.run();
//例如此处我们正在执行一个耗时操作,执行完毕后发送消息更新ui
//发送一个空消息
handler.sendEmptyMessage(0);
//如果此处我们需要使用执行完耗时操作的数据,可以这样写
//Message msg = handler.obtainMessage();
//msg.obj = "数据";
//handler.sendMessage(msg);
}
}.start();
}
}
sendEmptyMessage(); 此方法和sendMessage使用一致,区别就是发送一个空消息.
sendMessageDelayed(); 发送一个延时执行的消息
post(Runnable);该方法可以在子线程中更新UI,该方法运行在main线程中
removeCallbacksAndMessages();移除回调和消息;例如:我们在使Handler轮播一些图片时,想让它停止轮播,就可以使用这个方法.android为什么要设计只能通过handler来更新UI呢?
最根本的的目的就是解决多线程并发问题.假设如果在一个activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题呢? 更新界面错误.
你可能会说,我可以使用加锁的多线程啊,如果对更新UI的操作都进行加锁处理的话,应用程序的性能会大大下降.
处于对以上问题的考虑,Android给我们提供了一套更新UI的机制,我们只需要遵循这样的机制就可以了.
根本不用关心多线程的问题,所以更新UI的操作,都是在主线程的消息队列当中去轮询处理的.
Handler的原理是什么呢?
一,Handler封装了消息的发送,(主要包括消息发送给谁)
Looper(Handler内部自己的Looper)
1,内部包含一个消息队列,也就是MessageQueue,所有的Handler发送消息
都走向这个消息队列.
2,Looper.loop()方法,就是一个死循环,不断的从MessageQueue中取消息,
如果有消息就处理消息,没有消息就阻塞.
二,MessageQueue,就是一个消息队列,可以添加消息,并处理消息.
三,Handler,内部会跟Lopper进行关联,也就是说在Handler的
内部可以找到Looper,找到了Lopper也就找到 了MessageQueue,
在Handler中发送消息,其实就是向MessageQueue队列中发送消息.
Handler原理总结:
Handler负责发送消息,Loooper负责接收Handler发送的消息,
并直接把消息回传给Handler自己.
MessageQueue就是一个存储消息的容器.
Handler使用中遇到的问题:
在非UI线程中更新UI,抛出的异常:
在子线程创建Handler,抛出的异常:
注意:当需要在子线程中创建Handler时,需要先创建一个Looper,因为子线程中没有Looper对象
HandlerThread又是什么?
当我们向创建一个与线程相关的Handler时,我们可以使用HandlerThread,来解决多线程的并发问题.
子线程与主线程如何互发消息:
主线程Handler向子线程发送消息(伪代码)

Android在子线程中更新UI的几种方式:
使用图片吧,以前做的笔记,看着感觉更加清晰..
非UI线程真的不能更新UI吗?
答案是能,对.你没有看错,非UI线程也能更新UI.可能你会觉得我在扯淡,下面看一段代码:
package com.hnthgys.mytext;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tv;
//创建main线程的Handler
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textview);
//开启子线程
new Thread(){
@Override
public void run() {
super.run();
//SystemClock.sleep(100);
tv.setText("我是在子线程中更新的UI");
}
}.start();
}
}
我把布局代码也贴出来,
执行效果图:
可能看到这,你已经目瞪口呆了,这怎么可能,fuck,这完全颠覆了啊…..
主要原因:
当我们在更新UI时,Android中的ViewRootImpl类中的checkThread()方法会检查当前更新UI所在的线程,如图
3937 void More ...checkThread() {
//检查执行更新UI所在的线程
3938 if (mThread != Thread.currentThread()) {
//如果不在UI线程,就会抛出下面的异常,大家应该很眼熟吧
3939 throw new CalledFromWrongThreadException(
3940 "Only the original thread that created a view hierarchy can touch its views.");
3941 }
3942 }
查看系统源码后,你会发现,ViewRootImpl类会在 Activity的onResume()方法执行完成后才初始化,这也就解释了上面代码能运行的原因了,但是,你发现没有,我们在子线程中没有做任何的耗时操作,如果我在子线程中添加这句代码:
SystemClock.sleep(100);
那么系统将会抛出异常:”Only the original thread that created a view hierarchy can touch its views.”
不能在非UI线程中更新UI.
那么问题来了,如果ViewRootImpl类没有初始化完成,那么view视图是如何显示出来的呢???我也正在解决中…….
另外,当我们在子线程中获取到ViewRoot,我们可以调用addView()方法在子线程中更新UI,这其中的详情就靠大家去探索了…..