Android 消息传递机制分析

来源:互联网 发布:淘宝橙色cmyk 编辑:程序博客网 时间:2024/06/16 18:16

Android中采用Handle进行消息传递,本文从实例出发分析Handle中一个Message从创建到处理整个流程,以此阐明消息传递机制原理。
一、Handle使用示例
1、在Android studio 创建一个默认项目,在MainActivity中写下如下代码:

public class MainActivity extends AppCompatActivity {    private TextView tv;    private final static int MSGTYPE =1;    //主线程中创建Handle,并重写Handle处理消息的方法handleMessage()    //我这里只是简单处理更新一个textview显示    Handler mHandle = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case MSGTYPE:                    if(tv !=null){                        tv.setText(String.valueOf(msg.obj));                        Log.d("Handle",String.valueOf(msg.obj));                    }                    break;            }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv = (TextView) findViewById(R.id.tv);            new Thread(){                @Override                public void run() {                    try {                        for(int i= 0 ; i<10; i++) {                            Thread.sleep(1000);                            //建立一个消息实体,并携带数据                            Message msg = new Message();                            msg.what = MSGTYPE;                            msg.obj = "Thread" + i;                            //通过Handle发送消息                            mHandle.sendMessage(msg);                        }                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }.start();   }}

子线程通过handle发消息给主线程(UI线程)然后在主线程handle中处理消息,来达到更新UI目的。那么问题来了在这里我们为什么不直接在主线程更新UI或者直接在子线程调用tv.setText()?不在主线程更新是因为主线程不能进行耗时操作,假设一种场景tv控件的数据来源于网络,需要建立一个http获取数据,网络操作是一种极其耗时操作,要等几秒才能更新UI,这对用户体验来说极其不好,而且Android规定UI线程5s没反应,会报ANR错误,所以耗时操作都放子线程。而由于主线程不是线程安全的,Android规定子线程不能更新UI元素。
二、Handle原理分析
在这里我们从上述demo中mHandle.sendMessage(msg)这句代码作为切入口,分析这个msg经过哪些流程被送到mHandle.handleMessage(msg)中进行处理的。
进入Handle.java

public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        //Handle类持有MessageQueue引用        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        //target类型是Handle,this代表消息最终由哪个handle处理        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

可以发现,Handle发送的msg被添加进消息队列中,至此我们进入MessageQueue.java

boolean enqueueMessage(Message msg, long when) {        ....        synchronized (this) {            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                //唤醒主线程                nativeWake(mPtr);            }        }        return true;    }

enqueueMessage()这个方法是把消息加入消息队列,中间有一些if-else判断,当队列为空,主线程空闲等待,有消息加入时则唤醒主线程。我们再来看看Looper.java是怎样从队列拿消息分发给handle处理

public Handler(Callback callback, boolean async) {        ...        //Handle持有所在线程looper对象引用        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        //Handle持有Looper内部成员MessageQueue的引用        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    } public static @Nullable Looper myLooper() {        //返回当前线程所保存的looper对象,这里用到了ThreadLocal        //ThreadLocal是线程内部存储数据类,是线程的一个内部成员,        //在这里保存了自己的创建的looper对象        return sThreadLocal.get();    }    //prepare用于初始化looper对象private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        //创建looper对象,并保存looper到当前线程ThreadLocal中        sThreadLocal.set(new Looper(quitAllowed));    }public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        //开启死循环,不停到队列拿消息        for (;;) {            //next()从队列拿消息,当队列为空时则阻塞等待            Message msg = queue.next();              if (msg == null) {                // 如果没有消息,表明消息队列正在退出                return;            }            try {                //消息不为空,调用target对象dispatchMessage(),                  //在发送msg时,msg的target就保存了发送消息的                //handle对象,此时这个msg就送到处理消息的handle中                msg.target.dispatchMessage(msg);            } finally {                ....            }        .....        }    }public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            //handleMessage(msg)被我们重载实现            handleMessage(msg);        }    }

通过UML绘制Handle、Message、MessageQueue、Looper之间关系
这里写图片描述

至此,一个消息完成整个流程。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 吃热的凉的牙疼怎么办 我买的股票退市了怎么办 如果起诉离婚另一方不出庭怎么办 10个月宝宝还没长牙怎么办 超敏c反应蛋白>5怎么办 怀孕才两个月肚子就大了怎么办 腰椎间盘突出腿疼厉害怎么办 打了促排卵针不排卵怎么办 孕34周隐血1十是怎么办 窦性心动过缓伴不齐怎么办 09年买的万科b怎么办 苹果5s手机打不开机怎么办 剖腹产后一年半后意外怀孕怎么办 考驾照挂了5次了怎么办 怀孕咳嗽一个月了好不了怎么办 孕妇餐后2小时血糖高怎么办 我想开网店但我不懂该怎么办 新开的淘宝店没生意怎么办 做肝胆b超喝了水怎么办 红米1s刷机失败怎么办 红米3x手机太卡怎么办 红米2a线刷失败怎么办 红米2用不了4g怎么办 小米红米3s卡顿怎么办 魅蓝4g网速很慢怎么办 lol装到c盘了会怎么办 急用钱又借不到怎么办小额信贷 花呗分期买手机额度不够怎么办 2个月婴儿脸皴了怎么办 掉头发怎么办怎样能让头发变多 11个月的宝宝大便干燥怎么办 1岁宝宝又拉又吐怎么办 怀孕八个月了不想要了怎么办 奶水不够宝宝又不喝奶粉怎么办 手机恢复出厂设置密码忘了怎么办 5s锁屏密码忘了怎么办 深圳房子卖了户口没地方迁怎么办 宝马1系130i烧机怎么办 小孩流清鼻涕怎么办最简单方法 孕3个月胎盘低置怎么办 孩子判逆不听家长的话该怎么办