Handler MessageQueue Looper消息循环原理

Handler概述

Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler来投递消息到主线程执行UI更新操作。因为Android系统的View是非线程安全的,所以需要在主线程更新UI。总的来说Handler就是用来做线程间通信,在不同线程之间传递消息。

消息循环主体结图例

Handler原理图

从图中可以看出,四种颜色分别代表了四个对象,并且大致描述了几个对象之间的关系,以及消息的流转过程,首先Handler通过sendMessage将消息投递给MessageQueue,Looper通过消息循环(loop)不断的从MessageQueue中取出消息,然后消息被Handler的dispatchMessage分发到handleMessage方法消费掉。
消息循环中涉及的重要对象

Handler

通过Handler的sendMessage等方法来投递消息到MessageQueue,通过handleMessage来消费Message。Handler必须要有一个已经prepare好的Looper对象,也就是说必须调用了prepare方法(也包括prepareMainLooper方法),究其根本是初始化一个消息队列,这一过程将在下文中详细分析。

Looper

Looper负责从MessageQueue中取出消息,然后通过执行message.target.dispatchMessage()消费掉这个消息,这里的target就是Handler。

MessageQueue

消息队列,管理Handler投递过来的消息。

Message

用来承载数据的消息,最终被Handler消费掉。

UML类图

UML类图

通过上面的类图可以清晰的了解各个类之间的关系。然后再来分析一下源码

Handler的创建

 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());
            }
        }
        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 = callback;
        mAsynchronous = async;
}
  1. 在上面这段代码中,首先是检查是否存在潜在的内存泄漏,如果该类是匿名内部类,或者是成员类且没有static修饰符时那么打印一个内存泄漏风险警告。这是由于这种类型的class持有外部类的this引用,可能导致外部类无法释放。
  2. 接下来就是对成员变量mLooper赋值,在文章开头就提到过,这篇文章中提到的handler对象时在主线程(UI线程)中创建,而Android主线已经有一个消息队列了,所以直接将mLooper.mQueue赋给Handler的mQueue。

主线程MessageQueue的创建

public static void prepare() {
        prepare(true);
}
public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
 }

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        将looper对象装入ThreadLocal中,Handler就是从它里面取出looper对象的
        sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
        //创建消息队列
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

UI线程在创建的时候,会调用prepareMainLooper()这个方法,创建一个不退出的消息队列。所以prepareMainLooper这个方法自己永远也不要调用,它是系统调用的,如果我们需要用自己的消息队列呢?那么就应该调用prepare()方法。

Message在MessageQueue的流转

整个消息循环系统中的几个重要部件的创建都已经明白了,那么消息时怎么循环起来的,又是如何消费的呢?来看看下面是loop源码的一部分关键代码。代码非常简单易懂,就是从消息队列中取出消息,然后通过msg.target.dispatchMessage(msg)将消息投递到Handler。

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;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked();
        }
    }

Message被主线程消费

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

当消息循环中取出的消息被再次传递给Handler的时候,这个消息就走到了生命的尽头(并不代表对象销毁,有一个消息池来回收消息),从dispatchMessage方法可以看出,消息最终的归宿有三个,一是消息自身的callback接口,二是handler的callback接口,最后是handleMessage接口。

具体实例:

1、自定义线程相关handler使用

class MyThread extends Thread {
    public Handler handler;
    @Override
    public void run() {
        Looper.prepare(); //new 一个Looper对象
        handler = new Handler() { //拿到当前线程的 Looper 对象
            @Override
            public void handlerMessage(Message msg) {
                // TODO Auto-generated method stub
                System.out.println("current thread:" + Thread.currentThread());
            }
        };
        Looper.loop();//开始死循环处理消息
    };
}

2、主线程与子线程之间的信息交互

//创建主线程的Handler
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        Message mssage = new Message();
        System.out.println("main Handler");
        //向子线程发送消息
        threadHandler.sendMessageDelayed(message, 1000);
    };
};
//创建子线程的 handler
private Handler threadHandler;

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

    HandlerThread thread = new HandlerThread("handlerThread");
    //创建子线程的 handler
    threadHandler = new Handler(thread.getLooper()) {
        public void handlerMessage(Message msg) {
            Message message = new Message();
            //向主线程发送消息
            mHandler.sendMessageDelayed(message, 1000);
        };
    };
}

参考链接:

http://vjson.com/wordpress/handler-looper%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.html
http://it.51xw.net/mobile/2015/04/86.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值