Android的消息机制Handler

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机制来做的。

你可能感兴趣的:(Android的消息机制Handler)