从通信机制角度看应用启动过程
从通信机制角度看应用启动过程.png首先,让我们结合通信机制,来看看通过Launcher启动App的过程(假设为冷启动)。结合上图,其完整流程如下:
- Launcher 通过所持有的 AMS 的 Binder 调用 AMS 的
startActivity()
方法 → Binder 通信 - AMS 检查目标 App 进程是否已启动,若没有启动,则向 Zygote 进程发送创建新进程的请求 → Socket 通信
- Zygote 进程 fork 一个子进程(即 App 进程),之后并非立即启动,而是经历了以下流程:
- 创建 App 进程的 Binder
- 通过反射创建 App 进程入口函数(main)的 Method 对象,并返回给上层调用方
- App 进程启动过程中,将自己的 binder 反向 attach 到 AMS 中管理起来 → Binder 通信
- AMS 通过 App 的 binder 执行其 Activity 的启动流程 → Binder 通信
- 当进入到 App 进程内部后,之后的通信就可以通过 Handler 进行了
如何保证SystemServer进程一直活跃
SystemServer 是由 Zygote 进程创建的第一个进程,并且由 Zygote 反射调用其 main() 方法启动。
// SystemServer.java
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
// Prepare the main looper thread (this thread).
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
Looper.prepareMainLooper();
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
...
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,在其 run() 方法中,有一个 looper ,并且是 MainLooper ,其中的无限循环,就是为了让进程一直活跃。也就是说,Looper 的价值就是让进程一直活跃。
App进程启动时,在 ActivityThread 的 main() 方法中,也会启动一个 MainLooper ,这两个 Looper 有什么关系?
没有关系,它们根本就是两个不同进程的 MainLooper 。
Activity 的生命周期,都是运行在主线程上,事实上,每一个生命周期的代码,都是属于某一个 Message。为什么这么说呢,我们从 Activity 的启动来分析:
Activity详细启动流程.pngAMS 调度 Activity 的过程,最终会走到 realStartActivityLocked() 中。
Activity启动流程.png最终又走到了 ApplicationThread 的 scheduleTransaction() 中。
// ActivityThread.java
final H mH = new H();
...
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
// 会走到成员变量 mH 这个 Handler 的 EXECUTE_TRANSACTION 中
ActivityThread.this.scheduleTransaction(transaction);
}
class H extends Handler {
...
public static final int EXECUTE_TRANSACTION = 159;
...
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
...
case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
...
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
...
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
...
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
上面的 mTransactionExecutor.execute(transaction); 说明它就是一个消息。
为什么Acitivyt、Service生命周期都运行在主线程上,设计目的是什么?
统一规划UI的展示。
子线程:handler → sendMessage() → MessageQueue.enqueueMessage() 入队列
主线程:Looper.loop() → MessageQueue.next() → handler.dispatchMessage()
子线程将数据封装成一个 Message 对象,send 到主线程内部的 MessageQueue 中,等待被执行。
应用的启动入口 ActivityThread.main() ,做的两件最重要的事就是:将应用自身的 binder attach 到 AMS 中,以及启动主线程的 Looper。
// ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// 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();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Call per-process mainline module initialization.
initializeMainlineModules();
Process.setArgV0("<pre-initialized>");
// 准备 MainLooper
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
// 将应用自身的 binder attach 到 AMS 中
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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);
// 启动 MainLooper
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Handler 机制源码分析
Android的消息处理机制主要分为四个部分:
- 创建消息队列
- 消息循环
- 消息发送
- 消息处理
主要涉及三个类:
- MessageQueue
- Looper
- Handler
创建消息队列
整个创建过程涉及到两个类:MessageQueue
和 Looper
。它们在C++层有两个对应的类:NativeMessageQueue
和 Looper
。
// Looper.java
// 注意,这个 sThreadLocal 是静态的
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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");
}
// 将一个Looper对象保存在ThreadLocal里面
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// 新建一个MessageQueue对象
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// 通过JNI初始化C++层的NativeMessageQueue对象
mPtr = nativeInit();
}
private native static long nativeInit();
创建过程如下所示:
- Looper的prepare或者prepareMainLooper静态方法被调用,将一个Looper对象保存在ThreadLocal里面。
- Looper对象的初始化方法里,首先会新建一个MessageQueue对象。
- MessageQueue对象的初始化方法通过JNI初始化C++层的NativeMessageQueue对象。
- NativeMessageQueue对象在创建过程中,会初始化一个C++层的Looper对象。
- C++层的Looper对象在创建的过程中,会在内部创建一个管道(pipe),并将这个管道的读写fd都保存在mWakeReadPipeFd和mWakeWritePipeFd中。
然后新建一个epoll实例,并将两个fd注册进去。 - 利用epoll的机制,可以做到当管道没有消息时,线程睡眠在读端的fd上,当其他线程往管道写数据时,本线程便会被唤醒以进行消息处理。
总结得出,其关系如下图所示:
+------------+ +------+
|MessageQueue+----^+Looper|
+-----+------+ +------+
|
|
|
+-----------+------+ +------+
|NativeMessageQueue+^----+Looper|
+------------------+ +------+
A----^B表示B中保存A的引用
消息循环
// Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 调用 MessageQueue.next() 方法读取 Message,该方法会堵塞线程直到有消息到来为止
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// 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);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 通过 Handler.dispatchMessage() 分发消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
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();
return true;
}
消息循环过程如下:
- 首先通过调用Looper的loop方法开始消息监听。loop方法里会调用MessageQueue的next方法。next方法会堵塞线程直到有消息到来为止。
- next方法通过调用nativePollOnce方法来监听事件。next方法内部逻辑如下所示(简化):
- 进入死循环,以参数timout=0调用nativePollOnce方法。
- 如果消息队列中有消息,nativePollOnce方法会将消息保存在mMessage成员中。nativePollOnce方法返回后立刻检查mMessage成员是否为空。
- 如果mMessage不为空,那么检查它指定的运行时间。如果比当前时间要前,那么马上返回这个mMessage,否则设置timeout为两者之差,进入下一次循环。
- 如果mMessage为空,那么设置timeout为-1,即下次循环nativePollOnce永久堵塞。
- nativePollOnce方法内部利用epoll机制在之前建立的管道上等待数据写入。接收到数据后马上读取并返回结果。
总结得出,其流程如下图所示:
+------+ +------------+ +------------------+ +--------------+
|Looper| |MessageQueue| |NativeMessageQueue| |Looper(Native)|
+--+---+ +------+-----+ +---------+--------+ +-------+------+
| | | |
| | | |
+-------------------------------------------------------------------------------+
|[msg loop] | next() | | | |
| +------------> | | | |
| | | | | |
| | | | | |
| | | nativePollOnce() | | |
| | | pollOnce() | | |
| | +----------------> | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | pollOnce() | |
| | | +-----------------> | |
| | | | | |
| | | | | epoll_wait()
| | | | +--------+ |
| | | | | | |
| | | | | | |
| | | | | <------+ |
| | | | | awoken() |
| + + + + |
| |
| |
+-------------------------------------------------------------------------------+
消息发送
在讲消息发送前,我们先看一下消息的创建:
// Handler.java
public final Message obtainMessage()
{
// 注意这里的this,就是当前 Handler 的实例
return Message.obtain(this);
}
// Message.java
public static Message obtain(Handler h) {
Message m = obtain();
// 该 Message 的 target 保存了传入的 Handler
m.target = h;
return m;
}
消息发送过程主要由 Handler
对象来驱动。具体就是其 sendMessage() 方法。
// Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 将消息压入MessageQueue
return queue.enqueueMessage(msg, uptimeMillis);
}
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
private native static void nativeWake(long ptr);
- Handler对象在创建时会保存当前线程的looper和MessageQueue,如果传入Callback的话也会保存起来。
- 用户调用handler对象的sendMessage方法,传入msg对象。handler通过调用MessageQueue的enqueueMessage方法将消息压入MessageQueue。
- enqueueMessage方法会将传入的消息对象根据触发时间(when)插入到message queue中。然后判断是否要唤醒等待中的队列。
- 如果插在队列中间。说明该消息不需要马上处理,不需要由这个消息来唤醒队列。
- 如果插在队列头部(或者when=0),则表明要马上处理这个消息。如果当前队列正在堵塞,则需要唤醒它进行处理。
- 如果需要唤醒队列,则通过nativeWake方法,往前面提到的管道中写入一个"W"字符,令nativePollOnce方法返回。
总结得出,其流程如下图所示:
+-------+ +------------+ +------------------+ +--------------+
|Handler| |MessageQueue| |NativeMessageQueue| |Looper(Native)|
+--+----+ +-----+------+ +---------+--------+ +-------+------+
| | | |
| | | |
sendMessage()| | | |
+----------> | | | |
| | | |
|enqueueMessage()| | |
+--------------> | | |
| | | |
| | | |
| | | |
| | nativeWake() | |
| | wake() | |
| +------------------> | |
| | | |
| | | wake() |
| | +------------------> |
| | | |
| | | |
| | | |write(mWakeWritePipeFd, "W", 1)
| | | |
| | | |
| | | |
| | | |
| | | |
+ + + +
消息处理
在“消息循环”的源码中,我们提到了 Looper 对象的loop方法里面的queue.next方法如果返回了message,那么handler的dispatchMessage会被调用。
- 如果新建Handler的时候传入了callback实例,那么callback的handleMessage方法会被调用。
- 如果是通过post方法向handler传入runnable对象的,那么runnable对象的run方法会被调用。
- 其他情况下,handler方法的handleMessage会被调用。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// 如果通过 Message 传入了 Callback 实例,则由其响应
handleCallback(msg);
} else {
if (mCallback != null) {
// 如果新建Handler的时候传入了callback实例,那么callback的handleMessage方法会被调用
if (mCallback.handleMessage(msg)) {
return;
}
}
// 否则handler方法的handleMessage会被调用
handleMessage(msg);
}
}
public void handleMessage(@NonNull Message msg) {
}
总结得出,其流程如下图所示:
+------+ +-------+
|Looper| |Handler|
+--+---+ +---+---+
| |
| |
loop() | |
[after next()] |
+---------> | |
| |
|dispatchMessage()
+-------------> |
| |
| |
| | handleMessage()
| +-------+
| | |
| | |
| | <-----+
| | (callback or subclass)
| |
+ +
Handler 面试那些事儿
Handler 跨线程通信的原理
子线程到主线程通信,其实是利用了“线程间共享内存”。先看下图——Handler工作流程:
Handler工作流程.png跨线程的核心原理:
跨线程的核心原理.png从上图可以看到,跨线程通信明显是一个生产者消费者的设计模式。
Handler 内存泄露的原因
匿名内部类默认持有外部类的引用(activity → handler),匿名内部类只是表象,这个问题实际上是考察 JVM 的知识。
handerl 泄漏时的引用链:activity → handler → Message → MessageQueue → Looper → static sThreadLocal。static 变量就是一个 GC Root。
GC 算法
- 引用记数法:缺陷是对象相互引用(形成一个循环)时,无法计算。
- 可达性分析法:直接或间接的被 GC Root 持有引用。
子线程中维护的 Looper ,消息队列无消息时的处理方案是什么?有什么用?主线程呢?
这其实也是考量内存泄漏的。 虽然无消息时,并不会占用cpu,因为 Looper 中有睡眠机制,会停止 cpu 使用,但线程本身需要占用内存(比如每个线程都会有各自的工作内存),且每个应用可创建的线程是有限的,如果一个子线程中维护了一个 Looper ,而 Looper 中必然有一个死循环,只要循环在运行,该子线程就一直存在,如果在无消息时仍然不退出 looper ,就意味着这个线程本身所占用的内存也被泄漏了,并且该线程会成为一个 GCRoot ,被该线程引用的其他对象,也都会被泄漏。
- 子线程没有消息时的处理方案:调用 looper.quit() 退出循环
- 用处:停止循环,退出线程
- 主线程没有消息时怎么处理:不能退出循环,只能让其休眠。
// MessageQueue.java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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();
}
// nextPollTimeoutMillis 会传给 native 层,当传入的是 -1 时,就会“睡眠”
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// 根据最近一条消息要等待执行的时间,通过 nativePollOnce 唤醒
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.
// 无消息时,此变量赋值为 -1,下次循环时传入 nativePollOnce()
nextPollTimeoutMillis = -1;
}
...
}
}
nativePollOnce() 最终会调用 native层的 MessageQueue 中的 epoll() 机制,“-1”就代表睡眠。
Handler 如何处理发送延迟消息
先看看消息队列添加消息和取消息的过程:
MessageQueue的主要函数.png- 消息队列中的消息按待执行时间排序
- 队头是最早要执行的消息
- 当队头消息还未到达执行时间时,计算剩余等待时间,并通过 nativePollOnce 传给 native 层的 epoll() ,到时自动唤醒
Handler 没有消息处理时是阻塞的还是非阻塞的?为什么不会有 ANR 产生?
首先要明白什么是 ANR ?以及 ANR 的分类、触发原理。
ANR超时阈值.png其中,按键事件(input) 5秒钟未处理完时,其实不一定会触发 ANR ,只有当为获得相应时,不停点击按钮,也就是第一次按钮5秒内未处理完,又点击第二次按钮时,才会触发 ANR。
Service ANR触发机制.png其实 ANR 是一个以 Message 展示出来的 UI。
- 没有消息处理时是“阻塞”的,这才才会“睡眠”,释放 cpu。
- ANR 的产生是因为做了个“定时炸弹”,而 handler 中没有做定时炸弹,它的阻塞就是因为没有消息处理了,它要休眠,交出 cpu
我们使用 Message 时,应该如何创建它?
通过 obtain 获取 Message ,为什么要这么做呢,看看代码:
// Handler.java
/**
* Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
* If you don't want that facility, just call Message.obtain() instead.
*/
@NonNull
public final Message obtainMessage()
{
return Message.obtain(this);
}
// Message.java
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
// 优先从 Message pool 中获取
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();
}
当我们看到 sPool 时,就应该明白,通过 obtain() 获取时,是优先从 Message 内部自己维护的一个 Message Pool 中获取 Message 实例的。类似于 Thread Pool 机制,这里的 Message Pool 是为了高效的消息复用,而复用则是为了避免内存抖动。这里采用了享元设计模式。
内存抖动有什么问题?
内存抖动 → 内存碎片 → OOM
内存抖动 → 频繁GC(频繁STW) → 应用卡顿
Thread Pool 和 Message Pool ,都是运用享元模式,创建了一个共享内存池。享元模式的使用非常广,比如地图开发,对于接收到的位置信息,不能每次都 new 一个 Java Bean 出来,而是应该创建一个 Java Bean 的内存池出来,合理复用。再比如股票类应用、 ViewPager、RecyclerView 等等。
网友评论