我会对android的消息处理有三个核心类逐步介绍,他们分别是:Looper,Handler和Message。其实还有一Message Queue(消息队列),知道它是队列即可,就像我们所熟知的数组,它是另一种数据结构,这里不对它做介绍了。

我会以尽量浅显易懂的文字去描述,逐步介绍三个核心类,这个也是安卓面试常考的题目,希望大家都能学会Handler机制,如果文章上有一些谬误,或者说你们有什么意见或者建议,欢迎留言。

概述

1、Looper,Handler,Message和MessageQueue之间的关系:

你可以这样去理解:handler是一个工人,跟他相关的有这些组件:

  • Message :Handler接收和处理的对象(待处理的产品)
  • MessageQueue:消息队列,他采用队列的方式管理Message(传送带)
  • Looper:每个线程中只能有一个looper对象,调用它的looper方法负责读取MessageQueue中的消息;(发动机)

2、形象地描述:

1、主线程中有很多个Handler(工人),

2、他们共用一个MessageQueue(传送带),一个Looper(发动机),然后MessageQueue(传送带)上有各种Message(待处理产品),

3、Looper(发动机)会遍历MessageQueue(传送带),

4、Looper(发动机)看到了Message(待处理产品),就将它交给专门的Handler(工人)负责。

3. Handler类

Handler有两个重要的功能,一个是发送消息,一个是处理处理消息。

常用的方法:

  1. void handleMessage(Message msg) 用于重写,处理接收的消息
  2. final Boolean hasMessage(int what) 检查消息队列中是否包含what为指定值的消息
  3. fina Boolean hasMessage(int what, Object obj)检查消息队列中是否有what为指定值,obj为指定对象的消息
  4. Message、obtainMessage();获取消息(有多个重载可以获取what值为指定值得message·)
  5. sendEmptyMessage(int what) 发送空消息
  6. final Boolean sendEmptyMessageDelayed(int what,long

    delayMills):指定多少毫秒后发送消息
  7. final Boolean sendMesage(Message msg);立即发送消息
  8. final Boolean sendMessageDelayed(Message msg,long mills)延时发送消息

4. Message类

android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。

常用变量和方法:

  1. public int what:变量,用于定义此Message属于何种操作
  2. public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
  3. public int arg1:变量,传递一些整型数据时使用
  4. public int arg2:变量,传递一些整型数据时使用
  5. public Handler getTarget():取得操作此消息的Handler对象。

注意事项:

  1. 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
  2. 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
  3. 擅用message.what来标识信息,以便用不同方式处理message。

5. Looper类

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。

在一个Activity中,系统会自动帮用户启动Looper对象,它负责不断地遍历MessageQueue上的消息。

用户也可以自定义Looper线程,则需要用户手工调用Looper类中的方法。

源码分析

Looper

Looper的表现形式为享元模式,线程中的所有Handler共享这个元件,而且,它的写法保证了它在线程中的单例,也就是说一个线程中,只会有一个Looper。

将普通线程升级为Looper线程

执行以下代码就好了,是不是很简单?

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

        // ...其他处理,如实例化handler

        // 开始循环处理消息队列
        Looper.loop();
    }
}

Looper构造函数

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
} 

在构造方法中,它创建了一个MessageQueue(消息队列)。

prepare()底层代码

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(true));
} 

可以看到,在prepare()方法中,它先通过sThreadLocal.get()判断当前的ThreadLocal中是否包含Looer,如果没有就创建一个,放到ThreadLocal。

如果已经有Looper就抛出异常。也就说Looper.prepare()不能被调用两次,它保证了一个线程中只有一个Looper实例。

loop()底层代码

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;  

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        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;
            }  

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }  

            msg.target.dispatchMessage(msg);  

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }  

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }  

            msg.recycle();
        }
}  

方法还是先获取了ThreadLocal的Looper实例,如果looper为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。

紧接着,就拿到该looper实例中的mQueue(消息队列),然后开始无限循环,遍历消息队列。

它会不断地取出一条消息,如果没有消息就不断地return(阻塞)。

调用 msg.target.dispatchMessage(msg)分发消息;其中msg的target其实就是一个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;
    }

