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 开发者必须知道的消息机制问题总结的更多相关文章

  1. (转载)Android开发者必知的开发资源

    (转载)http://www.importnew.com/3988.html 随着Android平台市场份额的持续猛增 ,越来越多的开发者开始投入Android应用程序的开发大潮.如果您是一位2013 ...

  2. Android 面试收集录5 消息机制

    1.消息机制概述 1.1.消息机制的简介 在Android中使用消息机制,我们首先想到的就是Handler. 没错,Handler是Android消息机制的上层接口. Handler的使用过程很简单, ...

  3. android 进程/线程管理(四)----消息机制的思考(自定义消息机制)

    关于android消息机制 已经写了3篇文章了,想要结束这个系列,总觉得少了点什么? 于是我就在想,android为什么要这个设计消息机制,使用消息机制是现在操作系统基本都会有的特点. 可是andro ...

  4. [Android]简略的Android消息机制源码分析

    相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...

  5. 深入理解 Handler 消息机制

    记得很多年前的一次面试中,面试官问了这么一个问题,你在项目中一般如何实现线程切换? 他的本意应该是考察 RxJava 的使用,只是我的答案是 Handler,他也就没有再追问下去了.在早期 Andro ...

  6. Android开发系列之事件拦截机制

    对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...

  7. [转载]Android开发者必须深入学习的10个应用开源项目

    [转载]Android开发者必须深入学习的10个应用开源项目 原文地址:Android开发者必须深入学习的10个应用开源项目(http://blog.sina.com.cn/s/blog_7b8a63 ...

  8. Android 高级UI设计笔记08:Android开发者常用的7款Android UI组件(转载)

    Android开发是目前最热门的移动开发技术之一,随着开发者的不断努力和Android社区的进步,Android开发技术已经日趋成熟,当然,在Android开源社区中也涌现了很多不错的开源UI项目,它 ...

  9. 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...

随机推荐

  1. System.InvalidOperationException:This.NpgsqlTransaction has completed;it is no longer usable. at Npgsql.NpgsqlTransaction.CheckCompleted() in ...

    关于报此异常,原因是事务不能循环提交,一个方法中事务只能提交一次. System.InvalidOperationException:This.NpgsqlTransaction has comple ...

  2. 关于async function(){ let res = await } 详解

    本文引自: https://www.jianshu.com/p/435a8b8cc7d3 async function fn(){ //表示异步,这个函数里面有异步任务 let result = aw ...

  3. JS基础语法---函数作为参数使用---回调函数

    1. 函数可以作为参数使用, 如果一个函数作为参数, 那么我们说这个参数(函数)可以叫回调函数 2. 只要是看到一个函数作为参数使用了, 那就是回调函数 function sayHi(fn) { co ...

  4. Linux 释放cache

    sysc 将所有未写的系统缓冲区写到磁盘中,包含已修改的 i-node.已延迟的块 I/O 和读写映射文件 echo 3 > /proc/sys/vm/drop_caches To free p ...

  5. SQL Server(MSSQLSERVER) 请求失败或服务未及时响应,有关详细信息,请参见事件日志或其他的适用的错误日志。

    转自:https://www.fengjunzi.com/blog-25573.html 问题 有时候sqlserver无法启动了,原因是mssqlserver服务没有启动,当你手动启动时,又出现服务 ...

  6. CSS绘制三角形和箭头,不用再用图片了

    前言 还在用图片制作箭头,三角形,那就太lou了.css可以轻松搞定这一切,而且颜色大小想怎么变就怎么变,还不用担心失真等问题. 先来看看这段代码: /**css*/.d1{ width: 0; he ...

  7. CodeForces - 1230E(dfs+数论+树上贡献)

    题意 https://vjudge.net/problem/CodeForces-1230E 给出一棵树, 点上有权值, 对于每个点, 求它和所有后代节点的GCD的和. 思路 对于一个点,他对后代的贡 ...

  8. echarts自定义颜色主题

    1. 进入地址:  https://echarts.baidu.com/theme-builder/ 2. 配置主题 2.1. 可以选择挑选默认方案 2.2 可以进行一些样式配置 2.3 配置背景颜色 ...

  9. Python函数名做参数,闭包,装饰器

    简单讲解闭包的写法和应用,在这之前,先声明,你定义的任意一个函数都可以作为其他函数的参数.就像下面这段代码的参数func,接收的参数就是一个函数名,在函数体内部使用了func()调用执行函数. 请看下 ...

  10. 在 VS 中查看所有线程

    查看当前所有线程 在程序中打断点,然后启动调试(注意:线程窗口需要启动调试才可以看得到)点击 Debug > Windows > Threads 线程窗口如下: