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

//Handler构造函数public Handler() {//调用下面含有2个参数的构造函数        this(null, false);    }public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        /**        *Handler类含有2个成员变量,一个looper对象,一个messageQueue对象,在实例化Handle时个2个成员初始化赋值         *Looper.myLooper()方法返回当前线程looper对象引用,在后面分析Looper对象时再来分析这个方法实现。        */        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }       //初始化消息队列,Looper类负责初始化消息队列        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }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,target保存有发送此消息handler引用,代表这个消息最终由哪个handler来处理        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

可以发现,Handle发送的msg被添加进消息队列中,然而我们说这个队列是在Looper轮询器初始化的,我们先来看看Looper
进入Looper.java
Looper主要作用是从自己的消息队列中不断的拿出消息,并且分发给对应的handler处理

Looper对象的初始化

public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        /**        * 看到没下面这句才是真正实例化一个Looper对象        * 这里调用ThreadLocal类的set方法来保存looper引用        * 简单说下这个ThreadLocal类,这个类的主要线程的一个成员变量,用来存储线程的        * 私有数据,主要有2个方法,一个set(),一个get();        * 不懂同学可以看一看ThreadLocal原理        */        sThreadLocal.set(new Looper(quitAllowed));    }    //实例化Looper时,会在构造方法实例化一个MessageQueue对象,用来存储消息    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }    /**     * 这个方法是在主线程调用,用来在主线程中初始化looper     * 其实我们看还是调用prepare()方法     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }    public static @Nullable Looper myLooper() {        /**        * 看到没,ThreadLocal.get()返回了当前线程looper对象        *这个和前面Handler调用myLooper()返回looper引用对应        */        return sThreadLocal.get();    }

我们再来看看APP主线程ActivityThread类,是如何初始化looper轮询器的,当我们启动一个APP时,主线程ActivityThread类会被执行,从mian()函数开始

public static void main(String[] args) {       ......        /**        * 主线程初始化looper轮询器,我们在主线程中实例化Handler对象就是        * 绑定这个looper,而为什么没有传递looper引用        * 是因为中间有个ThreadLocal        */        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        // 开启loop死循环,不断轮训消息队列        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }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()从队列拿消息,当next()返回为null时,则在这里阻塞,等待唤醒后继续执行            Message msg = queue.next();              if (msg == null) {                // 如果没有消息,表明消息队列正在退出                return;            }            try {                //消息不为空,调用target对象dispatchMessage(),在发送msg时,msg的                //target就保存了发送消息的handle对象,此时这个msg就送到处理消息的                //handle中                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }        .....        }    }//消息分发public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            //handleMessage(msg)被我们重载实现            handleMessage(msg);        }    }

至此我们进入MessageQueue.java
MessageQueue有2个主要方法一个是消息入队enqueueMessage()。一个是消息出对next()

boolean enqueueMessage(Message msg, long when) {        ....        synchronized (this) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                //当入队消息是第一个时,如果loop()阻塞,则要唤醒loop线程                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;            }            if (needWake) {                //唤醒主线程,这里用到了jni编程,这这里不展开讲                nativeWake(mPtr);            }        }        return true;    }enqueueMessage()这个方法是把消息加入消息队列,中间有一些if-else判断,当队列为空,主线程空闲等待,有消息加入时则唤醒主线程。    Message next() {        // 从队列中取消息        final long ptr = mPtr;        if (ptr == 0) {            return null;        }        int pendingIdleHandlerCount = -1;         int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            //jni方法,功能是查看当前消息队列是否有消息,没有则等待,有则获取            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // 判断消息时间,没有到,则等待                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // 取消息                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    //没有消息,则阻塞等待                    nextPollTimeoutMillis = -1;                }}

通过UML绘制Handle、Message、MessageQueue、Looper之间关系

至此,一个消息完成整个流程。
欢迎大家对文章提出评论。

更多相关文章

  1. android的线程特点
  2. Android架构分析之Android消息处理机制(二)
  3. Android消息处理机制实现同步效果
  4. Android(安卓)Activity总结
  5. Android中Message机制的灵活应用
  6. Android中Message机制的灵活应用(一)
  7. 学习 Android(安卓)Handler 消息机制需要注意这些问题!
  8. Android(安卓)Java层 Looper 机制
  9. Android中的Handler的机制与用法详解

随机推荐

  1. JS的引入方式
  2. 懒加载及轮播图
  3. JQuery 常用方法
  4. 定位原理、模态框以及三列布局
  5. 作业_0706
  6. 前端作业-07-09
  7. 通过canvas作图片缓存
  8. uniapp H5图片上传压缩自动旋转
  9. canvas图片设置crossOrigin属性
  10. 演示文本操作3个功能+演示样式属性操作 3