Android开发艺术探索(十)
来源:互联网 发布:js 移动端 上传照片 编辑:程序博客网 时间:2024/06/07 10:21
Andrid的消息机制
消息机制概述
Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。
Handler的主要作用是将一个任务切换到某个指定的线程中去执行。
Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,程序就会排除异常。ViewRootImpl对UI操作做了验证,这个验证工作是由ViewRootImpl的checkThread方法来完成的。
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
Android系统不允许在子线程中访问UI?
这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。
为什么系统不对UI控件的访问加上锁机制呢?
1、加上锁机制会阻塞某些线程的执行
2、锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。
鉴于这两个缺点,最简单高效的方法就是采用单线程模型来处理UI操作,这个时候我们只需要使用Handler切换UI访问的执行线程即可。
Android的消息机制分析
ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,存储数据以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
我们来做一个例子:首先定义一个ThreadLocal对象,这里选择Boolean类型的。如下:
private ThreadLocal<Boolean> mBooleanThreadLocal=new ThreadLocal<>();
然后分别再主线程、子线程1、子线程2中设置和访问它的值,代码如下:
mBooleanThreadLocal.set(true); Log.e(TAG,"{Thread#main}mBooleanThreadLocal="+mBooleanThreadLocal.get()); /**thread1*/ new Thread(){ @Override public void run() { mBooleanThreadLocal.set(false); Log.e(TAG,"{Thread#1}mBooleanThreadLocal="+mBooleanThreadLocal.get()); } }.start(); /**thread2*/ new Thread(){ @Override public void run() { Log.e(TAG,"{Thread#2}mBooleanThreadLocal="+mBooleanThreadLocal.get()); } }.start();
我们来分析一下ThreadLocal的内部实现:
/**泛型类*/public class ThreadLocal<T> {/**get方法获取值*/ public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }/**set方法设置值*/ public void set(T value) { //获取当前运行的线程 Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }}
从ThreadLocal的get和set方法中可以看到他们所操作的对象都是当前线程的localValues对象的table数组,因此在不同的线程中访问同一个ThreadLocal的set和get方法,他们对ThreadLocal所做的读/写操作仅限于个子线程的内部!所以ThreadLocal可以在多个线程中互不干扰的存储和修改数据。
消息队列的工作原理
消息队列在Android中指的是MessageQueue,MessageQueue包含两个操作:插入和读取。读取操作本身会伴随着删除操作。
插入的方法为:enqueueMessage,作用是往消息队列中插入一条消息。
读取的方法为:next,作用是从消息队列中读取出一条消息并将其从消息队列中移除。
MessageQueue内部实现是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。
MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) { .... synchronized (this) { .... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; }
MessageQueue的next方法:
Message next() { final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } .... } .... } }
我们发现next中有一个无线循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里,当有新消息到来时,next方法会返回这条消息并将其从单链表中删除。
Looper的工作原理
Looper在Android的消息机制中扮演着循环的角色,具体来说就是它会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在哪里。
Looper的构造方法:
/**在构造方法中会创建一个MessageQueue消息队列*/ private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
通过Looper.prepare()为当前线程创建Looper对象,然后通过Looper.loop();开启消息循环。
new Thread(){ @Override public void run() { Looper.prepare(); Handler handler=new Handler(); Looper.loop(); } }.start();
Looper中还提供了一个prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的。
同样Looper也是可以退出的:Looper提供了quit和quitSafely来退出一个Looper,他们的区别:
quit会直接退出Looper
quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false,同样在不需要Looper时,应该调用quit方法来终止消息循环,否则这个子线程会一直处于等待的状态。
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; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); /**这里一个死循环*/ for (;;) { /**通过MessageQueue的next方法获取到消息*/ Message msg = queue.next(); // might block if (msg == null) { /**如果返回的消息是null,则跳出循环*/ return; } /**如果消息不为空,则进行消息的处理*/ msg.target.dispatchMessage(msg); final long newIdent = Binder.clearCallingIdentity(); msg.recycleUnchecked(); } }
Handler的工作原理
Handler的工作主要包含消息的发送和接收过程,消息的发送可以通过post的一系列方法以及send的一系列方法来实现,post的一系列方法最终是通过send的一系列方法来实现的。
我们来看一下Handler的sendMessage方法:
/**调用了sendMessageDelayed方法*/ public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } /**这里又调用了sendMessageAtTime方法*/ 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; } /**这里又调用了enqueueMessage方法*/ return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } /**这里调用了MessageQueue的enqueueMessage方法*/ return queue.enqueueMessage(msg, uptimeMillis); }
如图流程:
然后我们再来看处理消息:Looper对象将消息返回给Handler,Handler使用dispatchMessage去处理消息。
public void dispatchMessage(Message msg) { //检查Messge的callback是否为null,不为null就通过handleCallback来处理消息 if (msg.callback != null) { handleCallback(msg); } else { /**然后再这里判断CallBack是否为空,不为null就调用mCallback的handlerMessage来处理消息*/ if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这里呢:msg.callback()是一个Runnable对象。
private static void handleCallback(Message message) { message.callback.run(); }
mCallBack是一个接口:
public interface Callback { public boolean handleMessage(Message msg); }
通过Callback 可以采用如下方式创建Handler对象:
Handler handler=new Handler(callback);
通过CallBack创建一个Handler的实例,并且不需要派生Handler的子类。
Handler还有一个特殊的构造方法,通过Looper来构造Handler来构造。当looper为空时,就会抛出异常。这也解释了在没有Looper的子线程中创建Handler会引发程序异常的原因。
public Handler(Looper looper){this(looper,null,false);}
主线程的消息循环
Android的主线程就是ActivityThread,主线程的入口为main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()俩开启主线程的消息循环。
public static void main(String[] args) { .... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
private class H extends Handler { public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104; public static final int SHOW_WINDOW = 105; ...... }
ActivityThread通过Application和AMS进行进程间通讯,AMS以进程间通讯方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发消息,H收到消息后会将ApplicationThread中的逻辑切换到ACtivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
- Android开发艺术探索(十)
- Android开发艺术探索
- Android 开发艺术探索读书笔记
- 《Android开发艺术探索》读书笔记
- 【读书笔记】Android开发艺术探索
- 《Android开发艺术探索》读书笔记
- Android开发艺术探索1
- Android开发艺术探索2
- Android开发艺术探索3
- Android开发艺术探索(九)
- Android开发艺术探索(十一)
- Android开发艺术探索(十二)
- 《Android 开发艺术探索》读书笔记
- 《Android开发艺术探索》笔记
- Android 开发艺术探索 读书笔记
- 《Android开发艺术探索》笔记
- Android 开发艺术探索 第一章
- Android开发艺术探索-Drawable
- 常用正则表达式大全
- angularjs的作用域(ng-repeat 和 ng-controller)
- _REQUEST、$_POST、$_GET
- 修改Android签名证书keystore的密码、别名alias以及别名密码
- 数据结构二叉树个人总结
- Android开发艺术探索(十)
- POJ--Kaka's Matrix Travels(最大费用最大流 && 经典建图 && 好题)
- 面试ArrayList 和 Vector 的区别,HashMap 和Hashtable的区别
- 网页访问计数器
- Neural style之torch学习笔记4:学习神经网络包的用法(2)
- poj 2774 最长公共子串(后缀数组)
- Linux文件压缩
- Git 使用备忘录
- 关于spl_autoload_register()