Android 的消息机制
来源:互联网 发布:电视直播软件安装包 编辑:程序博客网 时间:2024/06/03 11:17
第一次写博客,其实也不算自己写的,算是对 [Android开发艺术探索] 这本书的 一个笔记吧.下面的内容 全部来自于这本书,就当自己加深印象,总觉归纳.
Android 的消息机制
android的消息机制,主要是指Handler的运行机制
- Handler底层
- MessageQueue 消息队列
- 它内部存储了一组消息,以队列的形式对外提供插入和删除工作
- 虽然叫队列,但是它是单链表结构存储
- Looper 轮询器
- 它是消息的无限循环.它会一直以无限循环的方法去查看有没有新的消息,如果有就去处理消息,没就一直循环等待
- 线程默认是没有Looper的,如果要使用Handler,就必须创建它.主线程也就是UI线程他是ActivityThread,在ActivityThread中会初始化Looper,这就是主线程可以使用Handler的原因.
- ThreadLocal 它不是线程,它的作用是在每个线程中存储消息.它可以在不同的线程互不干扰地存储数据,通过ThreadLocal可以获得每个线程的Looper
- MessageQueue 消息队列
简要概述
android的消息机制主要是指Handler的运行机制和它所附带的MessageQueue与Looper的工作过程.而平时开发主要只使用了Handler的上层接口.
- 主线层 UI线程
- 可以进行UI更新,但是不能进行耗时操作
- 子线程
- 可以进行耗时操作,但是不能更新,修改UI
- 子线程操作更新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."); } }
为什么系统不允许子线程中访问UI
- 因为Android的UI控件不是线程安全
- 为什么不安全?
- 如果加锁变安全,会让UI访问逻辑变得想当复杂
- 降低了UI访问的效率,锁会阻塞某些线程执行
所以在更新UI和主线程ANR上,使用到Handler
Handler工作
- 创建完毕后,内部的Looper和messageQueue已经开始协同工作了
- 通过Handler的post方法将一个Runnable投递到Handler内部的Looper;也可以通过send方法发送一个消息到Looper.(其实post方法最终也是send方法完成)
- 当send方法调用时,会调用MessageQueue的enqueueMessage方法翻入到消息队列中,让looper轮询到时,会去处理这个消息.
- 最终消息中的Runnable或者Handler的handlerMessage方法调用.Looper是运行在Handler的线程中.
ThreadLocal的工作原理
Threadlocal 是一个线程内部的数据存储类
- 它可以在指定的线程中存储,数据存储以后,只能在指定线程中读取,其它线程无法获取.
- Looper,ActivityThread,AMS中用到ThreadLocal
开发中使用:多个线程用到一个数据,但是每个线程需要独立的值,或者说独立的域.比如监听器的传递.还有Handler获取looper.
案例
final ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>(); mThreadLocal.set(true); Log.i(TAG, "run: " + mThreadLocal.get());//true new Thread("Thread1") { @Override public void run() { mThreadLocal.set(false); Log.i(TAG, "run: " + mThreadLocal.get());//false } }.start(); new Thread("Thread 2") { @Override public void run() { Log.i(TAG, "run: " + mThreadLocal.get());//null } }.start();
上诉案例 在主线中new出一个ThreadLocal<Boolean> 泛型为boolean类 然后在主线中给他设置一个true值 然后打印log get值true再new 出第二个线程 设置一个fslae 然后打印log get值为false有new 出第三个线程 不设置值 然后打印log get值为null
** 通过上面的测试结果 它们共同使用一个Threadlocal 但是都有自己的域 自己的值
其它线程无法获取,无法修改 **
原码分析<开发艺术> 377页
消息队列的工作原理
android的消息队列是指MessageQueue
名称虽然叫队列,但数据结构是链表,链表在插入与删除的效率有优势
- 插入
- enqueueMessage方法
- 在消息队列的链表中插入一条消息
- 读取
- next方法
- 读取消息队列中的一条信息,并把它从消息队列的链表中移除
- 它是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里,当有新的消息到来是就执行next方法.
原码分析<开发艺术> 380页
Looper的工作原理
Looper在Android的消息机制中扮演着消息循环的角色.具体来说它会不停地从MessageQueue中查看是否有新的消息,有则立即处理,没有则一直阻塞在那里,查看它的构造方法中会创建Message
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
如何在线程中获得Looper和开启Looper
Handler的工作需要Looper,没有Looper线程就会报错,可以通过Looper.perpare()方法创建一个Looper,再使用Loop.loop();开启消息循环.
new Thread() { @Override public void run() { Looper.prepare(); Looper.loop(); } }.start();
Looper的perpareMainLooper()方法
public static void prepare() { prepare(true); } 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)); }
这个方法是给主线程创建Looper使用的,本质也是通过prepare方法来实现.它在存在是为了在任何地方获得主线程的Looper,使用getMainLooper.
Looper的quit()方法和quitSafely()方法
//Looper中的方法public void quit() { mQueue.quit(false); }//MessageQueue中的方法 void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
- quit() 退出
- 直接退出Looper
//Looper的方法public void quitSafely() { mQueue.quit(true); }
- quitSafely() 安全退出
- 只是设置一个退出标记,只有所以消息全部处理完毕以后,才会退出.
- Looper退出后,通过handler发送消息会失败,这是handler的send()方法会返回一个false.如果是子线程创建出来的Looper,就应该在所以消息处理完毕后调用quit()来终止消息循环,不然子线程会一直在阻塞等待状态.只要终止Looper,线程就会立即终止.
Looper的loop()方法
/** * 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; // 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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.recycleUnchecked(); } }
- loop()方法是开启Looper的轮询,也就是真正启动Looper
- loop()方法就是一个死循环,跳出循环的方式是MessageQueue的next()方法返回一个null.
退出Looper
当Looper调用quit()方法调用时,Looper就会调用MessageQueue的quit()方法和quitSafely()方法,来通知消息退出队列 使得next()返回一个null.否则Looper就一直循环下去
处理消息Looper
当next得到了新的消息,Looper就会 msg.target.dispatchMessage(msg) ,实质上是msg.target就是发送消息的对象 对象调用了dispatchMessage(msg)这样的一个方法,特别就是这个对象调用的方法正在Looper中执行.就打到了将代码逻辑换到指定的线程执行.
Handler的工作原理
- Handler 主要是进行发送/接受消息(处理消息)
发送消息
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }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); }
看到以上代码 发送消息就是send系列方法层层调用,post一系列方法也是通过send来实现的. 整个过程其实就是往消息队列中插入了一条消息.
处理消息
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } public interface Callback { public boolean handleMessage(Message msg); }
- 收到消息交给dispatchMessage()进行分发
- 判断msg.callback是否为null //callback就是Runnable对象 Handler通过post传入的Runnable参数
- 如果不为null 就直接调用 handleCallback(msg);
- 如果为null 在判断本类的 mCallback 是否为null
- 不为空就 调用mCallback.handleMessage(msg) 其实这个方法相当于 平时我们创建一个Handler 重写handlerMessage来处理消息
- 最后调用重写handlerMessage来处理消息
- 判断msg.callback是否为null //callback就是Runnable对象 Handler通过post传入的Runnable参数
主线程的消息循环
android的主线就是ActivityThread
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
ActivityThread类中有main方法在main方法中调用了Looper.prepareMainLooper()和Looper.loop()来创建Looper和开启looper在ActivityThread类中还有一个名为H内部类 他就是Handler 内部定义了一组消息类型 包含了四大组件的启动和停止过程.
1 0
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android 的消息机制
- Android的消息机制
- Android 的消息机制
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android的消息机制
- Android 的消息机制
- Android的消息机制
- Android的消息机制
- android的消息机制
- Android的消息机制
- Mybatis简介与原理
- 关于重绘(repaint)和回流(reflow)的理解
- c++中vector的用法详解
- Android布局管理器 - 详细解析布局实现
- 使用Graphics类的DrawImageUnscaled时的发现
- Android 的消息机制
- vim编辑器(二)
- 谷歌卫星地图下载器
- 【DSP开发】【并行计算-CUDA开发】TI OpenCL v01.01.xx
- 打印出如下图案(菱形)
- Android 常用方法xml大全
- POJ【4047】——Problem D. Garden 线段树
- nyoj67
- HDU 2841 Visible Trees (容斥原理好题)