一.什么是Handler
Handler是Android给我们提供用来更新UI的一套机制,是一套消息处理机制,可以通过它来发送消息和处理消息。那作为开发者的我们,不禁会疑问?Google为什么要设计这套机制呢?这是为了解决在非UI线程中更新UI组件比较麻烦的问题。那么Android为什么不能在非UI线程中更新呢?首先Android的UI控件不是线程安全的,这是因为避免多线程并发所带来不安全问题。例如作一个假设,现在在子线程中刷新界面,同时也在UI线程中刷新界面,就会出现刷新不同步,简单来讲通过Handler就可以更新UI操作切换到主线程中执行。
二.Handler机制
Handler机制主要角色
Message:消息,就是一个载体,包含消息ID,消息处理对象和处理的数据等,统一放到MessageQueue,最终由Handler处理。
Handler:用于同一个进程的线程间通信,消息处理者,专门负责Message的发送和处理。我们使用Handler时,一般通过handleMessage(Message msg)来处理Message,也就是统一处理消息的回调,确保自己发出的消息也是自己来处理。
MessageQueue:由名字可推出这是队列,就是存放Handler发送过来的消息,按照先进先出的顺序规则来执行。将链表的数据结构以Message来串联起来,等待Looper的抽取,为什么需要队列呢?因为同一线程在一个时间只能处理一个消息。因此需要队列来保存这些消息,然后挨个挨个拿出来处理,创建一个线程时并不会自动创建MessageQueue,但是主线程创建时会默认创建Looper对象,而Looper创建时就会创建MessageQueue,其他非主线程需要looper的时候就会通过调用prepare函数来实现。
Looper:首先要理解一个线程是一段可执行的代码,作为App的主线程,不能让代码执行完,因为代码执行完的话app就会自动退出,因此不能让主线程不能让代码段执行,只能在代码中插入一个死循环,这时候Looper的作用就体现出来了,将主线程变成Looper线程。并且这时主线程就会在等其他线程发消息(更新UI和Activity状态等等)那,另外,Looper不断从消息队列拿出消息给主线程,也就是无限循环去查找是否有消息,有就去处理,没有的话就一直等待,一个MessageQueue需要一个Looper。
Thread:负责调度整个消息的循环。
通过上面可以得知:Handler的作用是发送和处理消息,Handler发送的消息必须被送到指定MessageQueue中,也就是说,要让Handler正常工作就必须有一个MessageQueue,但是MessageQueue是由Looper来抽取自身的Message,也因此当前线程中必须有一个Looper对象。
图解:
Handler深度理解
根据上面可得:每一个线程都必须有Looper,但是我们不禁会有疑问?主线程我们并没有创建Looper?其实主线程是ActivityThread,ActivityThread被创建的时候就会初始化Looper。
上面红色箭头就是主线程创建Looper对象代码语句。那么我们继续进入Looper去看看究竟是什么操作。
果然上面所分析的几个主要角色都在:Looper,MessageQueue,Thread,但是还多了一个ThreadLocal,这个是什么呢?其实这个是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定的线程获取到存储数据,对于其他线程来说则无法获取到数据,后面例子再详细叙述。
这时候又有疑问,为什么主线程中有一个死循环,但是没有造成阻塞呢?那我们从ActivityThread类开始分析:这个并不是一个真正的线程,就只是一个类,从main方法看到有Looper,那找找其对应的Handler,ActivityThread有一个内部类,继承Handler,并找到handleMessage
仔细观察:Activity的生命周期都有对应的case条件,并且service的生命周期等等都有条件。也就是通过loop来分发消息内部类的Handler,就会进入handleMessage。就会根据对应的条件来执行对应的代码,不断执行传递过来的消息。那么可以总结来说:ActivityThread的main方法主要是来做消息循环的,如果消息循环退出了,那么程序就退出了。但是从队列里取出消息可能会造成阻塞,如果某个消息处理的时间过于长,那么会影响UI线程的刷新,就造成卡顿现象。不会造成卡在的真正原因主要有二:
1.当没有消息来的时候就会wait,有句柄写的时候就会唤醒,进行等待。
2.所有的UI更新操作都通过handler来发消息。如各种的点击事件都会造成句柄写的操作来唤醒等待。
主线程是UI线程,优先级很高,主线程的死循环会不会特别消耗CPU资源呢?
其实如果主线程的队列里没有消息时,就会阻塞在loop的queue.next()里,这时候主线程就会释放CPU资源进入休眠状态,直到下个消息进来时就会唤醒主线程,现在是通过pipe管道来写入数据,类似I/0,不占用资源。
三.例子实践
下面简单写一个小例子加深理解:
倒计时例子:
-
public class MainActivity extends Activity {
-
private TextView text_one;
-
private mHandler mhandler = new mHandler(this);
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
text_one = findViewById(R.id.text_one);
-
start();
-
}
-
//Handler静态内部类 防止内存泄漏
-
private static class mHandler extends Handler{
-
WeakReference<MainActivity> weakReference;
-
public mHandler(MainActivity mainActivity){
-
weakReference = new WeakReference<MainActivity>(mainActivity);
-
}
-
@Override
-
public void handleMessage(Message msg){
-
MainActivity mainActivity = weakReference.get();
-
if(mainActivity != null){
-
mainActivity.text_one.setText(String.valueOf(msg.arg1));
-
}
-
}
-
}
-
private void start(){
-
new Thread(new Runnable() {
-
@Override
-
public void run() {
-
for(int i = 10;i>0;i--){
-
Message msg = new Message();
-
msg.what = 01;
-
msg.arg1 = i;
-
mhandler.sendMessage(msg);
-
try {
-
Thread.sleep(1000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
//计时结束后先不管
-
}
-
}).start();
-
}
-
@Override
-
protected void onDestroy(){
-
super.onDestroy();
-
if(mhandler != null){
-
mhandler.removeCallbacksAndMessages(null);
-
}
-
}