频道栏目
首页 > 资讯 > Android > 正文

Android中与消息机制相关类分析

18-05-05        来源:[db:作者]  
收藏   我要投稿

Android中与消息机制相关的主要类有Looper、Handler、Message和MessageQueue。

Looper

Looper是线程消息循环处理器,正是由它不停的去从MessageQueue中获取Message,然后交给Handler处理。每个线程只能有一个Looper,在Android中只有主线程默认创建有Looper对象,其余线程需要自己创建。

下面结合源码来深入理解一下Looper,上面说了除了UI线程需要自己创建Looper对象,并且只能创建一个,看下Looper的创建方法prepare():

    private static void prepare(boolean quitAllowed) {
        //创建之前先判断sThreadLocal中是否有Looper对象,如果有抛异常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建的Looper对象存放在sThreadLocal中,这样Looper对象就和创建它的线程关联在一起了
        sThreadLocal.set(new Looper(quitAllowed));
    }

前面提到UI线程不需要自己创建Looper对象,那如果创建了会是怎样呢?

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //创建Looper对象
        Looper.prepare();
    }

程序报错:

Caused by: java.lang.RuntimeException: Only one Looper may be created per thread
 at android.os.Looper.prepare(Looper.java:95)
 at android.os.Looper.prepare(Looper.java:90)
 at com.example.wangc.myapplication.MainActivity.onCreate(MainActivity.java:20)
 at android.app.Activity.performCreate(Activity.java:7117)
 at android.app.Activity.performCreate(Activity.java:7108)

创建好Looper对象后,就可以调用loop()方法进入消息循环,下面看下loop()方法具体是怎么循环遍历MessageQueue中的消息并且分发。

  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 (;;) {
            Message msg = queue.next(); // 取一条消息,没有消息会阻塞
            if (msg == null) {
                //消息队列中没有消息了,退出循环
                return;
            }
           .........省略.......
            try {
                //分发消息,这里的msg.target其实是一个Handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
             .........省略.......
            msg.recycleUnchecked();
        }
    }

至此Looper在消息处理中的任务就算完成。

Message

Message是消息的载体,发送者把需要传递的消息放在Message对象中。先看一下它中的重要变量和方法。

 //相当于用户定义的message识别码,用于区分不同的handler发送的消息
 public int what;
 //如果只需要传递整形数据的时候,不需要使用setData(Bundle)去设置数据,使用arg1,arg2开销更小。
 public int arg1;

 public int arg2;
 //传递的对象数据
 public Object obj;
 //消息的状态:FLAG_IN_USE = 1 << 0 表示消息正在使用
 int flags;
 //消息被处理的时间   
 long when;
 //消息携带的数据   
 Bundle data;
 //处理该消息的handler   
 Handler target;
 //传进来的runnable,最后也是封装成message   
 Runnable callback;

 //对象锁,在obtain()中有使用
 private static final Object sPoolSync = new Object();
 //重用的message
 private static Message sPool;
 //可以被重用的消息数
 private static int sPoolSize = 0;
 //相当于最大的可重用消息数
 private static final int MAX_POOL_SIZE = 50;

接下来依次分析Message对象是如何创建以及如何回收的?

Message对象的创建总的来说分为两种一是直接new一个message对象,还有一种是obtain()方法以及各种重载,但推荐使用后者。

new的方式和创建普通的Java对象一样:

    Message message = new Message();
    message.what = 0;

obtain()方法的重载有7种方式,这里只关注obtain()查看obtain()源码:

    public static Message obtain() {
        //前面提到的对象锁
        synchronized (sPoolSync) {
            if (sPool != null) {//有可用的message对象
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 将message的标志设置为使用中
                sPoolSize--;
                return m;
            }
        }
        //如果没有可重用的message对象还是直接new一个
        return new Message();
    }

到这里就可以知道为什么推荐使用obtain()来创建message对象了,因为它优先去找是否有可重用的,没有可重用的才会new message对象,这样降低了创建重复对象的开销。

Handler

到现在已经知道Looper是去循环遍历MessageQueue获取Message,那Message是如何添加到MessageQueue中去的呢?其实是通过Handler添加进去的,先看如何发送消息,

     public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

跟进源码发现sendMessage又是调用的sendMessageDelayed(),继续跟进,

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

可以看到这里又调用了sendMessageAtTime()方法再继续跟进,

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        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);
    }

到这里终于看到MessageQueue了,所以最终将消息添加到MessageQueue的是enqueueMessage,

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

处理Message也是由handler来完成。

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

在使用Handler时要注意一点,它很容易引起内存泄漏,所以在页面退出时最好调用removeCallbacksAndMessages()清空消息队列中的消息对象。

@Override
public void onDestroy() {
    mHandler.removeCallbacksAndMessages(null);
}

总结

Looper

通过loop()方法轮询MessageQueue,并将消息分发给对应的Handler.

Handler

发送消息和处理消息,在主线程中建立Handler并利用它在子线程中向主线程发送消息,在主线程接收到消息后会对其进行处理。

MessageQueue

保存Message

相关TAG标签
上一篇:ios进阶教程-如何使用RUNLOOP优化大图加载?
下一篇:静态路由和动态路由_路由汇总和默认路由
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站