对Android消息机制的理解

对Android消息机制的理解

Android的消息传递,是系统的核心功能

预备知识:

1. 什么叫做消息?

  • 消息(Message)代表一个行为(what)或者一串动作(Runnable),每一个消息在加入消息队列(MessageQueue)时,都有明确的目标(Handler

2. android怎么使用消息

// 子线程
    private class WorkThread extends Thread {
        @Override
        public void run() {
            //......处理比较耗时的操作
 
            //处理完成后给handler发送消息
            Message msg = new Message();
            msg.what = COMPLETED;  // 设置消息行为(what)
            handler.sendMessage(msg);
        }
    }
//主线程
private Handler handler = new Handler() {  // 消息的目标(handler)
        @Override
        public void handleMessage(Message msg) {   // 主线程接收子线程的消息
            if (msg.what == COMPLETED) {
                stateText.setText("completed");
            }
        }
    };

3. 为什么要使用消息机制?
有一个问题:既然消息是子进程通知主进程来完成某个任务,为什么子进程不自己去执行呢?

  • 1 不阻塞主线程

Android应用程序启动时,系统会创建一个主线程,负责与UI组件(widget、view)进行交互,比如控制UI界面界面显示、更新等;分发事件给UI界面处理,比如按键事件、触摸事件、屏幕绘图事件等,因此,Android主线程也称为UI线程。
由此可知,UI线程只能处理一些简单的、短暂的操作,如果要执行繁重的任务或者耗时很长的操作,比如访问网络、数据库、下载等,这种单线程模型会导致线程运行性能大大降低,甚至阻塞UI线程,如果被阻塞超过5秒,系统会提示应用程序无相应对话框,缩写为ANR,导致退出整个应用程序或者短暂杀死应用程序。

  • 2 并发程序设计的有序性

单线程模型的UI主线程也是不安全的,会造成不可确定的结果。
线程不安全简单理解为:多线程访问资源时,有可能出现多个线程先后更改数据造成数据不一致。比如,A工作线程(也称为子线程)访问某个公共UI资源,B工作线程在某个时候也访问了该公共资源,当B线程正访问时,公共资源的属性已经被A改变了,这样B得到的结果不是所需要的的,造成了数据不一致的混乱情况。
线程安全简单理解为:当一个线程访问功能资源时,对该资源进程了保护,比如加了锁机制,当前线程在没有访问结束释放锁之前,其他线程只能等待直到释放锁才能访问,这样的线程就是安全的。

Android只允许主线程更新UI界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到Handler机制来解决此问题。基于Handler机制,在子线程先获得Handler对象,该对象将数据发送到主线程消息队列,主线程通过Loop循环获取消息交给Handler处理。


Android消息机制原理

1. 消息机制结构

Android消息机制结构
流程: 子线程消息请求 --> handler推送消息到消息队列 --> looper循环取消息 --> 主线程处理消息

2. 消息机制内容

  • Handler

    消息的真正处理者,具备获取消息、发送消息、处理消息、移除消息等功能。

  • Message

    消息(Message)代表一个行为(what)或者一串动作(Runnable),每一个消息在加入消息队列时,都有明确的目标(Handler)。

  • MessageQueue

    以队列的形式对外提供插入和删除的工作,其内部结构是以链表的形式存储消息的。

  • Looper

    Looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给目标(Handler)处理。


源码分析
  • Handler

Handler的构造方法

public Handler() {
    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());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = null;
}
/**
 * Use the provided queue instead of the default one.
 */
public Handler(Looper looper) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = null;
}

Handler消息处理
send接口

public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)

所谓的“(send)发送”消息只是把消息插入到了消息队列中,同时指定消息处理的时间。如果指定的时间为0,表示要立即处理,MessageQueue会把这条消息插到队列的头部。

post接口

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r)
{
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

从代码的实现上看,这些“Post”方法也是在使用“send”类的方法在发送消息,只是它们的参数要求是Runnable类的对象,然后在方法中调用getPostMessage()获取了一个Message对象来发送。
看到这里就很好理解了:“post”类型的方法用来发送带有处理方法的消息“send”类型的方法则用于发送传统的带有消息Id的消息

  • Message

引用google原文:

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler#obtainMessage methods, which will pull them from a pool of recycled objects.

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool; 
                sPool = m.next; 
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
   // message池回收机制
    public void recycle() {
        clearForRecycle();
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
  • Message类表示一个消息对象,要发送消息首先就要先获得一个消息对象,Message类的构造函数是public的,但是不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的,
    Message内部通过next成员实现了一个链表,这样sPool就了为了一个Messages的缓存链表。

  • MessageQueue
    MessageQueue对象的构造是调用本地方法nativeInit()完成的。nativeInit()方法对应的JNI函数是native层的android_os_MessageQueue_nativeInit()函数

static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ......
}
//android_os_MessageQueue_nativeInit()函数最主要的功能是新创建了一个本地的
//Native MessageQueue对象。NativeMessageQueue的构造函数如下:

NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

NativeMessageQueue的构造函数只是创建一个本地的Looper类对象。从NativeMessageQueue类的代码看,它本质上是一个代理类。它把Java层的调用转变为对native层Looper类的函数调用,native层的Looper类才是关键所在。
native层的Looper类也实现了一套完整的消息处理机制。但是Java层的Looper类和native层的Looper类并没有直接关系。MessageQueue虽然使用了Native层的Looper类,但也只使用了它的等待/唤醒机制,其余的如消息队列的实现还是在Java层。因此,如果再看到MessageQueue有中从Java到native层之间的调用,可以略去中间过程,直接分析native层Looper类中的函数。

MessageQueue中的消息循环在方法next()中

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
       return null;
    }  
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 调用本地方法等待nextPollTimeoutMillis毫秒, -1表示要永远阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);
        // 这里使用了针对this对象同步,因此只要next方法还没退出
        // 再调用本对象的任何方法都将导致调用线程挂起
        synchronized (this) { 
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;        // mMessages指向队列头
            if (msg != null && msg.target == null) {
                // “SyncBarrier”的标志就是其消息的target为null
                // 如果队列的第一条消息是“SyncBarrier”,忽略普通消息,查找第一条“异步消息
                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 (false) Log.v("MessageQueue", "Returning message: " + msg);             

                return msg;

            }

        } else {  // 表示队列中没有现在必须处理的消息了 

            nextPollTimeoutMillis = -1;

        }

        if (mQuitting) { // 如果退出标志设置了,则销毁native对象,然后返回

            dispose();

            return null;

        }                     

        // 第一次进入idle会检查是否安装了idle handler

        if (pendingIdleHandlerCount &lt; 0

                &amp;&amp; (mMessages == null || now &lt; mMessages.when)) {

            pendingIdleHandlerCount = mIdleHandlers.size();

        }

        if (pendingIdleHandlerCount &lt;= 0) {

            mBlocked = true;

            continue;  // 没有安装idle handler则继续for循环

        }

        // idle handler放入数组mPendingIdleHandlers中

        if (mPendingIdleHandlers == null) {

            mPendingIdleHandlers = new IdleHandler[Math.max(

                                      pendingIdleHandlerCount, 4)];

        }

        mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

    }

    // 处理所有idle handler,如果回调结果为false,表示不再继续处理

    // 则 从idle handler的列表中移除该handler

    for (int i = 0; i &lt; pendingIdleHandlerCount; i++) {

        final IdleHandler idler = mPendingIdleHandlers[i];

        mPendingIdleHandlers[i] = null; 

        boolean keep = false;

        try {

            keep = idler.queueIdle();

        } catch (Throwable t) {

            Log.wtf("MessageQueue", "IdleHandler threw exception", t);

        }

        if (!keep) {

            synchronized (this) {

                mIdleHandlers.remove(idler);

            }

        }

    }

    pendingIdleHandlerCount = 0;

    //如果有idle handler存在,把nextPollTimeoutMillis设为0,让循环继续,而不是阻塞

    nextPollTimeoutMillis = 0;

}
}
  • Looper
    Looper构造函数
Looper::Looper(bool allowNonCallbacks) :

    mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),

    mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {

int wakeFds[2];

int result = pipe(wakeFds);  //创建匿名管道   .

mWakeReadPipeFd = wakeFds[0];  

mWakeWritePipeFd = wakeFds[1];  

//把读写管道都设成非阻塞式

result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);

result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); 

mIdling = false;

mEpollFd = epoll_create(EPOLL_SIZE_HINT);  //创建和初始化epoll对象    

struct epoll_event eventItem;

memset(&amp; eventItem, 0, sizeof(epoll_event));

eventItem.events = EPOLLIN;

eventItem.data.fd = mWakeReadPipeFd;

//把“读管道”加入到epoll监听中

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &amp; eventItem); 
}

Android的消息队列和消息循环都是针对具体线程的,一个线程可以存在一个消息队列和消息循环,特定线程的消息只能分发给本线程,不能跨线程和跨进程通讯。但是创建的工作线程默认是没有消息队列和消息循环的,如果想让工作线程具有消息队列和消息循环,就需要在线程中先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。


整个流程是这样的 : Android的主线程就是ActivityThread,入口方法是main。通过Looper.prepareMainLooper()来创建主线程的Looper和MessageQueue,然后调用Looper.loop()开启主线程的消息循环。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值