Android开发Android开发Android知识

Android笔记之Handler异步消息处理机制

2016-08-30  本文已影响406人  小时不识月z

主要参考文献:深入理解Android,卷1
       Android 5.0 源码

学识尚浅,错误之处请指正。

为什么需要这种机制?

<p>在Android的应用开发中我们经常遇到这样的场景,为了避免在UI线程中的耗时操作产生ANR,我们会开启新的线程来处理好事操作,得到处理结果在对UI进行更新。
但是因为android本身设计时考虑到性能优先,所以UI操作并非线程安全的,为了避免多线程操作UI带来的线程安全问题,android设计者直接规定:只允许UI线程来操作UI组件。
为了解决这个问题我们需要考虑这么几个问题:

  1. 解决线程间的相互通信。
  2. 一方发送的消息另一方能够及时收到,并做出处理。

Handler异步消息处理机制就是Android中解决这个问题提供的一种方案。

基本的原理很简单:

<p>

他的四个组成部分Message、Handler、MessageQueue和Looper,形象的说明了各自的分工:

图片来源:http://www.lxway.com/608450004.htm

举一个并不确切的例子说一下我的理解:

Handler好比一个要搬家的人,不同的线程就像是旧的住所和新的住所,你在旧的住所将自己的东西打包好装在行李箱(Message )中,并在上面贴上自己的标签,如姓名联系方式等(Message.target);MessageQueque好比是物流,你将打包好的行李箱交给他,他帮你运输和存储;looper就好比送货员,他将运送过来的消息取出来,查看主人(target),它将你的东西送到你的手里,任凭你的处理和摆放。

如何简单地使用它

<p>网上有很多这样的教程,但是我看了一些,觉得大多数单单使用这一点并没有讲清楚,让初次接触的人用起来非常复杂,其实事实并不是这样的。希望我能够尝试这说清楚。
首先它是一种线程间通信的通信的方式,因此至少涉及到了两个线程,我们将这两个线程按照一次消息处理的地位分为发送消息线程和接受消息线程。

对于接受线程:

  1. 创建Looper对象。
    Looper.prepare();使用Looper的prepare()方法即可创建一个Looper对象,其中也包含了MessageQueque对象的创建,当然每一个线程中只能允许拥有一个Looper对象。
  2. 初始化Handler对象,并重写其handleMessage()方法来处理接受的消息。
  3. 启动Looper。
    Looper.loop();使用Looper的loop()方法即可启动loop,这样Looper对象才能不断的管理MessageQueque。

对于发送线程:

  1. 创建携带信息的Message对象。
  2. 使用接受线程创建的Handler对象来发送消息。
    reThread.mHandler.sendMessage(msg);
简单的小程序
/**
 * 该例子是在子线程为发送消息的线程,主线程中接收消息
 */
public class HandlerTestActivity extends AppCompatActivity {

    //声明Handler变量
    private Handler mHandler ;

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

        //开启一个接受线程
        Thread reThread = new Thread(){
          @Override
          public void run(){
              //1.使用prepare函数创建Looper对象
              Looper.prepare();
              //2.初始化Handler对象
              mHandler = new Handler(){
                  //3.重写handleMessage方法来处理接收到的消息
                  @Override
                  public void handleMessage(Message msg) {
                      //Message.what字段可以携带少量信息,这里作为身份识别信息
                      if(msg.what==0x123){
                          //接收到消息,弹出对话框提醒
                          Toast.makeText(HandlerTestActivity.this,"reThread接收到消息",Toast.LENGTH_LONG).show();
                      }
                  }
              };
              //3.使用loop函数开启Looper
              Looper.loop();
          }
        };
        //开启该接收消息的线程
        reThread.start();

    }

    /**
     * 添加一个按钮,为按钮添加点击事件
     * 在该回调函数中向接收线程发送消息
     */
    public void sendMessageFromSendThread(View view){
        //1.创建Message对象
        Message sendMsg = new Message();
        //在Message的what字段中携带少量信息,用于消息的身份识别
        sendMsg.what = 0x123;
        //使用接收线程创建的Handler对象的sendMessage函数向接收线程发送消息
        mHandler.sendMessage(sendMsg);
    }
}
   

这里有一个需要注意的问题:
我们经常使用的场景中是主线程即UI线程最为接收消息的线程,然后更新UI来相应数据的变化,在这种情况下,Looper对象的创建和启动这两个步骤可以省略,因为android本身已经默认为主线程完成了Looper相关的工作,用户只要实现Handler相关的即可。具体我们后面再做分析。

源码分析

<p>

1.Looper类

主要变量

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 private static Looper sMainLooper;  // guarded by Looper.class

 final MessageQueue mQueue;
 final Thread mThread;

Looper构造器声明为private,不允许用户自己初始化Looper对象;自身携带了消息队列MessageQueue。

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

用户需要调用prepare函数来创建对象,可以看出prepare函数保证了一个线程中只有一个looper对象也就意味着只有一个MessageQueue消息队列。除此之外prepare函数还利用线程的ThreadLocal变量来存储该线程创建的Looper对象,使用这种机制来关联looper对象和他的调用线程。

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        //ThreadLocal为线程局部变量类
       //set方法设置线程的局部变量
        //get方法得到线程的局部变量
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

loop函数开启了一个死循环,不断的从消息队列中读取消息,并将消息交给Handler对象去处理。

  /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;
       //略去一部分
    
        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);
            }
       //分发给message的target字段包含的目标handler中。
            msg.target.dispatchMessage(msg);

       //略去一部分
        }
    }

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

总结Looper的作用:

2.Handler类

看源码中说明:

A Handler allows you to send and process Message and Runnable
objects associated with a thread's MessageQueue. Each Handler
instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue.

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.

Handler构造器有很多个,最终会调用到这两个。

   //没有传入looper对象时,使用Looper.myLooper()获得,前面提到过myLooper函数是从当前线程中取出绑定的looper对象。
   //然后获得该looper对象中的消息队列
   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;
    }
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

sendMessage相关的几个函数,最终会调用sendMessageAtTime这个函数。他把得到的消息Message对象和消息队列MessageQueue交给enqueueMessage函数处理。
enqueueMessage负责在消息Message的target字段标记为自己,以便接收到的消息能够分发到自己。
接下来交给MessageQueque的enqueueMessage()方法将消息添加到消息队列中。

 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);
    }

前面Looper对象取出消息队列中的消息后会根据msg.target调用该Handler的dispatchMessage函数。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        //如果msg本身包含callback,交给消息的callback去处理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {//如果没有,讲给Handler的callback去处理,也就是重写的handleMessage函数去处理。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

总结Handler的作用:

MessageQueue和Message这两个组成部分比较简单,在此不做详解。看完了以上的代码部分的分析,知道这个机制原理很简单,这样再回去看看我那个不准确的小例子,你会发现很多的不合理的之处,但是用来初步理解还是可以的。

上一篇下一篇

猜你喜欢

热点阅读