Android 消息机制问题总结

作者:fvaryu 时间:2023-08-06 03:10:49 

Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的。下面就来说说最基本的东西。

Looper

作用:

关联起Thread

循环取出消息

1、Looper是否可以直接实例化?

Looper构造方法是私有的,其中做了两件事

创建一个MessageQueue

得到与之对应的Thread


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

2、一个线程能对应多个Lopper?

不能,一个线程对应一个Looper对象,通过ThreadLocal保证一个线程只有一个Looper与之对应,如果多次调用Looper.prepare();则会抛出运行时异常。


private static void prepare(boolean quitAllowed) {
 if (sThreadLocal.get() != null) { // 查看是否有looper与当前线程对应
   throw new RuntimeException("Only one Looper may be created per thread");
 }
 sThreadLocal.set(new Looper(quitAllowed));
}

3、Looper是无限循环,会阻塞吗?

是,当开启一个loop后是一个死循环,从MessageQueue中取出消息,处理消息,但是也有可能退出,在没有消息后退出循环。


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;
   }
// 略
   msg.target.dispatchMessage(msg);
 }

4、可以再次调用Looper.prepareMainLooper吗?

不可以,Looper.prepareMainLooper最终也是调用prepare(),同2.


public static void prepareMainLooper() {
 prepare(false); // 创建一个Looper
 synchronized (Looper.class) {
   if (sMainLooper != null) {
     throw new IllegalStateException("The main Looper has already been   prepared.");
   }
   sMainLooper = myLooper();
 }
}

5、MainLooper什么时候创建的?

MainLooper是启动Activity创建ActivityThread(并不是一个Thread)时候创建,所以不能多次创建。


public static void main(String[] args) {
// 略
 Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// 略
ActivityThread thread = new ActivityThread();
thread.attach(false);
// 略
if (sMainThreadHandler == null) {
 sMainThreadHandler = thread.getHandler();
}

// 略
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

Handler

作用:

发送消息到MessageQueue

处理消息

1、Handler如何与Looper、MessageQueue关联起来?

我们知道一个Looper对应一个Thread,一个Looper包含一个MessageQueue。当我们创建Handler时就会从当前线程中取出与之对应的Looper,让后在从Looper中取出MessageQueue。


// 1、自动获取
public Handler(Callback callback, boolean async) {
// 略
 mLooper = Looper.myLooper(); // 取出当前线程中的Looper
 if (mLooper == null) {
   throw new RuntimeException(
     "Can't create handler inside thread that has not called    Looper.prepare()");
 }
 mQueue = mLooper.mQueue; // 取出MessageQueue
 mCallback = callback;
 mAsynchronous = async;
}
// 2、传递一个Looper进来
public Handler(Looper looper, Callback callback, boolean async) {
   mLooper = looper;
   mQueue = looper.mQueue;
   mCallback = callback;
   mAsynchronous = async;
 }

Message

单项链表结构。

作用:

数据的载体

1、消息如何复用的?

从全局消息池(链表结构)中


public static Message obtain() {
 synchronized (sPoolSync) {
   if (sPool != null) {
     Message m = sPool;
     sPool = m.next;
     m.next = null;
     m.flags = 0; // clear in-use flag
     sPoolSize--;
     return m;
   }
 }
 return new Message();
}

2、Message为什么能传递?

Android中想要传递对象要么实现Serializable要么Parcelable,在这里是实现了Parcelable接口。

public final class Message implements Parcelable {
  // 略
}

3、如何与Handler关联?

我们知道在消息传机制中Handler充当着“快递员”的角色,那么他又是如何与“货物”--Message * 呢?实际上Message有一个成员变量target他的类型正是Handler,


/*package*/ Runnable callback;

public int arg1;

public int arg2;

public Object obj;

/*package*/ Handler target; // 关键点

当我们通过Handler去send一个Message时候最终都会为target赋值为this,即当前的Handler。


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 msg.target = this; // 赋值语句
 if (mAsynchronous) {
   msg.setAsynchronous(true);
 }
 return queue.enqueueMessage(msg, uptimeMillis);
}

另为如果是通过Message.Obtain(),获取的复用Message也会为其赋值。

多说一句,Handler.obtainMessage()调用的就是Message.Obtain()。


public final Message obtainMessage(){
 return Message.obtain(this);
}

总结:

通过一系列的包涵关系,最终Looper、Handler、Message、MessageQueue即发生关联,从而形成一个闭合,开启消息循环。

Android 消息机制问题总结

困惑

最近一直在看这方面的知识,但是能力有限,还是有不少困惑,如果有错误,或你理解下面的问题请联系我fvaryu@qq.com,愿与君交流学习,谢谢

1、Message中的sPool,哪里初始化的?为什么Message.obtain()中不会抛异常?

2、ActivityThread并不是线程,为什么可以创建一个Looper,Main Thread什么时候创建?

3、为什么序列化了的对象就可以传递?与Binder有关?

4、MessageQueue对应的是NativeMessageQueue,具体实现需要学习?

5、Loop.loop(),会退出吗?退出时机是什么?如果会退出,那么主线程同样会退出吗?

标签:Android,消息机制
0
投稿

猜你喜欢

  • java使用RandomAccessFile类基于指针读写文件实例代码

    2021-07-29 17:45:45
  • 基于Fedora14下自带jdk1.6版本 安装jdk1.7不识别的解决方法

    2022-05-17 19:27:39
  • mybatis多个plugins的执行顺序解析

    2021-12-09 18:53:27
  • 使用异步方式调用同步方法(实例详解)

    2023-03-29 16:38:21
  • 解决idea中yml文件图标问题及自动提示失效的情况

    2021-06-08 14:59:42
  • 将Qt项目升级到Qt6吐血经验总结

    2023-08-22 08:08:30
  • 轻松学习C#的结构和类

    2023-12-10 13:46:19
  • 浅析C++中的函数与指针

    2022-03-13 11:04:28
  • 关于eclipse安装spring插件报错An error occurred while collecting items to be installed...解决方案

    2023-05-27 03:34:45
  • Spring Cloud 系列之服务调用 OpenFeign的实现

    2023-10-20 07:34:00
  • 详解Android应用main函数的调用

    2021-09-30 12:01:19
  • 解决IDEA springboot"spring-boot-maven-plugin"报红问题

    2023-08-15 21:29:19
  • 关于Android的 DiskLruCache磁盘缓存机制原理

    2022-12-16 16:57:43
  • SparkSQL开窗函数分析使用示例

    2022-04-16 02:26:32
  • Android UI设计系列之ImageView实现ProgressBar旋转效果(1)

    2021-06-07 11:08:21
  • MyBatis持久层框架的用法知识小结

    2022-05-21 17:12:55
  • Android下Activity全屏显示实现方法

    2022-12-17 19:57:56
  • Java 匿名内部类详解及实例代码

    2021-09-13 13:03:34
  • java判断三位数的实例讲解

    2023-07-06 08:29:53
  • C#嵌套类的访问方法

    2021-07-26 18:03:20
  • asp之家 软件编程 m.aspxhome.com