您的位置:首页 > 移动开发 > Android开发

Android 消息处理机制

2016-01-31 20:38 519 查看
本文原创,转载请注明地址。

作为一个软件工程大二的学生党,这篇文章或许有不对的地方,欢迎大家指正。我在学习EventBus的时候,里面的机制和Android
Handler
+
Looper
+
Message
类似所以就写了这篇博文,不禁感叹到Google的设计。

一:从用法说起

下面一段代码,点击一个Button,开启一个新线程,着这个线程里面do something,然后通知
Handler
来更新UI。

[code]import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;

/**
 * 在Work Thread里面通知UI Thread更新UI
 */
public class MainActivity extends AppCompatActivity {

    Button mBtn;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mBtn.setText("Change");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn = (Button) findViewById(R.id.mBtn);
        mBtn.setOnClickListener(v -> {
            //开启一个WordThread
            new Thread(() ->
            {
                //通知UI Thread更新
                Message msg = mHandler.obtainMessage();
                msg.sendToTarget();
            }).start();
        });
    }
}


看似简单几行代码,背后却有巧妙地实现,下面一起来看看。

二 :
Looper

何为
Looper
: Google文档这样说:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.

还给了以下的例子

[code]class LooperThread extends Thread {
        public Handler mHandler;
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
           Looper.loop();
        }
    }


Looper线程,就是可以存在一个消息循环的线程,这个线程一直工作,有任务执行时,执行完成一个任务之后,继续指向下一个任务。通过上述Demo,一个普通的线程就可以变为Looper线程。

看下面的图:(CAD画的,同学有什么好的画图软件可以推荐给我。。。)




咱们看看源码

[code]    /*Looper内部就是一个ThreadLocal,ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。其内部原理是通过生成一个它包裹的泛型对象的数组,在不同的线程会有不同的数组索引值,通过这样就可以做到每个线程通过 get() 方法获取的时候,取到的只能是自己线程所对应的数据*/
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;//主线程Looper
    final MessageQueue mQueue; //Looper维护的消息队列
    final Thread mThread; //当前线程

    /**
     *初始化Looper,该方法必须在loop()方法之前
     */
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * 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(); //得到当前线程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //得到当前Looper管理的消息队列

        // 看不懂。。。
        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;
            }

            // Log
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            //将真正的处理共工作交给target 也就是Handler对象
            msg.target.dispatchMessage(msg);

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

            // 好像也是Log
            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.recycleUnchecked(); //回收Messages对象
        }
    }


Looper还有以下方法

[code] /**
  * 返回当前线程的Looper
  */
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

 /**
  * 返回应用程序主线程的Looper
  */
 public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

 /**
   * 返回当前Looper所属线程
   */
 public @NonNull Thread getThread() {
       return mThread;
 }

 /**
   * 返回当前Looper所管理的MessageQueue
   */
 public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
 }


注意:

一个线程有且只有一个
Looper


Looper
内部维护一个
MessageQueue
(后面会说到)。

Looper.prepare()
要在
Looper.loop()
之前执行。

三:
MessageQueue
Message(比较简单,可直接跳过)

还是先看看Google文档:

Message


Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases. (一个Message对象包括任意的数据,并且可以发送给Handler,并且有what,arg1,arg2,obj 成员变量可以使用)

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.(不推荐使用构造函数创建对象,而是使用Message.obtain()与Handler.obtainMessage()从池中获得)

需要注意以下几点:

Message
有个成员变量target,为一个
Handler
对象。

Message
内部维护一个Pool,使用
Handler.obtainMessage()
Message.obtain()
可以提升性能,2种方法其实是一样的(下面代码解释)。

[code]//Message.obtain()方法
 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool; //sPool就是所说的那个池啦
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }


[code]//Handler.obtainMessage()
public final Message obtainMessage()
{    
    //还是在调用Message.obtain(),最终还是在调用上面那个方法,并且把Message的target对象设置为当前Handler对象,即调用obtainMessage()的Handler
    return Message.obtain(this);
}


MessageQueue
:

Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.(MessageQueue是一个存储Message的容器类,并且被Looper所管理(dispatched ),但是一般不直接操作MessageQueue,而是通过Handler与Looper)

MessageQueue
就是一个消息队列,里面存放
Message
,源码我也没怎么看懂,指导是一种数据结构就好了。

四:
Handler
(重点咯)

更具本文第一开始给的例子,看一下Handler怎么工作的:

发送消息:Word Thread里面调用
msg.sendToTarget()


接受消息:在
handleMessage()
方法更新了UI

下面一步步跟踪源码来理解:

Step1: 不管是使用
msg.sendToTarget()
,
mHandler.sendMessage
都会调用下面这个方法
sendMessageAtTime()


[code]public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue; //当前线程的Looper所维护的消息队列
        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);
    }


Step2:将
Message
加入到了
MessageQueue
里面,就开始由
Looper
来继续管理了。
Looper.loop()
里面有个很重要的一行代码:

[code]msg.target.dispatchMessage(msg);


对,警察叔叔,快抓住它,上文说到msg.target是一个
Handler
对象。在
Handler.dispatchMessage
里面有个Hook,最终在
handlerMessage
进行真正的消息处理。

[code]public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //如果msg设置了Callback
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //如果当前handler设置了Callback
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

private static void handleCallback(Message message) {
        message.callback.run();
    }

 public void handleMessage(Message msg) {
    }


Handler
的一些补充:

Handler.post(Runnable r)
Handler.sendMessage()
实则是一样的,只是把
Runnable
对象封装为
Message
了。

[code]private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }


Handler
可以在任意一个线程里面创建,并且把
Message
添加到自己关联的
Looper
里面。

用一张图表示一哈:



最后,本文开始的代码就好理解了,在主线程里面创建一个
Handler
,所以
Handler
发送的
Message
都在UI线程里面,在其他线程里面取得一个
Handler
的引用,就可以实现不同线程的通信了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: