面试时说的Android消息机制
来源:互联网 发布:冲压模具设计软件 编辑:程序博客网 时间:2024/06/14 04:27
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/enhtmlclip/Image.png)
MessageQueue
消息队列,但是内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
Looper
由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。
Handler
主要作用是将一个任务切换到某个指定的线程中去执行。
系统之所以提供Handler,主要原因就是为了解决在子线程中无法访问UI的矛盾。
系统为什么不允许在子线程中访问UI呢?
- 因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控 件处于不可预期的状态。
那竟然不安全,为什么系统不对UI控件的访问加上锁机制呢?
- 加锁后会有2个缺点:首先加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。
第一步:Handler发消息msg(在子线程中发送message,在主线程中创建Handler)
创建Handler完毕后,
通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,
也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。
其实post方法最终也是通过send方法来完成。
步骤流程: sendMessage(msg)-->sendMessageDelayed(Message msg, long delayMillis)-->sendMessageAtTime(Message msg, long uptimeMillis)-->enqueueMessage(Message msg, long when)
这里的enqueueMessage方法是调用了MessageQueue的enqueueMessage方法
//1.发消息handler.sendMessage(msg);//发消息public final boolean sendMessage(Message msg){ //2 sendMessageDelayed return sendMessageDelayed(msg, 0);}//2 sendMessageDelayedpublic final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } //3 sendMessageAtTime return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}//3 sendMessageAtTimepublic boolean sendMessageAtTime(Message msg, long uptimeMillis){ boolean sent = false; //this 就是 handler mQueue 就是从Looper中取出来的消息队列 MessageQueue queue = mQueue; if (queue != null) { //handler发消息 把当前的handler对象绑定到Message对象中 msg.target = this; // 把消息放到消息队列里,排序 ,如果你调用sendMessage(msg),uptimeMillis = 0 //4 enqueueMessage sent = queue.enqueueMessage(msg, uptimeMillis); } 。。。 return sent;} //4 enqueueMessagefinal boolean enqueueMessage(Message msg, long when) { final boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } else if (msg.target == null) { mQuiting = true; } msg.when = when; //Log.d("MessageQueue", "Enqueing: " + msg); //mMessages 是MessageQueue对象里的属性 Message p = mMessages; if (p == null || when == 0 || when < p.when) { //msg 发的消息 msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } if (needWake) { nativeWake(mPtr);//jni } return true;}
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/enhtmlclip/214918_USvs_174429.png)
消息队列在Android中指的是MessageQueue, MessageQueue主要包含两个操作插入和读取
插入 对应的方法为enqueueMessage
- enqueueMessage的作用是往消息队列中插入一条消息
读取 对应的方法为next
- next的作用是从消息队列中取出一条消息并将其从消息队列中移除
从enqueueMessage的实现来看,它的主要操作其实就是单链表的插入操作。
打插入消息的动作并不复杂,无非是在消息链表中找到合适的位置,插入Message节点而已。因为消息链表是按时间进行排序的,所以主要是在比对Message携带的when信息。消息链表的首个节点对应着最先将被处理的消息,如果Message被插到链表的头部了,就意味着队列的最近唤醒时间也应该被调整了,因此needWake会被设为true,以便代码下方可以走进nativeWake()。
从next的实现来看,它是一个无限循环的方法,如果消息队列中没有信息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。
第三步:当Handler发送过来的消息message打入到消息队列后,Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法就会被调用。
Looper(轮询器),会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/enhtmlclip/212125_vxi5_174429.png)
* ActivityThread类中初始化,应用程序运行之前执行,main方法如下: //主线程 public static final void main(String[] args) { 。。。 //轮询器的初始化 Looper.prepareMainLooper(); 。。。 //轮询器开始取消息 Looper.loop();}
* loop方法
public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while (true) { //没有消息阻塞 Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); if (me.mLogging!= null) me.mLogging.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("Looper", "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(); } }}
Looper往消息队列中取消息,其中loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null
public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while (true) { //没有消息阻塞 //从消息队列里取消息头 Message msg = queue.next(); // might block if (msg != null) { ... 打印日志 。。。 处理消息 msg.target.dispatchMessage(msg); 。。 回收消息 msg.recycle(); } }} 处理消息 msg.target.dispatchMessage(msg);public void dispatchMessage(Message msg) { if (msg.callback != null) {//runOu...... //处理runOnUithread方法 //1 handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //调用handleMessage 主线程 handleMessage(msg); }}//1. handleCallback(msg);//2.private final void handleCallback(Message message) { message.callback.run();//runnable 跟线程没关系}
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/enhtmlclip/215548_4LpP_174429.png)
- 面试时说的Android消息机制
- Android面试中消息机制解答
- Android消息机制浅析——面试总结
- Handler消息机制--面试篇
- 面试时绝对不能说的 13 句话
- 面试时绝对不能说的13句话
- 面试时绝对不能说的 10 句话
- 面试时绝对不能说的13句话
- Android消息机制(Handler机制)
- android Handler机制 消息机制
- Android 消息处理机制
- Android消息机制
- android 消息机制
- Android消息机制(一)
- Android消息处理机制
- Android消息处理机制
- Android消息机制(一)
- Android 中的消息机制
- 如何让一个ListVIew按类型显示多种Item视图
- 文件的读取写入
- Java设计模式—代理模式
- 欢迎使用CSDN-markdown编辑器
- Leetcode 349. Intersection of Two Arrays
- 面试时说的Android消息机制
- 网络流24题——太空飞行计划问题
- IntelliJ IDEA - 热部署插件JRebel 安装使用教程
- 使用UITextView想看下视图层次出错,解决方法
- iOS UITableViewCell 的 imageView大小更改
- 【置顶】Android启动页面有白屏(或者黑屏)过渡解决方案
- 消息推送
- HHU Kingdom of Black and White(暴力搜索+线段树)
- Android 使用Gson解析Json数据