这是默认的Handler构造函数,它自动地为当前Handler添加了一个Looper,如果当前线程没有Looper,他就会报错。

    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = null;
    }

而通过这个构造函数就可以在Handler中添加自己的Looper了。

sendMessage()的底层代码实现

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

 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
     Message msg = Message.obtain();
     msg.what = what;
     return sendMessageDelayed(msg, delayMillis);
 } 

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

 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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}  

sendMessage()调用到最后会执行enqueueMessage方法,enqueueMessage中首先为meg.target赋值为this,因此有下面结论:

1、把当前的Handler作为msg的target属性,msg也拥有了Handler的引用。

2、msg被放置到消息队列中,由Looper开始循环遍历。

3、当Looper遍历到msg,Looper直接根据msg中的target(Handler引用)找到对应的Handler。

主线程中的Looper

Android应用程序的入口为ActivityThread.main方法,此方法中首先会创建Application和默认启动的activity,并将他们关联在一起,而该应用的UI线程的Looper也是在这创建的,因此我们不用再显式地创建Looper。

public static void main(String args[]) {
    //...代码省略
    //1.创建消息循环Looper,也就是UI线程的消息队列,prepareMainLooper底层代码就是prepare
    Looper.prepareMainLooper;
    //启动ActivityThread
    ActivityThread thread = new ActivityThread();
    Thread.attach(false);
    AsyncTask.init();
    Looper.loop();//启动looper
}

Handler虽然能传Runable对象,内部实际用的还是Message

如果仅仅查看Handler的API,你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,而且最终也是加到了MQ当中。

Handler中源码如下:

    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

总结:

1、首先Looper.prepare()在本线程中创建一个Looper实例,创建Looper实例的时候,Looper构造函数中也创建了一个MessageQueue对象。

也就是说:Looper和MessageQueue一个线程只会有一个。

2、Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息。

3、Looper根据Msg的target找到对应的Handler,然后把Msg交给那个Handler去处理。

4、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue关联。

5、Handler的sendMessage方法中,Handler会将自身作为参数传给msg,msg中的target就是Handler自己,然后msg会加入到MessageQueue中。

6、在构造Handler实例时,我们会重写handleMessage方法,也就是消息分发中(msg.target.dispatchMessage(msg)),最终会执行(回调)我们handleMessage中写的方法。

最新文章

  1. what is difference in (int)a,(int&amp;)a,&amp;a,int(&amp;a) ?
  2. mongoDB研究笔记:复制集故障转移机制
  3. HDU1269 迷宫城堡
  4. zepto的bug2
  5. Recruit Coupon Purchase Winner&#39;s Interview: 2nd place, Halla Yang
  6. 阿里云实战之二(mysql+phpmyadmin)
  7. java.lang.StringBuilder源码分析
  8. JAVA--线程wait()、lnotify()和notifyAll()方法
  9. WindowsclientC/C++编程规范“建议”——前言
  10. 网络负载均衡环境下wsHttpBinding+Message Security+Windows Authentication的常见异常
  11. UML2.0
  12. VS2017提醒找不到MSVCR110D.dll
  13. day 4 - 2 数据类型练习
  14. poj 1789 prime
  15. PostgreSQL基础知识与基本操作索引页
  16. col-xs , col-sm , col-md , col-lg是什么意思?什么时候用?
  17. 让“懒惰” Linux 运维工程师事半功倍的 10 个关键技巧!
  18. Validate the date format
  19. vo、po、dto、bo、pojo、entity、mode如何区分
  20. 查询出结果 给其 加上序号的方法 msql

热门文章

  1. win10 uwp InkCanvas控件数据绑定
  2. 开始Java8之旅(四) --四大函数接口
  3. json生成方式
  4. 走进 UITest for Xamarin.Forms
  5. jdk8 JAVA_OPTS
  6. oracle建表权限问题和JSP连接oracle数据库基本操作
  7. Angular服务的5种创建方式
  8. 如何在 Apple TV 上使用描述文件
  9. win7旗舰版安装IIS
  10. localstorage本地定时缓存