Android的消息机制就是Handler的运行机制。
先研究明白以下3个问题,有助于对Handler有一个深入的认识。
1.为什么会有Handler呢?
最主要是因为Android不允许非主线程对UI进行操作,子线程去访问网络做一些耗时运算后得到了我们想要的数据,但是尴尬的事情发生了子线程此时手握数据但是它没有权限去改UI。这时Handler伸出了天使的翅膀…Handler从子线程手中接过了数据去把数据给了主线程然后主线程就更新了UI。
题外话
ViewRootImpl的checkThread方法对UI操作做了线程的验证如果不是主线程就是报错“Only the original thread that created a view hierarchy can touch its views”中文翻译就是“越俎代庖”
2.所以Handler是用来干什么的呢?
Handler在Android系统中的作用非常关键,它可以解决子线程不能更新UI的矛盾,同时可以对消息进行发送和处理。
这就好比只有皇上才能发圣旨,但是皇上不会去做具体的事,原因大家都懂,皇上管的是江山社稷,如果他都去做具体的事了,估计国家基本也要完蛋了。所以这些事只能由各个大臣去实施,大臣们在各自州府县衙办事过程中获得了新的事件信息,这些信息想要让天下人都知道怎么办,又不能发圣旨昭告天下,这事只有皇上才能干啊。这时驿站出现了,官老爷们把消息给了驿站,驿站收到信件奏折,快马加鞭把消息传到了宫城内的皇帝手中,皇帝大笔一挥将信息昭告天下。所以脑洞告诉我Handler可以理解为是一个邮驿系统
3.为什么Android不允许非主线程处理UI呢?
因为Android的UI控件不是线程安全的,如果多线程并发访问可能会大致UI控件处于不可预期的状态,你可能想到可以用锁解决,但是用锁首先就是访问UI的逻辑会变得很复杂,其次因为锁会阻塞某些线程的执行而导致降低UI的访问效率。所以最简单且高效的方法就是采用单线程模型来处理UI操作。(该句话出自Android开发艺术探索)这样我们也就根本不用去关心多线程的问题了。另外,开什么玩笑,人人都能发圣旨,那不天下大乱了!
需要注意的是,不允许子线程操作UI并不是不能,如果作死了非要在子线程操作UI也是可以的。因为取决于能不能的关键是ViewRoot在不在该线程中。说一下,更新界面的操作会调用一个invalidate()方法这个方法会去checkThread,而invalidate()就是ViewRootImpl调用的,ViewRootImpl是在Activity的onResume时候通过window getDecorView 后addView时被创建的。所以在onResume前做就不会报错了。这点不多说了,知道子线程也是可以更新UI的就行,别人问子线程能不能更新UI,你就说实际上是能的就OK,别的不用知道了,开发中正常人都不会这么做。
如何使用Handler
在创建Handler的时候,它会跟一个默认的线程进行绑定,这个线程中有一个MessageQueue(消息队列),MessageQueue只是一个消息的存储单元,并不能处理消息,而Looper就是用来做这个的。Looper一直循环的去查是否有新消息,如果有就处理。线程默认是没有Looper的,这需要去创建(用Looper.prepare(),同时在消息相关代码最后调用Looper.loop()才可以进行轮询)。但在UI线程中因为它是ActivityThread,ActivityThread创建的时候就初始化了Looper,所以我们在主线程中可以直接使用Handler.
Handler handler=new Handler(){
public void handlerMessage(Message msg){
mas.what…//发过来的Message在这里被接收到了 可以获取msg的信息
}
}
主要方法有:
Handler.post(Runnable) 直接运行在UI线程中
Handler.postDelayed(Runnable,long) long代表每隔多少时间去执行这个方法
Handler.sendMessage(Message)
Handler.senMessageDelayed
Handler.removeCallbacks(Runnable); 将该消息在消息列表中移除。
其中所传送的Message以下主要属性
Message.arg (int)可以存放整型数据
Message.obj (Object)存放Object
Message.what (int) 指定自定义消息代码,用于接收时候区分
Message.sendToTarget(); 将消息发送给自己,也就是发送给当前的Handler。可以替用Handler.sendMessage();等发送消息的方法。
如果有些情况下没有必去要创建一个Message时,也可以复用系统的Message对象:handler.obtainMessage();会返回一个Message对象。
我们发送消息的话肯定要指定一个消息发送的地址,要知道消息是发送到哪里的,obtainMessage()方法中实际上也就是把消息发送给这个Hnadler自己。方法内部是Message.target来实现的而这个target就是当前的Handler。
Message.sendToTarget()里面实际上也是调用了 target.sendMessage();上面说了target就是Handler自己,所以本质上Message.sendToTarget和Handler.sendMessage();方法是一样的。
Handler原理
MessageQueue
就是消息队列,其中的消息可以添加和删除。Handler发送的消息都汇总到这里来。
Looper
Looper的内部包含了MessageQueue消息队列,所以Looper可以对MessageQueue进行肆无忌惮的轮询,Looper.Loop方法已经启动就开启死循环对MessageQueue进行轮询,如果有消息就处理,没有的话就阻塞。
创建Looper的时候就new了MessageQueue,并把当前的线程给确定了。
Looper(boolean){
MessageQueue mQueue=new MessageQueue(boolean);
…….
mThread = Thread.currentThread();
}
Handler
handler内部与Looper关联,然后Looper内部有MessageQueue,Handler发送的消息都在MessageQueue里
简单说就是handler负责发送消息,Looper负责接收消息并把消息传回给handler自己。
要注意的是如果实在主线程中创建的Handler的话,在handlerMessage方法中不要去写太耗时的方法,否则会出现卡UI的现象。
如果一定要用handler处理耗时操作的话也可以用 HandlerThread,HandlerThread创建了Looper然后关联默认的Handler,解决了多线程并发问题,避免绑定指定looper时候会出现的空指针问题。这样就完全不占用主线程资源
HandlerThread thread=new HandlerThread(“xxxxxthread”);
thread.start();
handler=new Hanlder(thread.getLooper()){
public void handlerMessage(Message msg){
//耗时操作在子线程中执行 操作数据库 下载资源什么的
}
}
handler.sendMessage……..;//主线程发送消息就行
关于更新UI
除了直接利用handler外runOnUiThread();和view本身的post方法也可以更新UI
private void xxxxUpdateUI(){
runOnUiThread(new Runnable){
public void run(){
//更新UI的方法
}
}
}
handler的post方法以及runOnUiThread()以及view的post方法内部实际上都是封装好的handler发送message的一套方式,其实本质上都是通过handler机制来做的。