Android Handler总结

作者: sunbinqiang | 来源:发表于2016-08-21 22:52 被阅读737次

    Handler是什么

    Handler是Android提供的用来更新UI的一套机制, 也是一套消息处理机制,我们可以通过它来发送和处理消息。
    Handler在Android Framework中应用也非常广泛: 其中,Activity生命周期回调的方法被调用的过程就是通过Handler来实现的。

    为什么要使用Handler

    Android设计的时候就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制更新UI界面,会抛出异常错误(非UI线程更新UI)。
    另外,关于UI线程和更新UI,官网有如下一段话的解释:

    When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler.

    当应用启动的时候, 会创建一个进程,进程的主线程运行着消息队列,管理着最高级别的应用对象(例如activity,broadcast receivers 以及其他界面上的内容),这个主线程就是我们常说的UI线程。当我们创建自己的线程的时候,我们想要和主线程进行通信,就必须通过Handler来实现。

    Handler 基本用法

    官网的解释:

    There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

    翻译不太通顺,直接用代码来解释一下吧:
    1, Handler提供了以下方法:
    post(Runnable) [postAtTime(Runnable, long)](https://developer.android.com/reference/android/os/Handler.html#postAtTime(java.lang.Runnable, long)) [postDelayed(Runnable, long)](https://developer.android.com/reference/android/os/Handler.html#postDelayed(java.lang.Runnable, long)) sendEmptyMessage(int) sendMessage(Message) [sendMessageAtTime(Message, long)](https://developer.android.com/reference/android/os/Handler.html#sendMessageAtTime(android.os.Message, long)) [sendMessageDelayed(Message, long)](https://developer.android.com/reference/android/os/Handler.html#sendMessageDelayed(android.os.Message, long))
    其中post Runnable 是将Runnable发送给其他线程(创建handler的线程)执行;
    send message一类的方法则是发送消息, 与下面的handleMessage相对应:
    2, 重写Handler的handleMessage方法,可以通过message类型判断执行条件,而handleMessage的执行是在其他线程中,可以做UI更新的操作。

    Handler handler = new Handler(){    
        @Override    
        public void handleMessage(Message msg) {  
                super.handleMessage(msg);
                ...//更新UI
        }
    };
    

    实例

    下面是一个通过Handler实现TextView控件读秒操作的例子, 在App的首页广告闪屏中很常见,直接上代码:

    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //计时
            int msgId = msg.what;
            if(msgId == MSG_ID_COUNT){
                int count = getCount();
                    ignoreBtn.setText("跳过 " + count);
                if(count > 0){
                    mHandler.sendEmptyMessageDelayed(MSG_ID_COUNT, 1000);
                }
            }else if(msg.what == MSG_ID_IGNORE){
                launchApp();
            }else if(msg.what == MSG_ID_NOAD){
                launchApp();
            }
        }
    };
    
    private int getCount(){
        mCount--;
        if(mCount <= 0){
            launchApp();
        }
        return mCount;
    }
    

    写了一个Handler,通过重写handleMessage处理3个消息。我们会在程序刚启动的时候,发送一个MSG_ID_COUNT消息,开始首页广告的倒计时:

    mHandler.sendEmptyMessageDelayed(MSG_ID_COUNT, 1000);
    

    其中第二个参数表示延时的事件1000ms
    当用户点击“跳过”按钮的时候,我们会发送一个MSG_ID_IGNORE消息:

    mHandler.sendEmptyMessageDelayed(MSG_ID_IGNORE, 100);
    

    这样,就实现了一个基本的广告跳过的功能。可以看到,Handler实现了2个基本功能:

    1, 可以指定消息发送的时间,倒计时功能;
    2, 可以在handleMessage方法中更新UI,倒计时界面;

    Handler原理

    说到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();   // 获取Looper  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;       // 获取消息队列  
        mCallback = null;  
    }
    

    上述注释的两行代码,可以看到有两个概念: **Looper, MessageQueue **
    首先说说这两个概念:

    1. Looper, 包含一个消息队列MessageQueue, 作为消息封装的载体。
      主要方法: Looper.looper() , 一个死循环方法, 不断地查看MessageQueue中是否有新的消息;
    2. MessageQueue, 一个消息队列, 可以添加消息,处理消息;

    那么,在Handler的构造方法中,获取了Looper和MessageQueue并保存为全局变量,是为了与Handler进行关联。
    下面是一张简明的图,很好的阐述了Handler, Looper, MessageQueue三者之间的关系:


    关系,来自stackoverflow

    总结: Handler负责发送消息, Looper负责接收Handler发送的消息,并直接把消息传回Handler自己,后续就会调用回调等方法(handleMessage)来处理。

    为什么要设计Handler更新UI
    上面的关系图,我们可以看到,如果想更新UI, 通过Handler的sendMessage方法发送消息, 然后作为UI Thread的Looper会轮询消息队列MessageQueue, 从而保证了多线程下UI更新的正确性。也就是说,如果没有Handler机制,在多线程下更新UI,我们必须使用锁来保护避免并发的问题。


    update 来自《Android开发艺术》
    Android中的UI并不是线程安全的, 如果在多线程下访问UI,则会导致UI界面状态不可控制的情况; 而如果加上锁的机制,有两个缺点:1, 将UI访问的逻辑变的非常复杂; 2, 会导致访问UI的效率变低。
    所以,基于这两个缺点,在多线程中操作UI,就提供了Handler机制。

    参考资料:
    慕课网 Handler详解
    Handler官网
    stackoverflow What is the relationship between Looper, Handler and MessageQueue in Android?

    相关文章

      网友评论

        本文标题:Android Handler总结

        本文链接:https://www.haomeiwen.com/subject/elspsttx.html