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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。 能简单说得我们尽量不复杂: 为了避免ANR,我们会通常把...
    阿敏其人阅读 33,814评论 5 110
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,434评论 25 707
  • 1. ANR异常 Application No Response:应用程序无响应。在主线程中,是不允许执行耗时的操...
    JackChen1024阅读 1,342评论 0 3
  • 前言 今天去电影院看了《大话西游2》重制版,带着满满的情怀回来。虽然《大话西游》已经被解读过无数次,但我不想浪费这...
    弗雷斯特阿甘阅读 234评论 0 0
  • 耳朵里塞着耳机,最小的音量,最清晰的音质,最适宜的脚步,最好的速度,最美的时光,和光景,这一切都抚慰着,寂静的心灵...
    慕卿苑阅读 268评论 0 0