Android 中Handler、Message、Looper机制详解

通过这张图,大家会对Handler、Message有一个初步的、形象化的认识。下面我们研究Handler、Message的源码。

我们先了解与消息机制有关的几个类

1、Message

消息对象,顾名思义就是记录消息信息的类,这个类有几个比较重要的字段:

a、arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。

b、obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。

c、what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过
switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。

2、Message Pool

通常,我们使用Message.obtain()和Handler.obtainMessage()从Message Pool中获取Message,避免直接构造Message。那么Android是否因为Message Pool缓存的Message对象而造成OOM呢?对于这个问题,我可以明确地说:“APP不会因Message Pool而OOM”。为什么呢?我们一步一步看。

Message Obtain分析

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            //如果池中数据不为空,则将池中数据取出作为当前消息
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0;
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

从代码片中,可以看到Message是直接由sPool赋值的。

Handler.obtainMessage()源码

public final Message obtainMessage(){
    return Message.obtain(this);
}

Handler.obtain()最终还是调用Message.obtain()来获取的。

下面是Message Pool的相关源码分析。

Message Pool数据结构

Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;

从代码中可以很明确的看到,Message Pool的数据结构实际就是一个链表。sPool就是一个全局的消息池,sPoolSize记录链表长度,MAX_POOL_SIZE表示链表的最大长度为50。

消息回收

 /**
     * Return a Message instance to the global pool.
     * <p>
     * You MUST NOT touch the Message after calling this function because it has
     * effectively been freed.  It is an error to recycle a message that is currently
     * enqueued or that is in the process of being delivered to a Handler.
     * </p>
     */
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

从代码分析上看:
1、将待回收的Message对象字段置空(避免因Message过大,使静态的消息池内存泄漏)。因此无论原先的Message对象有多大,最终被缓存进Message Pool前都被置空,那么这些缓存的Message对象所占内存大小对于一个APP内存来说基本可以忽略。所以说,Message Pool并不会造成APP的OOM。
2、以内置锁对方式(线程安全),判断当前线程池的大小是否小于50。若小于50,直接将Message插入到消息池链表尾部;若大于等于50,则直接丢弃掉,那么这些被丢弃的Message将交由GC处理。

3、MessageQueue

消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。

//重点方法:enqueueMessage
final boolean enqueueMessage(Message msg, long when) {
    //...
    synchronized (this) {

        //...
        //内部的死循环,不断从将发送过来的消息加入到消息队列中,并按照时间顺序排队
        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;
    //...
    return true;
}
//从上述代码中我们可以看出,enqueueMessage其实就是对要加入消息队列中的消息进行排队,如果有延迟时间则排到队列后,没有则排到队列前面,当排好队后,等待Looper的loop()方法从排好的消息队列中取出消息。

4、Looper

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:

class LooperThread extends Thread{
    public Handler mHandler;
    public void run(){
        Looper.prepare();
        //其它需要处理的操作
        Looper.loop();
    }
}

和这个方法类似的方法:prepareMainLooper,这个方法是系统调用,为了给主线程准备的Looper,我们无需手动调用

public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    //表示每个线程只能拥有一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //原来Looper.prepare是为了给当前线程准备一个Looper
    sThreadLocal.set(new Looper(quitAllowed));
}


//ThreadLocal:线程本地变量,在哪个线程set的值,只能在那个线程获取出来
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//从线程本地变量中获取出来
//说明:Looper只属于当初设置的那个线程

//倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()
//获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则
//Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象,但Looper.getMainLooper是由系统调用,供主线程使
//用.故:我们在线程给主线程发送消息的时候,在主线程中无需调用Looper.prepare()

Looper.myLooper():

public static Looper myLooper() {
    return sThreadLocal.get();
}

//loop()方法内部维护一个死循环不断的从消息队列中获取消息并通过msg.target.dispatchMessage(msg)最终将msg传递给handler的handleMessage()方法
public static void loop() {
    final Looper me = myLooper();

    //...
    //内部的死循环不断的从消息队列中获取消息
    for (;;) {
        Message msg = queue.next(); // might block

        //...
        //这里的msg.target就是handler
        //不断的将消息传递到handler的dispatchMessage方法参数中
        msg.target.dispatchMessage(msg);

        //...
        //处理完消息后,回收消息
        msg.recycle();
    }
}


Handler类中:

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }   

5、Handler

handler 通过发送和处理Message和Runnnable对象来关联相对应线程的MessageQueue
1、可以让对应的Message和Runnable在未来的某个时间点进行相应处理
2、自己想要处理的耗时操作放在子线程,让更新ui的操作放在主线程
3、在子线程中使用handler需要在前后使用Looper.prepare() 和Looper.loop();
消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的Handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。

//关键方法:
//obtainMessage:利用Message.obtain()方法,从消息池中获取消息
//sendMessage:
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
        msg.target = this;  // message的target必须设为该handler!
        //这里调用MessageQueue的enqueueMessage()方法,将消息添加到消息队列中并排队
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    //...
    return sent;
}
//handleMessage,从消息队列中获取消息并处理,默认父类不处理,通常由子类实现并处理
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
  • Handler通过obtainMessage方法从消息池中获取消息,通过sendMessage方法将消息发送到消息队列中;
  • MessageQueue通过enqueueMessage将Handler发送过来的消息进行排队;
  • Looper.prepare方法为当前线程准备一个Looper;
  • Looper.myLooper方法从线程中取出之前准备的Looper;
  • Looper.loop方法,内部的死循环将消息队列中的消息取出,然后将消息中持有的Handler引用获取到,将消息作为方法参数发送到Handler的handleMessage方法中,由Handler的实现类进行相关处理。

6、拓展

Handler 造成的内存泄漏
解决方式一:

private  String test="abc";
    //leak:泄漏
    //handler这样声明会造成内存泄漏(不使用static)
    //产生原因:内部类隐式的持有外部类的引用
    //java为了避免由于外部类回收造成的内部类直接被回收,将外部类的引用传给内部类
    //解决办法:将内部类声明成静态的,由于静态内部类不外部类的引用
    //引用的四种级别:
    //1,强引用 GC永远不能回收
    //Object o=new Object();
    //当o=null;new Object就可以回收
    //2,软引用:当内存不足时,GC回收软引用中的对象数据
//  SoftReference<Object> sr=new SoftReference<Object>(r);
    //sr就是对Object的软引用
    //3,弱引用:GC可以随时回收
//  WeakReference<Object> wr=new WeakReference<Object>(r);
    //wr就是对Object的弱引用
    //4,虚引用 一般不用

         //5 在activity生命周期onDestory结束时候调用 handler.removeCallback()
//  private static Handler handler=new Handler(){
//      public void handleMessage(android.os.Message msg) {
//          //彻底处理完消息,这个对象就可以回收了
//          
//      };
//  };
    public static class MyHandler extends Handler{
        private WeakReference<Context> wr=null;
        public MyHandler(Context context) {
            wr=new WeakReference<Context>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Context context = wr.get();
            if(context!=null){
                MainActivity activity=(MainActivity) context;
            }
        }
    }
    private MyHandler myHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myHandler=new MyHandler(this);

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值