Fork me on GitHub

Android Framework 专项 - Handler(二)

MessageQueue 到底是什么?

从 MessageQueue 的创建说起

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//保证了MessageQueue的唯一性
mThread = Thread.currentThread();
}

MessageQueue 就是在 Looper 的构造方法里创建的,一个 Looper 就对应了一个 MessageQueue

MessageQueue 如何实现线程间的数据隔离

线程是不持有系统资源的进程,所以同一个进程中的线程是共用的同一个进程持有的内存,说人话就是进程中持有的内存中的变量和数据每个线程都可以直接读取,MessageQueue 是存放线程要处理的消息的,我们当然不希望它是进程持有的线程之间共享的,不能被其他的线程所干扰,换句话说 MessageQueue 必须是线程隔离的

android.os.Looper#prepare(boolean)

1
2
3
4
5
6
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 和 ThreadLocal 关联起来,保证 looper 的唯一性
}

注意 Looper 是一个静态类非一个实例,在 Looper 的 prepare 阶段,会去 new Looper(quitAllowed),并将其放入 ThreadLocal,这样就让 Looper 成为了 线程变量,而 MessageQueue 由 Looper 创建并持有,所以 MessageQueue 自然也成了 线程变量,这样就实现了每个线程有自己独立的 Looper 和 MessageQueue 实例,且相互隔离。

MessageQueue 是每个线程独有的。每个线程都拥有自己的消息队列,因此在不同的线程之间无法直接共享消息队列。消息是在一个线程中创建和发送到该线程的消息队列,然后由该线程的 Looper 从队列中取出并处理。

1
`ThreadLocal` 是一个线程级别的存储,它在每个线程中维护一个独立的存储空间(`ThreadLocalMap`),每个存储空间使用 `ThreadLocal` 对象作为键。不同线程的存储空间互不干扰,实现了线程间的数据隔离;但在同一个线程内,`ThreadLocal` 对象可以共享,对应的数据副本在不同方法间保持一致。、

MessageQueue 同步屏障

试想一种情况 MessageQueue 需要处理

android.os.MessageQueue#next 中的另一种执行逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@UnsupportedAppUsage
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();
}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
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.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}

我们重点看这一段

1
2
3
4
5
6
7
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}

如果 msg != null,而 msg.target == null 的时候,android.os.MessageQueue#next 执行了完全不同的另一种逻辑,target 就是 msg 的目标 handler,也就是说如果 msg 没有目标 handler 的时候,那么 msg 就是一个屏障消息,android.os.MessageQueue#next 就会进入无限循环读取异步消息的逻辑

从这里我们知道 MessageQueue 提供了一个屏障,这个屏障可以让 MessageQueue 越过所有同步消息优先执行异步消息

我们看看这个屏障该如何升起与取消

简单的发送一个 msg.target == null 的消息升起同步屏障

可以简单的发送一个 msg.target == null 的消息来升起这个屏障吗?
尝试一下你就会发现出现了”Message must have a target.”的异常

android.os.MessageQueue#enqueueMessage msg.target == null 的危险性与抛出的异常
1
2
3
4
5
6
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
...
}

我们发现 android.os.MessageQueue#enqueueMessage 第一步就是检查 msg.target 是否为 null,msg.target == null 极度危险,一旦消息没有正确的被处理,会导致整个 MessageQueue 进入异步消息的死循环无法退出,因此 消息屏障的触发与取消必须被管控起来

严格管控下的同步屏障的触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}

这段代码是 Android 消息机制中的一部分,用于实现同步屏障(Sync Barrier)。同步屏障是一种机制,可以用来控制消息队列中的消息执行顺序,特别是用于确保后续的同步消息在某个条件满足之前被阻塞执行。下面对这段代码进行分析:

  • postSyncBarrier() 方法:

    • 这个方法是向消息队列中添加一个同步屏障。
    • 同步屏障是一种特殊的消息,它会阻塞后续的同步消息的执行,直到同步屏障被释放。
    • 该方法返回一个用于标识同步屏障的 token,这个 token 在稍后调用 removeSyncBarrier() 方法时需要使用。
  • postSyncBarrier(long when) 方法:

    • 这个方法是 postSyncBarrier() 的内部实现。
    • 该方法会创建一个同步屏障消息,设置其触发时间(when)和一个唯一的 token。
    • 同步屏障消息将被插入消息队列中,并根据触发时间排序。
  • 在插入同步屏障消息时:

    • 遍历消息队列,找到合适的位置插入同步屏障消息,以保持消息队列的顺序。
    • 如果同步屏障消息需要插入的位置在已有消息之后,将同步屏障消息插入到该位置之后。
    • 如果同步屏障消息需要插入的位置在已有消息之前,将同步屏障消息作为新的头部消息。

总结起来,这段代码实现了向消息队列中插入同步屏障消息的功能。同步屏障消息的作用是阻塞后续的同步消息的执行,直到满足某个条件后释放同步屏障。这种机制可以用于控制消息队列中消息的执行顺序,确保在特定条件满足之前某些消息不被执行。

严格管控下的同步屏障的取消
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Removes a synchronization barrier.
*
* @param token The synchronization barrier token that was returned by
* {@link #postSyncBarrier}.
*
* @throws IllegalStateException if the barrier was not found.
*
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();

// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}

这段代码是用于移除同步屏障(Sync Barrier)的逻辑。它会从消息队列中移除指定的同步屏障消息,并在必要时唤醒消息队列,以继续处理后续的消息。下面对这段代码进行分析:

  • removeSyncBarrier(int token) 方法:

    • 这个方法用于移除同步屏障。
    • 它接受一个参数 token,即之前调用 postSyncBarrier() 方法返回的标识同步屏障的 token。
    • 如果指定的同步屏障消息被找到并移除,将会在必要时唤醒消息队列。
  • 在移除同步屏障消息时:

    • 遍历消息队列,寻找包含指定 token 的同步屏障消息。
    • 如果找到了匹配的同步屏障消息,将其从消息队列中移除。
    • 如果在移除同步屏障消息后,消息队列不再被其他消息阻塞,会将队列唤醒,以继续处理后续的消息。
  • recycleUnchecked() 方法:

    • 在移除同步屏障消息后,调用这个方法将消息对象回收,以便释放资源。

总结起来,这段代码实现了移除同步屏障消息的功能。当同步屏障条件满足后,通过调用 removeSyncBarrier() 方法来移除同步屏障消息,从而解除对后续同步消息的阻塞。这个机制可以用于控制消息队列中消息的执行顺序,确保在特定条件满足后执行后续的同步消息。

从 ViewRootImpl 看同步屏障的使用

android.view.ViewRootImpl#scheduleTraversals

1
2
3
4
5
6
7
8
9
10
11
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

同步屏障的触发

1
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

再看一下发送消息的逻辑

android.view.Choreographer#postCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Posts a callback to run on the next frame.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame.
* @param token The callback token, or null if none.
*
* @see #removeCallbacks
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}

android.view.Choreographer#postCallbackDelayed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Posts a callback to run on the next frame after the specified delay.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame after the specified delay.
* @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeCallback
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}

postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}


重点: android.view.Choreographer#postCallbackDelayedInternal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}

synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}

我们看异步消息的发送逻辑

1
2
3
4
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);

android.view.ViewRootImpl#unscheduleTraversals

1
2
3
4
5
6
7
8
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}

android.view.ViewRootImpl#unscheduleTraversals 的调用时机比较有意思,这里暂不做更多的分析

Skipped 30 frames!

错误信息

在进行 UI 更新的时候,如果 UI 线程忙碌,主线程有时会抛出异常信息:Skipped 30 frames! The application may be doing too much work on its main thread.

错误原因

这是因为触发屏障的实际操作是发送一个 target 为 null 的 msg,但是如果这个 target 为 null 的消息被前面的耗时消息所耽误而一直没能执行,导致后面不断发送来的需要立即执行的异步消息都被耽误,触发同步屏障之后,系统发现,本该立即执行的异步消息已经积累了30帧只会,就会报出这个主线程忙碌的错误

改进方法

罪魁祸首就是那个耽误了 target == null 的同步屏障 msg 执行的 msg,这个同步消息的执行如此之耗时,以至于我们必须考虑对其优化或者考虑是不是适合放在主线程
可以放到其他线程去执行

MessageQueue 中的 synchronized

android.os.MessageQueue#next 中的 synchronized
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@UnsupportedAppUsage
Message next() {
...
for (;;) {
...
synchronized (this) {
...
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
...
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
...
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
...
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...
}
}
第一处

synchronized 用于同步代码块的范围涵盖了整个 next() 方法。具体来说,这段代码实现了一个消息循环(message loop)用于处理消息队列中的消息。在多线程环境下,有多个线程可能会调用 next() 方法来获取下一个消息,因此需要确保对共享资源的访问是安全的。

第二处

mIdleHandlers.remove(idler):这行代码从 mIdleHandlers 集合中移除一个 IdleHandler。由于多个线程可能同时访问和修改 mIdleHandlers,因此需要确保这个操作是原子的,以避免不一致或意外的结果。

其他函数

都是 synchronized 保护下的
MessageQueue 是 Android 框架中用于处理消息传递和线程通信的关键组件,多个线程可能会同时访问和修改消息队列,因此需要使用同步机制来避免竞态条件和其他线程安全问题。

MessageQueue 中的 IdleHandler

使用与意义

IdleHandler 是 Android 消息传递机制中的一个重要概念,它允许你在消息队列空闲时执行一些额外的操作。

  1. 使用: 你可以通过 MessageQueueaddIdleHandler() 方法将一个或多个 IdleHandler 添加到消息队列中。
  2. 意义: IdleHandler 允许你在消息队列空闲时执行一些轻量级的任务,这些任务通常是一些不需要立即处理、不会阻塞主线程的操作。常见的用例包括资源回收、后台数据同步、性能优化等。通过利用空闲时间执行这些任务,可以提高应用的性能和资源利用率。
  3. 执行时机: IdleHandlerqueueIdle() 方法在消息队列没有即时任务需要处理时调用。如果 queueIdle() 返回 true,该 IdleHandler 将继续保持在队列中,以便在下一次空闲时调用;如果返回 false,则该 IdleHandler 将从队列中移除。
为什么会有多个 IdleHandler
  1. 功能分离: 不同的 IdleHandler 可以用于执行不同类型的任务,如资源回收、后台数据同步、性能优化等。通过将不同的任务逻辑分离到不同的 IdleHandler 中,可以使代码更加模块化和可维护。
  2. 任务优先级: 不同的 IdleHandler 可以根据优先级来执行任务。高优先级任务可以通过将对应的 IdleHandler 添加到队列中,确保在空闲时尽快执行。低优先级任务则可以延迟到更空闲的时候执行。
  3. 动态注册和注销: 多个 IdleHandler 允许开发者在不同的时刻动态地注册和注销任务。这使得可以根据应用程序的状态和需求来动态地调整任务的执行。
  4. 任务复用: 如果有多个相似的任务需要在空闲时执行,可以通过不同的 IdleHandler 实现任务的复用,避免重复编写类似的代码。
  5. 提高性能: 通过将不同的任务拆分到多个 IdleHandler 中,可以减少单个 IdleHandler 的负载,从而提高任务的执行效率。
关键行为分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@UnsupportedAppUsage
Message next() {
...
for (;;) {
...
synchronized (this) {
...
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
/**
* 如果首次进入空闲状态,则获取要运行的空闲处理程序数量。
* 空闲处理程序仅在消息队列为空或队列中的第一个消息(可能是一个屏障)将在未来被处理时运行。
*/
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
// 没有要运行的空闲处理程序。继续循环并等待一段时间。
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
/**
* 运行空闲处理程序。
* 我们只会在第一次迭代期间到达这个代码块。
*/
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...
}
}

mPendingIdleHandlers 与 mIdleHandlers

  1. mPendingIdleHandlers
    • 类型:IdleHandler[]
    • 作用:用于存储当前等待执行的空闲时处理对象(IdleHandler)数组。在消息队列空闲时,这些处理会被调用,以执行额外的任务。
    • 使用场景:用于临时存储等待执行的空闲时处理,直接与循环内部逻辑相关。
  2. mIdleHandlers
    • 类型:ArrayList<IdleHandler>
    • 作用:用于存储注册的空闲时处理对象。开发者可以将多个 IdleHandler 添加到这个列表中,以便在消息队列空闲时执行不同的任务。
    • 使用场景:用于持久存储注册的空闲时处理,可以在任何时候添加或移除 IdleHandler

总结区别:

  • mPendingIdleHandlers 是一个数组,用于存储当前等待执行的空闲时处理对象。它是循环内部临时使用的,用于遍历调用每个等待执行的空闲时处理。
  • mIdleHandlers 是一个列表,用于持久存储注册的空闲时处理对象。开发者可以随时将 IdleHandler 添加到列表中,以便在消息队列空闲时执行不同的任务。
给 PendingIdleHandlers 分配新的数组空间
1
2
3
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}

这段代码的作用是在消息队列空闲时,检查是否需要运行空闲时处理。如果当前没有消息或者队列中的第一个消息的处理时间还未到来,并且之前的 pendingIdleHandlerCount 小于 0,那么它会获取当前注册的空闲时处理的数量,并将其赋值给 pendingIdleHandlerCount。这样,当队列为空或者第一个消息处理时间未到来时,代码会准备好运行已注册的空闲时处理。通常情况下,这个检查用于确保空闲时处理在适当的时机被调度执行。

1
2
3
4
5
6
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
// 没有要运行的空闲处理程序。继续循环并等待一段时间。
mBlocked = true;
continue;
}

这段代码的作用是在消息队列空闲时,如果没有等待执行的空闲时处理,就将消息队列标记为被阻塞状态,并继续等待更多的消息或任务进入队列。这个逻辑用于优化资源管理,确保在没有即时任务需要处理时,程序仍然能够保持运行,以便在有任务时能够立即执行。

1
2
3
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}

注意:隐含的条件是 pendingIdleHandlerCount <= 0 才能走到这里,所以判断条件其实是 pendingIdleHandlerCount <= 0 && mPendingIdleHandlers == null,通常表示这是第一次运行空闲时处理

如果 mPendingIdleHandlersnull,则进入条件判断。在这里,使用 Math.max(pendingIdleHandlerCount, 4) 来计算数组的长度,其中 pendingIdleHandlerCount 是等待执行的空闲时处理的数量。如果等待执行的处理数量小于 4,则数组长度取值为 4,否则取值为 pendingIdleHandlerCount

PendingIdleHandlers 赋值
1
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
运行空闲时处理 PendingIdleHandlers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
/**
* 运行空闲处理程序。
* 我们只会在第一次迭代期间到达这个代码块。
*/

for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

注意:这里注释中的第一次迭代是指的消息队列第一次处理完所有已经在队列中的消息的那一次迭代,是想说明在后续的迭代中,如果消息队列仍然处于空闲状态,那么这段代码块不会再执行,因为它只在消息队列刚刚变为空闲时运行。

队列的唤醒与阻塞

android.os.MessageQueue#enqueueMessage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}

synchronized (this) {
...
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;
}

在这段代码中,mBlocked 在两个地方起到了不同的作用:

  1. 第一个 mBlocked 作用:

    1
    needWake = mBlocked;

    在这里,mBlocked 被赋值给变量 needWake。这个操作是为了判断是否需要唤醒事件队列(消息循环)。具体情况如下:

    • 如果 mBlockedtrue,表示事件队列当前正处于阻塞状态,即没有立即需要处理的消息。在这种情况下,如果新消息的插入导致事件队列不再阻塞,就需要唤醒事件队列,以便消息循环继续执行。
    • 如果 mBlockedfalse,表示事件队列没有阻塞,新消息的插入不会改变这个状态,因此不需要唤醒。
  2. 第二个 mBlocked 作用:

    1
    needWake = mBlocked && p.target == null && msg.isAsynchronous();

    在这里,mBlocked 参与了判断条件。这个条件用于判断是否需要唤醒事件队列,以提醒消息循环处理新消息。具体情况如下:

    • 如果 mBlockedtrue,表示事件队列当前正处于阻塞状态。然后,进一步判断消息队列中是否有异步消息,并且插入的新消息也是异步消息,那么需要唤醒事件队列,以便消息循环能够立即处理这个异步消息。

综上所述,mBlocked 在这段代码中的两个地方都与判断是否需要唤醒事件队列有关。第一个地方是用来判断是否需要在新消息插入时唤醒事件队列,以便消息循环继续执行。第二个地方是在特定条件下,判断是否需要唤醒事件队列来处理异步消息。

android.os.MessageQueue#next

在提供的代码片段中,有三处不同的地方使用了 mBlocked 参数,并且它们在不同的上下文中起到了不同的作用。以下是每个位置的详细分析:

第一处 mBlocked 作用:

1
mBlocked = false;

在这里,mBlocked 被设置为 false,表示消息队列不再处于阻塞状态。这是在找到了一个准备好被处理的消息后执行的操作。通过将 mBlocked 设置为 false,消息循环可以继续处理消息,而不需要等待。

第二处 mBlocked 作用:

1
mBlocked = true;

在这里,mBlocked 被设置为 true,表示消息队列当前处于阻塞状态。这是在判断没有可处理的消息,并且没有要运行的空闲处理程序时执行的操作。通过将 mBlocked 设置为 true,消息循环进入了等待状态,等待新的消息或者空闲处理程序的到来。

第三处 mBlocked 作用:

1
mBlocked = true;

这里的 mBlocked 也被设置为 true,与第二处的作用相同。这是在消息队列处理完所有的空闲处理程序之后,仍然没有要运行的空闲处理程序时执行的操作。通过将 mBlocked 设置为 true,消息循环会继续等待,直到有新的消息到达或者空闲处理程序需要运行。

总结来说,mBlocked 在这段代码中的三处不同的作用是:

  1. 在找到一个准备好被处理的消息后,将其设置为 false,使得消息循环可以继续处理消息。
  2. 在没有可处理的消息且没有要运行的空闲处理程序时,将其设置为 true,使得消息循环进入等待状态。
  3. 在所有空闲处理程序都被处理后,仍然没有要运行的空闲处理程序时,将其设置为 true,继续等待。

通过这样的设置,消息循环能够根据不同的情况来控制阻塞和等待状态,以便有效地处理消息和任务。

Android 性能专题 - 插件化预热-拼多多为什么只有26M-GPT4快问快答

什么是双亲委派机制

双亲委派机制(Parent Delegation Model)是Java类加载器(ClassLoader)使用的一种特定模型,在这个模型中,类加载器在尝试加载类或资源时,会首先委托给其父类加载器进行加载,这个过程会一直递归下去,直到达到最顶层的启动类加载器(Bootstrap ClassLoader)。如果父类加载器可以完成类加载工作,就使用父类加载器的定义,如果父类加载器无法完成加载工作(即它搜索的路径中没有找到所需的类),子类加载器才会尝试自己去加载这个类。

双亲委派机制的优点包括:

  1. 避免类的重复加载:由于在顶层开始尝试加载类,因此类在JVM中只会被加载一次,这样就保证了Java虚拟机中存在的类,都是由唯一的类加载器加载的,从而避免了类的多次加载。

  2. 保护程序安全:通过给予Java核心库的加载工作委托给顶层的启动类加载器,保证了Java核心库的类型安全,防止核心API库被随意替换。用户自定义的类加载器不会去尝试加载这些Java核心类库,这样可以防止一些恶意代码替代了这些核心API,从而破坏JVM的安全性。

  3. 促进正向的类加载工作流:这种委派模式促进了类加载器之间的协作,遵循了一种从顶层到底层的工作流,使得类加载过程更加清晰。

尽管双亲委派模型带来了上述好处,但在某些情况下,比如在一个Java程序中需要加载一些运行时生成的类,或者是从网络上下载的类,这时可能需要自定义类加载器,并打破双亲委派模型。在Java中,可以通过重写ClassLoaderloadClass方法来实现自定义的类加载逻辑,从而在必要时绕过双亲委派模型。

为什么可以避免类的重复加载

双亲委派模型可以避免类的重复加载主要是因为它采用了一种层次化的类加载方式,确保了每个类由一个确定的类加载器负责加载。具体来说,这种机制避免类重复加载的原理和机制包括以下几点:

  1. 层次化的类加载器结构:在Java中,类加载器通常有三层结构(启动类加载器、扩展类加载器、应用程序类加载器),加上可能的自定义类加载器。每个类加载器都有可能有自己的父加载器(除了启动类加载器,它是最顶层的加载器,没有父加载器)。当一个类加载器接收到类加载的请求时,它首先不会尝试自己去加载这个类,而是把这个请求委托给它的父加载器去执行,这样一层层向上直到启动类加载器。

  2. 委托机制确保类的唯一性:因为加载请求最终会传递到启动类加载器,如果这个类能被启动类加载器加载,那么它就会由启动类加载器完成加载。如果启动类加载器加载不了,它会返回到下一级加载器尝试,依此类推,直到找到合适的加载器加载这个类。这样一来,无论是哪个类加载器加载的类,都确保了在JVM中的唯一性,因为同一个类在JVM中只会被加载一次。

  3. 缓存机制:类加载器在加载类的过程中,会将加载过的类缓存起来。当再次请求加载这个类时,类加载器会首先检查这个类是否已经加载过了,如果已经加载,就直接从缓存中返回这个类的Class对象,而不会再次加载。这个缓存机制进一步保证了类的唯一性和避免重复加载。

  4. 安全性考虑:双亲委派模型还有一个重要的安全考量。通过让启动类加载器(负责加载Java核心库的加载器)在加载类的最前线,可以避免核心API被恶意替换。因为用户自定义的类加载器无法直接加载核心API类,所以即使有类的全限定名相同,也不会影响到JVM内部的类实现,这样就避免了核心类库的污染和重复加载。

通过上述机制,双亲委派模型不仅避免了类的重复加载,而且还提升了Java程序的运行时安全性和稳定性。

向我解释启动类加载器、扩展类加载器、应用程序类加载器

Java虚拟机(JVM)使用类加载器(ClassLoader)来动态加载类文件到内存中。在JVM中,类加载器遵循双亲委派模型,并且主要分为三种类型:启动(Bootstrap)类加载器、扩展(Extension)类加载器、应用程序(Application)类加载器。它们的作用和层次结构如下:

启动类加载器(Bootstrap ClassLoader)

  • 作用:这是Java类加载器层次结构中的最顶层加载器。它负责加载JVM基础核心类库,如rt.jarresources.jarcharsets.jar以及类路径中指定的类。这些库位于JDK的jre/lib目录或者是JAVA_HOME/lib目录下。
  • 实现:启动类加载器并不是Java实现的,而是由JVM自身的底层代码(如C/C++)实现的。这意味着它不继承自java.lang.ClassLoader,不能被Java代码直接引用。
  • 特点:由于其加载的是Java的核心类库,因此它不会被Java程序直接控制。它提供了Java运行时环境的基础类,保证了JVM的稳定性和安全性。

扩展类加载器(Extension ClassLoader)

  • 作用:扩展类加载器负责加载Java的扩展库,即位于JDK的jre/lib/ext目录或者由系统属性java.ext.dirs指定位置的类库。这些库提供了标准Java类库之外的扩展功能。
  • 实现:扩展类加载器是用Java实现的,并且它是java.lang.ClassLoader类的一个实例。在类加载器的层次结构中,它是启动类加载器的子加载器。
  • 特点:扩展类加载器使得用户可以通过放置jar文件到ext目录来扩展JVM的功能,而不需要放到JVM核心类库目录中去。

应用程序类加载器(Application ClassLoader)

  • 作用:应用程序类加载器负责加载用户类路径(Classpath)上的类库,这包括来自于CLASSPATH环境变量、-classpath-cp命令行选项指定的所有类库。它是程序中默认的类加载器,通常用来加载我们自己写的类和第三方库。
  • 实现:这个加载器也是用Java实现的,并且是ClassLoader的一个直接子类。在类加载器的层次结构中,它是扩展类加载器的子加载器。
  • 特点:大部分应用程序中的类都是由这个加载器加载的。它保证了应用程序类是由用户定义的路径加载,而不是由JVM的内部路径决定。

这三个类加载器共同协作,确保了Java应用程序在运行时可以动态加载类,同时保障了系统的安全性和类加载机制的高效性。

向我介绍什么是Android的插件化

Android的插件化是一种在Android开发中实现模块化、动态加载和运行时集成第三方组件或模块的技术手段。它允许应用在不需要重新安装或更新整个应用的情况下,动态加载和卸载功能模块。这种技术为应用提供了极高的灵活性和扩展性,使得应用能够根据需要加载新功能或更新现有功能,同时减少了应用的初始下载大小。

Android插件化的关键特点

  1. 动态性:插件可以在应用运行时被动态加载和替换,无需重新安装或更新整个应用。
  2. 模块化:应用的不同功能可以被封装成独立的模块(插件),便于单独开发和管理。
  3. 减小体积:通过插件化,可以减少应用的初始下载大小,用户可以根据需要下载额外的功能模块。
  4. 更新灵活:可以单独更新某个功能模块而不影响整个应用,提高了更新的灵活性和效率。

实现机制

Android插件化的实现通常涉及到以下几个关键技术点:

  • 类加载器(ClassLoader):利用Android的ClassLoader机制动态加载插件中的类。通过自定义ClassLoader实现类的加载,避免了插件和宿主应用之间的类冲突。
  • 资源管理:动态加载插件中的资源(如布局、图片、字符串等),并确保宿主应用和插件能够正常使用这些资源。
  • 四大组件激活:动态注册和管理插件中的四大组件(Activity、Service、BroadcastReceiver、ContentProvider),由于这些组件需要在AndroidManifest.xml中静态声明,插件化技术需要绕过这一限制,通过宿主代理或其他机制实现组件的动态激活。
  • 上下文隔离:保证插件运行时的上下文(Context)环境与宿主应用隔离,避免相互干扰。

应用场景

  • 功能模块化:将应用的不同功能模块化,按需加载,减轻应用初始体积。
  • 热更新:修复bug或更新应用的某部分功能而无需更新整个应用。
  • 组件共享:实现跨应用的组件共享和复用。

挑战

尽管插件化为Android应用开发带来了诸多好处,但它也引入了一些挑战,包括复杂的实现机制、性能优化、资源管理、安全性问题等。因此,实现一个稳定、高效的插件化框架需要深入理解Android的底层机制和高级特性。

双亲委派机制在Android插件化中的作用

在Android插件化中,双亲委派机制扮演着至关重要的角色,主要体现在插件的加载和运行时隔离方面。由于Android基于Java开发,而Java的类加载器采用双亲委派模型,这对Android插件化的实现产生了直接影响。以下是双亲委派机制在Android插件化中作用的几个关键点:

确保类的唯一性

双亲委派机制通过优先委托父加载器加载类的方式,确保了JVM中每个类的唯一性。在Android插件化框架中,这一点尤为重要,因为它可以防止插件中的类与宿主应用或其他插件中的类发生冲突。例如,如果插件和宿主应用都使用了同一个库的不同版本,双亲委派机制可以确保在同一个类加载器上下文中,这个库的类只被加载一次,从而避免版本冲突。

提高安全性

双亲委派机制通过限制类的加载来源,提高了应用的安全性。在插件化环境中,通过自定义类加载器加载插件,可以控制插件的权限,防止插件访问或修改宿主应用的私有数据。同时,由于双亲委派模型优先使用父加载器加载类,它还可以防止恶意代码通过插件覆盖或篡改系统类库。

实现运行时隔离

在Android插件化中,通常需要实现插件之间以及插件与宿主之间的运行时隔离。双亲委派机制允许插件化框架通过自定义类加载器为每个插件创建独立的命名空间,从而实现隔离。这种隔离机制确保了插件可以独立运行,互不干扰,同时也方便了插件的管理和更新。

动态加载插件

插件化框架通常需要动态加载和卸载插件,双亲委派机制使得这种动态性成为可能。通过使用自定义的类加载器加载插件,可以在运行时根据需要加载或替换插件,而不需要重新启动应用。这种机制不仅提高了应用的灵活性,也支持了热更新和模块化部署。

绕过双亲委派模型的挑战

虽然双亲委派机制有诸多优点,但在实现Android插件化时,有时也需要绕过这种机制。例如,当插件需要使用不同版本的共享库时,可能需要设计复杂的类加载策略来加载正确的类版本,而不是由双亲委派机制自动选择。这就需要插件化框架能够灵活控制类加载过程,可能通过反射或者使用自定义类加载器来实现。

总的来说,双亲委派机制为Android插件化提供了一个强大的基础,使得插件化框架能够高效、安全地管理和加载插件。但同时,它也带来了一定的挑战,需要开发者深入理解其工作原理,才能有效地利用这一机制实现高质量的插件化方案。

双亲委派机制如何使得这种动态性成为可能

双亲委派机制本身是一种类加载机制,确保了类加载的有序性和安全性。在讨论它如何使得动态性成为可能时,我们主要关注的是这种机制如何被用于实现和优化Android插件化中的动态加载和动态更新功能。

实现动态加载

在Android插件化中,动态加载指的是应用运行时按需加载外部或者附加的代码和资源,而不是在应用安装时就固定下来。双亲委派机制通过以下方式促进这种动态加载:

  • 自定义类加载器:开发者可以通过创建自定义的类加载器来绕过双亲委派机制的一些限制,使得在应用运行时可以加载或替换插件。这些自定义类加载器可以直接或间接继承自ClassLoader类,通过重写findClass方法来实现对插件类的加载。
  • 隔离和独立:自定义类加载器可以为每个插件创建独立的命名空间,这意味着即使多个插件包含相同的类名,它们也能够被独立加载和运行,互不干扰。这种隔离保证了插件之间的动态性和独立性。

支持动态更新

动态更新是指在不重启应用的情况下更新应用的部分内容或功能。双亲委派机制在这方面的作用体现在:

  • 类版本的控制:通过自定义类加载器,应用可以动态地卸载旧的插件类和加载新的插件类。这是因为Java虚拟机(JVM)允许同一个类可以被不同的类加载器加载,只要这些类加载器是不同的实例。因此,通过更新类加载器或者使用不同的类加载器实例,可以实现插件的动态更新。
  • 资源的动态加载:除了代码之外,插件化还涉及到资源的动态加载,如图片、布局文件等。自定义类加载器也可以被用来加载这些资源,使得应用能够在运行时不仅更新代码,还可以更新资源。

如何绕过双亲委派机制

虽然双亲委派机制的初衷是为了保证安全和一致性,但在插件化场景下,有时需要绕过这一机制以实现更高级的动态性。这通常通过以下方式实现:

  • 反射和Hook技术:通过反射调用或修改Android内部的API行为,或者使用Hook技术,可以在不违背双亲委派原则的前提下,实现对类加载过程的控制和干预。
  • 自定义类加载策略:通过实现自定义的类加载器,并在其中实现特定的加载策略,可以有选择性地加载或替换特定的类或资源,从而实现对双亲委派机制的有效绕过。

总的来说,双亲委派机制在Android插件化中的作用并不直接关联到动态性的实现,而是通过自定义类加载器和策略,以及对这一机制的细致控制和适当绕过,使得动态加载和更新成为可能。

Android Framework 专项 - Handler(三)

要理解 Message 的创建,先要说内存抖动

内存抖动的原因及引发的问题

在 Android 虚拟机中,当我们在应用程序中创建大量的临时对象,并且这些对象的存在时间很短暂,虚拟机会识别这些临时对象并将它们标记为待清理的垃圾对象,以便在适当的时候回收它们。然而,这个垃圾回收过程可能会引发一个称为“stop the world”的现象。所谓“stop the world”指的是,在进行垃圾回收的过程中,虚拟机会暂停应用程序的正常执行,以便能够安全地识别和回收这些临时对象。

在 Java 字节码中,创建一个对象通常需要执行三个关键操作: newdupinvokespecial。这三个操作合在一起构成了对象创建的原子过程。

虚拟机通常通过两种方式来判断一个对象是否是垃圾。一种方式是使用“引用计数”机制,但这种方法存在循环引用的问题,因此无法完全准确地标记垃圾对象,从而导致垃圾回收不彻底。另一种方式是使用“根可达”(或称为“根引用”)的方式,即从根对象(如线程栈、静态变量等)开始,追踪对象之间的引用关系,从而判断哪些对象是可达的,哪些是垃圾。

然而,在标记垃圾对象的过程中,如果垃圾回收线程与创建对象的线程同时工作,就可能出现问题。例如,如果一个对象刚刚被创建但尚未与变量关联,而垃圾回收线程已经扫描到这个对象并标记为垃圾,那么在后续的垃圾回收过程中,这个对象可能会被回收,而在应用程序中使用相关变量时可能会触发空指针异常。为了避免这种情况,标记垃圾的过程通常需要暂停所有工作线程,确保在标记过程中不会有对象的状态发生变化。这就是为什么在进行垃圾回收时会出现应用程序“停顿”的现象。

然而,如果垃圾回收过程耗时过长,会导致应用程序长时间无响应,造成卡顿现象。因此,我们需要尽量避免在应用中创建大量生命周期很短的临时对象,以减少垃圾回收的频率和影响。

内存抖动的原因: 创建了大量的生命周期很短的对象。
导致的问题: 在用户看来程序出现了卡顿。

Message 中 obtain() 与 recycle()

显而易见,Message 那真是生命周期极短数量极其庞大,是最容易内存抖动的地方
所以 Message 内存必须被复用

我们说说 Message 的销毁与创建

创建: android.os.Message#obtain()

注意:说创建是不准确的,应该是提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 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) {
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();
}

这段代码是用于在 Android 中创建和管理消息对象的一部分。它通过维护一个对象池,允许在需要时重复利用已有的消息对象,从而减少内存分配和垃圾回收的开销。当需要获取一个新的消息对象时,它会首先检查对象池是否有可用的对象。如果有,它会从对象池中取出一个对象并重置其状态,然后返回;如果对象池为空,它会创建一个新的消息对象并返回。这种做法有助于提高应用程序的性能和效率。

1
2
3
4
5
synchronized (sPoolSync) {
if (sPool != null) {
...
}
}

pFOuBmd.png

1
Message m = sPool;

pFOuD0A.png

1
sPool = m.next;

pFOuwOH.png

1
m.next = null;

pFOu6tP.png

1
2
3
m.flags=0;
sPoolSize--;
return m;
回收: android.os.Message#recycle()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}

这里提一下 gCheckRecycle

1
2
3
4
5
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}

在较低版本的 Android 上 ( < 5.0 ),某些检查可能会导致不必要的异常情况,如 IllegalStateException。这可能会影响应用程序的稳定性。通过避免这些检查,可以避免异常的抛出,提高应用程序的可靠性。

回收: android.os.Message#recycleUnchecked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
@UnsupportedAppUsage
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;

消息的标志位 flags 的含义是消息对象是否正在使用中。

1
2
3
4
5
6
7
8
9
@UnsupportedAppUsage
void recycleUnchecked() {
...
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
...
}
}
}

在同步块中,检查消息池的大小是否小于最大池大小(MAX_POOL_SIZE)。

  • 如果是,将当前消息对象放入消息池的链表中,以供将来重用。
  • 更新消息池的大小。

pFOud6e.png

1
next=sPool;

pFOurTI.png

1
sPool=this;

pFOuykt.png


内存抖动参考自:
知乎博主Android小瓜: Android 性能优化大法——内存抖动
原文链接: https://zhuanlan.zhihu.com/p/575959909

android.os.Message#obtain() 与 android.os.Message#recycleUnchecked 相关图片来自于:
版权声明:本文为CSDN博主「-_-void」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xmh19936688/article/details/51901338

Android 性能专题 - 启动优化(一)启动耗时

声明:本文是《Android 性能优化入门与实战》——张世欣著 的笔记

为什么要做启动优化

App 启动耗时每减少 1s,用户流失率降低 6.9%

启动监控

1
2
3
4
5
6
7
8
9
补充知识

**App 冷温热启动分辨**:根据进程、Activity 是否已经存在

**冷启动**: 进程初始化 -> Activity.onCreate -> Activity.onStart

**温启动**: Activity.onCreate -> Activity.onStart

**热启动**:Activity.onStart

监控数据设计

需要比较的对照组:

  1. 旧版本-新版本
  2. 冷启动-温启动-热启动

数据与目的:

  1. 获取总耗时,判断新版本更快还是更慢
  2. 获取各区间耗时,具体分析耗时到具体区间

App 启动代码顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Application 构造函数

Application#attachBaseContext

ContentProvider#onCreate

Application#onCreate

Activity#onStart

Activity#onResume

View#onDraw

Activity#onWindowFocusChanged

起点:Application 构造函数

终点:

方案一:MainActivity 的某个 View 的第一次 onDraw(绘制函数)

- 优点:可以拿到第一帧绘制的耗时
- 缺点:
1. 执行时第一帧还没绘制完成
2. 需要选择某个核心布局,业务改造的时候容易影响到启动监控逻辑

方案二:MainActivity 的 onWindowFocusChanged

- 优点:不受业务逻辑的影响
- 缺点:被调用的时可能已经不是首帧,会将非首帧的时间算入

获取启动各阶段耗时

方法:

  1. 手动埋点
  2. 编译时 AOP

获取启动性能数据

1
2
3
4
5
6
分析 App 启动慢的几个重要指标
-> 代码耗时 (判断标准:App 运行的 CPU 时间充足)
-> 获取的 CPU 时间不足 (判断标准:App 运行的 CPU 时间不足)
-> 线程优先级不够 (判断标准:主线程优先级)
-> 被其他线程抢占过多 (判断标准:主线程被抢占次数)
-> 内存不足 (判断标准:启动期间 GC 执行次数和耗时)

线下分析

Logcat 或者 adb logcat 中查看关键字 Displayed 相关日志

自动执行 App的启动并获取启动耗时:通过 adb shell am start 实现多次自动启动 App 并获取第一次的启动耗时:

1
adb shell am start -S -W -R -3 com.antfortune.wealth/com.alipay.mobile.quinox.LauncherActivity
  • am start 是 ActivityManagerService 提供的命令,用来启动 Activity。
  • - S 即 Stop,表示在每次启动前,先强制停止 App 运行,以实现冷启动。
  • - W 即 Wait,表示执行后等待启动完成再退出,以统计整个启动的耗时。
  • - R 即 Repeat, 表示重复执行启动的次数,-R 3 表示重复启动 3 次。

会得到以下信息

1
2
3
4
5
6
7
8
Stopping: com.antfortune.wealth
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.antfortune.wealth/com.alipay.mobile.quinox.LauncherActivity }
Status: ok
LaunchState: COLD
Activity: com.antfortune.wealth/com.alipay.mobile.quinox.LauncherActivity
TotalTime: 463
WaitTime: 467
Complete

上述 TotalTime 就是整个冷启动的耗时,与 Locat 过滤 Displayed 得到的时间基本是一致的。

这两种方式,统计的都是 App 启动到 Activity 首次调用的时间 onWindowFocusChanged 的时间

如果想统计从 App 启动到数据请求成功后某个布局完全展示出来的耗时,可以在启动终点调用 Activity#reportFullyDrawn 来通知当前

已经完全绘制完成,然后在 Logcat 里过滤 Fully drawn 就可以看到整个流程的耗时。

Android Framework 专项 - IPC Binder 机制(一)

问题

1
2
The formulation of the problem is often more essential than its solution, which may be merely a matter of mathematical or experimental skill.
― Albert Einstein

Q: Binder 是什么?binder 是如何出现的

Q: Binder 通信模型是什么?

Q: Aidl 通信机制是什么?

Q: Bindservice 流程分析

Q: Binder 通信是如何走到 Native 层的

Q: ServiceManager 是什么?

Q: Binder 通信之 Client 端调度流程解析

从 Android 系统设计说起

Android 的系统的三个层次

application 应用层 - Framework 层- native 层

Android 中的应用层和系统服务层不在同一个进程,系统服务在单独的进程中。
Android 中的不同应用属于不同的进程,每一个应用是 zygote fork 出来的

为了安全,Android 的应用层与系统层之间是隔离的

Android 系统 IPC 原理

pFOKnAI.png

每个 Android 的进程,只能运行在自己进程所拥有的虚拟地址空间。
对应一个 4GB 的虚拟地址空间,其中 3GB 是用户空间,1GB 是内核空间,当然内核空间的大小是可以通过参数配置调整的。
对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。
Client 进程向 Server 进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client 端与 Server 端进程往往采用 ioctl 等方法跟内核空间的驱动进行交互。

用户空间和内核空间知识补充

怎么理解不同进程之间用户空间不能共享,而内核空间却是可共享的?

  1. 用户空间的共享: 在标准情况下,不同进程的用户空间是彼此隔离的,不能直接共享内存。每个进程有自己独立的虚拟地址空间,不同进程的相同虚拟地址并不会映射到相同的物理内存。这意味着一个进程不能直接访问另一个进程的用户空间。
  2. 内核空间的共享: 内核空间是操作系统内核的一部分,对所有进程来说都是共享的。这是因为内核提供了操作系统的核心功能,比如进程调度、内存管理、文件系统等。不同进程需要与内核进行交互来请求服务和操作资源。因此,所有进程都共享一个操作系统内核
  3. 内核空间中的数据隔离: 尽管内核空间对所有进程来说是共享的,但内核本身会实施严格的隔离措施,以防止一个进程的操作影响其他进程。内核使用许多机制来确保不同进程的请求和数据是独立的,从而保障系统的稳定性和安全性。
  4. 内核空间中的共享数据结构: 在某些情况下,内核中可能存在一些数据结构是为多个进程共享的,例如文件描述符表、进程控制块等。这种共享是通过内核维护的数据结构来实现的,而不是直接让不同进程的内核空间映射到相同的物理内存。

总结起来,不同进程的用户空间通常是不能直接共享的,每个进程都有自己的独立虚拟地址空间。但所有进程共享同一个内核空间,内核提供了操作系统的核心功能。内核空间中的数据隔离和共享是通过内核内部的机制来实现的

页表知识补充

页表是操作系统中用于管理虚拟内存与物理内存之间映射关系的数据结构。在计算机中,虚拟内存是指操作系统为每个进程提供的独立的内存地址空间,而物理内存则是实际的硬件内存。由于物理内存有限,虚拟内存允许多个进程同时运行,而不会受到物理内存大小的限制。

页表的主要功能是将虚拟地址转换为物理地址。当进程访问虚拟地址时,操作系统通过页表查找,找到对应的物理地址,从而实际完成内存的读写操作。

具体来说,页表包含了虚拟地址和物理地址之间的映射关系。每个进程都有自己的页表,其中的页表项记录了虚拟地址的页号(page number)与物理地址的页框号(page frame number)之间的对应关系。页框是物理内存的一个固定大小的块,而页是虚拟内存的一个固定大小的块。通过查找页表,操作系统可以找到虚拟地址对应的物理地址,从而实现内存访问。

总结

  1. 用户空间与内核空间: IPC 机制总的操作又可以分为用户空间进行的操作和内核空间进行的操作
  2. 内核空间中的数据结构不同: 不同 IPC 机制内核中的数据结构不同
  3. 复制与映射: IPC 机制利用进程间可共享的内核空间来完成底层通信工作,其中我们可以简单的分为复制和虚拟内存映射两种方式

我们从这几个角度来快速的区分 IPC 机制

IPC-共享内存

用户空间与内核空间行为

用户空间:

  1. 创建共享内存: 进程通过系统调用在用户空间申请一块共享内存,得到一个唯一的标识符。
  2. 映射共享内存: 进程使用系统调用将共享内存映射到自己的虚拟地址空间,从而可以直接访问这块内存区域。这个映射实际上是指向了内核空间中设置的共享页表项。
  3. 读写数据: 进程可以在映射的共享内存区域进行读写操作,与普通内存一样,无需复杂的通信操作。

内核空间:

  1. 设置共享内存页表: 内核负责在共享内存的物理地址和虚拟地址之间建立映射关系,设置页表项,确保多个进程能够访问相同的物理内存。
  2. 同步和权限控制: 内核维护共享内存的元信息,包括大小、权限等。在多个进程访问时,内核会处理访问的同步和权限控制问题。
  3. 不同进程的映射: 当不同进程请求映射共享内存时,内核将相同的物理内存映射到不同的进程虚拟地址空间,使它们共享同一块内存。它们共享相同的页表项,由 TLB(Translation Lookaside Buffer,页表查找缓冲器)实现。

内核空间中的关键数据结构

共享的页表项

复制与映射

双进程分享的是共享页表项,物理地址内数据只有一份,复制次数 0

IPC-管道

不同管道类型

无名管道(Unnamed Pipe)
  • 无名管道是一种单向通信机制,只能用于父子进程或者具有共同祖先的进程之间通信
  • 创建无名管道使用的是 pipe() 系统调用。该调用返回一对文件描述符,一个用于读取,一个用于写入。
  • 无名管道的数据传输是单向的,数据写入一个描述符后可以被另一个描述符读取。
  • 在很多系统上,无名管道的缓冲区大小是固定的,通常为一页大小(例如4KB)。这意味着管道的数据容量有限,无法容纳大规模的数据。
有名管道(Named Pipe,FIFO):
  • 有名管道是一种基于文件系统的命名管道,可以用于任意进程之间通信,不受关系限制

  • 使用 mkfifo 命令或 mkfifo() 系统调用创建有名管道。它在文件系统中创建一个特殊的文件节点,进程可以像读写普通文件一样读写这个节点来进行通信。

  • 有名管道的数据传输是单向的,需要同时创建一个读取端和一个写入端。多个进程可以连接到同一个有名管道进行通信。

  • 有名管道(Named Pipe,FIFO)的缓冲区大小是由系统内核设置的,并且通常与页大小(Page Size)有关。在大多数Linux系统中,页大小通常为4KB,因此默认情况下,有名管道的缓冲区大小也会是4KB。

    某些系统可能会允许你通过特定的系统参数进行配置。具体的设置方法可能会因操作系统版本和发行版而异。

管道的指针操作,写满与阻塞

写入从头指针开始,读取从尾指针开始。写入之后,头指针挪动,读取之后尾指针挪动。
如果是头指针赶上尾指针,那么管道被写满,写就会被阻塞。如果是尾指针赶上头指针,那么管道为空,read阻塞。

用户空间与内核空间行为

用户空间:

  1. 创建管道: 进程通过系统调用在用户空间创建一个管道,得到两个文件描述符,一个用于读取,一个用于写入。
  2. 写入数据: 进程使用写入文件描述符将数据写入管道。
  3. 读取数据: 进程使用读取文件描述符从管道中读取数据。

内核空间:

  1. 管道管理: 内核维护管道的数据结构,包括缓冲区和读写指针。
  2. 数据传递: 内核通过管道将写入的数据从一个进程的写入文件描述符复制到另一个进程的读取文件描述符。
  3. 进程同步: 内核确保在多个进程访问管道时的同步,避免数据错乱。

内核空间中的关键数据结构

单向管道

复制与映射

在基本的管道(Pipe)IPC 机制中,数据实际上只涉及一次复制操作,因为管道是一个字节流传输机制,数据在管道中以字节为单位连续传输。

简单的说 Linux 是文件系统,管道也是文件,这个文件由两个指针进行操作,一头写入一头读取,当一个进程将数据写入管道时,数据直接写入这个文件内,即管道的缓冲区中,当另一个进程从管道中读取数据时,这些数据会被从管道的缓冲区读取到接收方进程的内存中,而管道不持有这些数据,数据的角度来看,实际上只有一次数据复制。

IPC-消息队列

用户空间与内核空间行为

用户空间:

  1. 创建消息队列: 进程通过系统调用在用户空间创建一个消息队列,得到一个唯一的标识符。
  2. 发送消息: 进程使用系统调用将消息发送到消息队列,包括消息类型和数据(需要通信双方约定好)。
  3. 接收消息: 进程使用系统调用从消息队列中接收消息,根据消息类型读取相应的数据。

内核空间:

  1. 消息队列管理: 内核维护消息队列的元信息,包括消息队列的状态、大小等。
  2. 消息传递: 内核将进程发送的消息复制到消息队列中,或从消息队列中复制消息给接收的进程。(跨进程消息队列两次复制
  3. 进程同步: 内核确保在多个进程访问消息队列时的同步,以避免竞态条件。

内核空间中的关键数据结构

消息队列

复制与映射

两次复制

  1. 写入数据: 当一个进程将消息写入消息队列时,消息数据会从发送方进程的内存复制到消息队列的内核缓冲区中。这是第一次复制操作。
  2. 读取数据: 在接收方进程中,数据需要从内核缓冲区复制到接收方进程的内核空间中。这是第二次复制操作。

IPC-Socket

用户空间与内核空间行为

用户空间:

  1. 创建 Socket: 进程通过系统调用在用户空间创建一个 Socket,得到一个文件描述符,用于读写数据。
  2. 发送数据: 进程使用文件描述符发送数据到指定的 Socket。
  3. 接收数据: 进程使用文件描述符从 Socket 中接收数据。

内核空间:

  1. Socket 管理: 内核维护 Socket 的数据结构,包括缓冲区、连接状态等。
  2. 数据传递: 内核通过 Socket 将进程发送的数据从一个进程的发送缓冲区复制到另一个进程的接收缓冲区。
  3. 连接管理: 内核负责管理连接的建立、维护和断开,以及处理各种网络协议。

内核空间中的关键数据结构

内核空间中的关键数据结构

  1. 发送缓冲区: 发送方进程使用 Socket 发送数据时,数据首先被复制到发送缓冲区(Send Buffer)中。这个缓冲区在内核空间中,用于临时存储待发送的数据。发送缓冲区的大小可以由操作系统参数或套接字选项进行配置。
  2. 接收缓冲区: 接收方进程使用 Socket 接收数据时,数据会被存储在接收缓冲区(Receive Buffer)中。这个缓冲区同样位于内核空间,用于临时存储接收到的数据。接收缓冲区的大小也可以通过操作系统参数或套接字选项进行配置

复制与映射

linux 2.4 内核以下

两次用户、内核态的切换,三次数据拷贝

linux 2.4 内核及其以上

两次用户、内核态的切换,两次数据拷贝

内核态零拷贝原理

数据不再被复制到 socket 关联的缓冲区中了,仅仅是将一个描述符(包含了数据的位置和长度等信息)追加到 socket 关联的缓冲区中。DMA 直接将内核中的缓冲区中的数据传输给协议引擎,消除了那一次需要 cpu 周期的数据复制。

IPC-Binder

用户空间与内核空间行为

从用户空间和内核空间的角度来看,IPC(Inter-Process Communication,进程间通信)Binder 的工作可以简洁地描述如下:

用户空间:

  1. 创建 Binder 对象: 进程通过系统调用创建 Binder 对象,通常是 Binder 类的子类实例。这个对象用于表示一个通信通道,可以用来发送和接收数据。
  2. 发送数据: 进程通过 Binder 对象将数据(通常是 Parcel 对象)发送到另一个进程。这个过程会将数据传递给内核空间的 Binder 驱动
  3. 接收数据: 进程通过 Binder 对象接收另一个进程发送的数据。接收的数据也是以 Parcel 对象的形式返回给用户空间。

内核空间:

  1. Binder 驱动: Binder 驱动位于内核空间,负责管理 Binder 通信。当进程发送数据时,数据会传递给 Binder 驱动。
  2. 数据传递: Binder 驱动将进程发送的数据从发送方进程的用户空间复制到接收方进程的用户空间,这一过程中涉及数据的复制和映射
  3. 线程池管理: Binder 驱动还管理了一个线程池,用于处理进程间的数据传递请求。这确保了数据的传递不会阻塞主线程,提高了性能。
  4. 权限和安全性: Binder 驱动实施权限和安全性控制,确保只有经过授权的进程可以进行 Binder 通信。

内核空间中的关键数据结构

指向物理内存的内核空间虚拟内存

复制与映射

复制:发送方进程将数据复制到内核空间虚拟内存中。
映射:将这个内核中的虚拟内存映射给接收方进程。

为什么 Android 要采用 Binder 作为 IPC 机制?

在下不才,这个问题,交给大佬回答

作者:Gityuan
链接:https://www.zhihu.com/question/39440766/answer/89210950
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在开始回答 前,先简单概括性地说说Linux现有的所有进程间IPC方式:

  1. 管道: 在创建时分配一个page大小的内存,缓存区大小比较有限。
  2. 消息队列: 信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信。
  3. 共享内存: 无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快,但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决。
  4. 套接字: 作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信。
  5. 信号量: 常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  6. 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等。

Android的内核也是基于Linux内核,为何不直接采用Linux现有的进程IPC方案呢,难道Linux社区那么多优秀人员都没有考虑到有Binder这样一个更优秀的方案,是google太过于牛B吗?事实是真相并非如此,请细细往下看,您就明白了。


接下来正面回答这个问题,从5个角度来展开对Binder的分析:

(1)从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。

(2)从稳定性的角度
Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。

仅仅从以上两点,各有优劣,还不足以支撑google去采用binder的IPC机制,那么更重要的原因是:

(3)从安全的角度
传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Android作为一个开放的开源体系,拥有非常多的开发平台,App来源甚广,因此手机的安全显得额外重要;对于普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。

Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。Android 6.0,也称为Android M,在6.0之前的系统是在App第一次安装时,会将整个App所涉及的所有权限一次询问,只要留意看会发现很多App根本用不上通信录和短信,但在这一次性权限权限时会包含进去,让用户拒绝不得,因为拒绝后App无法正常使用,而一旦授权后,应用便可以胡作非为。

针对这个问题,google在Android M做了调整,不再是安装时一并询问所有权限,而是在App运行过程中,需要哪个权限再弹框询问用户是否给相应的权限,对权限做了更细地控制,让用户有了更多的可控性,但同时也带来了另一个用户诟病的地方,那也就是权限询问的弹框的次数大幅度增多。对于Android M平台上,有些App开发者可能会写出让手机异常频繁弹框的App,企图直到用户授权为止,这对用户来说是不能忍的,用户最后吐槽的可不光是App,还有Android系统以及手机厂商,有些用户可能就跳果粉了,这还需要广大Android开发者以及手机厂商共同努力,共同打造安全与体验俱佳的Android手机。

Android中权限控制策略有SELinux等多方面手段,下面列举从Binder的一个角度的权限控制:
Android源码的Binder权限是如何控制? -Gityuan的回答

传统IPC只能由用户在数据包里填入UID/PID;另外,可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。从安全角度,Binder的安全性更高。

说到这,可能有人要反驳,Android就算用了Binder架构,而现如今Android手机的各种流氓软件,不就是干着这种偷窥隐射,后台偷偷跑流量的事吗?没错,确实存在,但这不能说Binder的安全性不好,因为Android系统仍然是掌握主控权,可以控制这类App的流氓行为,只是对于该采用何种策略来控制,在这方面android的确存在很多有待进步的空间,这也是google以及各大手机厂商一直努力改善的地方之一。在Android 6.0,google对于app的权限问题作为较多的努力,大大收紧的应用权限;另外,在Google举办的Android Bootcamp 2016大会中,google也表示在Android 7.0 (也叫Android N)的权限隐私方面会进一步加强加固,比如SELinux,Memory safe language(还在research中)等等,在今年的5月18日至5月20日,google将推出Android N。

话题扯远了,继续说Binder。

(4)从语言层面的角度
大家多知道Linux是基于C语言面向过程的语言,而Android是基于Java语言(面向对象的语句),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。可以从一个进程传给其它进程,让大家都能访问同一Server,就像将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。从语言层面,Binder更适合基于面向对象语言的Android系统,对于Linux系统可能会有点“水土不服”。

另外,Binder是为Android这类系统而生,而并非Linux社区没有想到Binder IPC机制的存在,对于Linux社区的广大开发人员,我还是表示深深佩服,让世界有了如此精湛而美妙的开源系统。也并非Linux现有的IPC机制不够好,相反地,经过这么多优秀工程师的不断打磨,依然非常优秀,每种Linux的IPC机制都有存在的价值,同时在Android系统中也依然采用了大量Linux现有的IPC机制,根据每类IPC的原理特性,因时制宜,不同场景特性往往会采用其下最适宜的。比如在Android OS中的Zygote进程的IPC采用的是Socket(套接字)机制,Android中的Kill Process采用的signal(信号)机制等等。而Binder更多则用在system_server进程与上层App层的IPC交互

(5) 从公司战略的角度

总所周知,Linux内核是开源的系统,所开放源代码许可协议GPL保护,该协议具有“病毒式感染”的能力,怎么理解这句话呢?受GPL保护的Linux Kernel是运行在内核空间,对于上层的任何类库、服务、应用等运行在用户空间,一旦进行SysCall(系统调用),调用到底层Kernel,那么也必须遵循GPL协议。

而Android 之父 Andy Rubin对于GPL显然是不能接受的,为此,Google巧妙地将GPL协议控制在内核空间,将用户空间的协议采用Apache-2.0协议(允许基于Android的开发商不向社区反馈源码),同时在GPL协议与Apache-2.0之间的Lib库中采用BSD证授权方法,有效隔断了GPL的传染性,仍有较大争议,但至少目前缓解Android,让GPL止步于内核空间,这是Google在GPL Linux下 开源与商业化共存的一个成功典范。

有了这些铺垫,我们再说说Binder的今世前缘

Binder是基于开源的 OpenBinder实现的,OpenBinder是一个开源的系统IPC机制,最初是由 Be Inc.开发,接着由 Palm, Inc. 公司负责开发,现在OpenBinder的作者在Google工作,既然作者在Google公司,在用户空间采用Binder 作为核心的IPC机制,再用Apache-2.0协议保护,自然而然是没什么问题,减少法律风险,以及对开发成本也大有裨益的,那么从公司战略角度,Binder也是不错的选择。

另外,再说一点关于OpenBinder,在2015年OpenBinder以及合入到Linux Kernel主线 3.19版本,这也算是Google对Linux的一点回馈吧。

综合上述5点,可知Binder是Android系统上层进程间通信的不二选择。


IPC 原理来自于

gityuan: http://gityuan.com/2015/10/31/binder-prepare/

参考资料:《计算机操作系统》

Android-Framework-AMS专项-Activity启动的整体流程与第一个阶段解读参数

Activity 启动整体流程分析

我们把 Activity 的启动简单分为几个部分

  • Intent

    startActivity(intent):
    ActivityA ActivityB

  • 解析 Activity 启动参数
    ActivityRecord

  • activityStack ActivityRecord,管理和启动 ActivityB

  • AMS:
    activityStackSupervisor: 封装 clientTransaction(触发事件)

  • 通信的流程:clientTransaction
    Application: process Activit

Activity 启动第一阶段解析参数

ActivityStarter:解析 Activity 启动参数,每一个 Activity 启动都会有一个 Starter 去启动它。
ActivityStartController:Controller 回去生产 Starter 对象,控制 Starter 的创建。

Q:为什么需要 ActivityStartController?

AMS 和 ATMS

在 Android 操作系统中,ATMS(ActivityTaskManagerService)和AMS(ActivityManagerService)是两个关键的系统服务,负责管理应用程序的生命周期和任务栈。它们在 Android 的多任务处理和应用管理方面扮演着重要角色。随着 Android 系统的发展,这两个服务的职责和架构也发生了一些变化。下面是它们之间主要的区别:

  1. 职责分工

    • AMS(ActivityManagerService):在 Android 10 及其之前的版本中,AMS 是负责管理系统中的所有活动(Activities)、应用进程和服务的核心服务。它处理应用的启动、切换、生命周期管理等任务。
    • ATMS(ActivityTaskManagerService):从 Android 10(API 级别 29)开始引入,ATMS 负责管理活动和任务的栈管理(Activity and Task Stack Management)。这意味着 ATMS 处理应用的 UI/界面任务,如任务视图、活动栈、分屏模式和最近应用的管理。ATMS 实质上是从 AMS 中拆分出来的,专注于任务和活动栈的管理,而让 AMS 能更专注于后台进程和应用管理。
  2. 架构变化

    • 随着 Android 系统的发展,其架构也在不断演进。引入 ATMS 是 Android 系统为了更好地模块化和清晰地区分不同服务的职责而做的改变。这种变化有助于简化系统服务的管理,使得系统架构更加清晰,同时也为未来的扩展和维护提供了便利。
  3. 目的

    • 分拆 AMS 和引入 ATMS 的目的是为了更好地分离和管理 Android 系统中的活动和任务栈管理逻辑,从而提高系统的稳定性和可维护性。ATMS 的引入,让 AMS 能够更加专注于进程管理和服务管理,而不是同时处理与 UI/界面相关的复杂逻辑。

总结来说,ATMS 和 AMS 在 Android 10 之后的版本中共同工作,以更高效和清晰的方式管理应用的生命周期和用户界面任务。ATMS 的引入是 Android 系统架构演进的一个例证,显示了 Android 团队在不断追求更好的系统管理和性能优化方面的努力。

ActivityStartController

Android 10 之前的版本,ActivityStartController 是在 AMS 的 initialize 时创建
Android 10 开始,AMS 分离出了一些职责到 ATMS 中,包含了 initialize

starter,factory,pool 与 recycle,reset

在这个 initalize 中初始化了 ActivityStartController
ActivityStartController 初始化时去获取 starter

实际上是由 ActivityStartController 初始化时去 创建了一个 Factory
这个 Factory 是定义在 ActivityStartController 内部的 DefaultFactory,通过 DefaultFactory 的 obtain 函数去获取一个 ActivityStarter

DefaultFactory 中构建了一个 synchronizedPool 复用池子,就和 Hanlder 一样,通过 obtain 去获取 ActivityStarter,设置三个缓存
采用享元模式去复用,避免内存抖动,同样的,使用完之后要去 recycle,recycle 的时候要去 reset,之后添加回 pool

所以 本质上是使用工厂模式去建造 ActivityStartController 缓存池,并管控 ActivityStartController 的复用,由 AMS 创建

Activity 的启动参数到底被谁持有了?

activity 的启动参数实际上是被添加到了 ActivityStarter 的 Request 中,之后执行 ActivityStarter 的 execute 函数

Request 是 Activity 的内部类,在这个内部类中包含了 Activity 启动过程中一系列的交互参数
比如我们要从 Activity A 启动 Activity B,这个过程中需要记录 Activity A 与 Activity B 的核心参数( 如果我们对 Activity A 启动 Activity B 之后,Activity A 和 B 生命周期函数的执行顺序有所了解,就非常容易理解这里为什么需要记录 A B 双方的核心参数,启动 B 的过程也是 A B 进行交互的过程)

ActivityStarter 的 execute 函数会执行 executeRequest, 这时候 ActivityStarter 就会去创建一个 ActivityRecord,这就是我们要启动的 Activity

在启动之前,会先执行 ActivityStartController 的 doPendingActivityLaunches 函数
这个函数是用来启动那些没有来得及启动的 Activity(举个例子,在启动之前按了 Home 键)

之后再调用 StartActivityUnchecked,其中调用 StartActivityInner
这里注意下 StartActivityInner 头两个参数是 final ActivityRecord r,ActivityRecord sourceRecord
这个函数里会计算 ActivityRecord (要启动的 Activity 的 task 标志),也就是启动模式,接着计算 sourceRecord(源 Activity)所在的 task,将计算后的启动模式 mLaunchFlags 设置给 Intent ( mIntent.setFlags(mLaunchFlags)

Launcher 启动创建黑白屏

注意完成上述操作之后我们去调用 Launcher 去启动一个 Activity 的时候,为了让用户知道我们已经进行了响应,会启动一个黑白屏

Q:这个时候 Activity 有启动吗?

A:并没有,上述操作只是在构建一个 Activity 的启动流程,并没有真正的去启动一个 Activity,因为 Activity 必须依附于一个进程存在,所以黑白屏是为了表示已经开始响应启动流程,但是进程的创建是一个相对缓慢的过程,之后进程创建之后才会启动 Activity

Activity的真正启动

启动完黑白屏之后,通过调用 mRootWindowContainer.resumeFocusedStackTopActivities 启动 Activity

首先,什么是 RootWindowContainer,注释上写 RootWindowContainer 就是 WindowContainer 的根容器,管理了所有窗口容器,
设备上所有的窗口(Window)、显示(Display)都是由它管理的 它通过 RootWindowContainer 来检查当前任务栈顶部的 Activity 是否真的可见。

在这个过程中,RootWindowContainer 会考虑多个因素来确定一个 Activity 的可见性,包括但不限于:

  • 屏幕的显示状态:例如,如果设备处于锁屏状态,则处于前台的 Activity 可能不会被认为是可见的。
  • 其他应用的窗口:例如,如果有一个全屏模式的应用或是一个浮动窗口覆盖在当前 Activity 上方,可能会影响当前 Activity 的可见性。
  • 系统对话框或通知:这些元素也可能覆盖在活动 Activity 上方,影响其可见性。
  • 分屏模式:在分屏模式下,两个 Activity 可以同时处于可见状态,RootWindowContainer 需要管理这些 Activity 的可见性和布局。

基于上述因素的判断,如果 RootWindowContainer 确定任务栈顶部的 Activity 是可见的,resumeFocusedStackTopActivities 方法就会继续执行,恢复该 Activity。这个过程涉及调用 ActivityonResume() 方法,使其成为用户可以与之交互的活动界面。

这一机制确保了 Android 系统在恢复 Activity 时能够正确地管理用户界面的状态和交互,保持应用的流畅运行和用户体验的一致性。

当启动一个新的 Activity 时,resumeFocusedStackTopActivities 方法负责将这个新的 Activity 放置到正确的任务栈和位置上。如果判断结果为 !resumeOnDisplay,并且 focusedStack != null,那么就会执行 focusedStack.resumeTopActivityUncheckLocked,进入启动的第二阶段

从零开始写一个 ARouter - router-annotation 路由注解模块

  • ARouter
    • router-annotation 路由注解模块

    • arouter-api ARouter框架的API模块

      • src.main.java
        • com.alibaba.android.arouter
          • base 基础功能相关的包
            • UniqueKeyTreeMap.java 唯一键树形映射的类
          • core 核心功能相关的包
            • AutowiredLifecyleCallback.java(废弃) 自动注入生命周期回调的类
            • AutowiredServiceImpl.java 自动注入服务的实现类
            • InstrumentationHook.java(废弃) 仪表钩子的类
            • InterceptorServiceImpl.java 拦截器服务的实现类
            • LogisticsCenter.java 物流中心的类
            • Warehouse.java 仓库的类
          • exception 异常相关的包
            • HandlerException.java 处理异常的类
            • InitException.java 初始化异常的类
            • NoRouteFoundException.java 未找到路由的异常类
          • facade 提供各种服务和回调的包
            • callback 回调相关的包
              • InterceptorCallback.java 拦截器回调的接口
              • NavigationCallback.java 导航回调的接口
              • NoRouteFoundException.java 未找到路由的异常接口
          • service 服务相关的包
            • AutowiredService.java 自动注入服务的接口
            • ClassLoaderService.java 类加载器服务的接口
            • DegradeService.java 降级服务的接口
            • InterceptorService.java 拦截器服务的接口
            • PathReplaceService.java 路径替换服务的接口
            • PretreatmentService.java 预处理服务的接口
            • SerializationService.java 序列化服务的接口
          • template 模板相关的包
            • IInterceptor.java 拦截器接口
            • IInterceptorGroup.java 拦截器分组接口
            • Ilogger.java 日志记录器接口
            • IPolicy.java 策略接口
            • IProvider.java 提供者接口
            • IProviderGroup.java 提供者分组接口
            • IRouteGroup.java 路由分组接口
            • IRouteRoot.java 路由根接口
            • Isyringe.java 注射器接口
            • Postcard.java 路由信息封装类
          • launcher 启动器: 包含一些用于启动ARouter框架的类和线程管理相关的类。
            • _Arouter.java ARouter框架的内部启动类,用于初始化ARouter。
            • Arouter.java ARouter框架的启动类,用于初始化ARouter。
          • thread (线程)包含了与线程管理相关的类。
            • CancelableCountDownLatch.java 可取消的倒计时计数器,用于线程同步。
            • DefaultPoolExecutor.java 默认的线程池执行器,用于执行异步任务。
            • DefaultThreadFactory.java 默认线程工厂,用于创建线程。
          • utils 工具类。
            • ClassUtils.java 用于操作类的实用工具类。
            • Consts.java 包含一些常量值的类。
            • DefaultLogger.java 默认的日志记录器类。
            • MapUtils.java 用于操作地图数据的实用工具类。
            • PackageUtils.java 用于操作包信息的实用工具类。
            • TextUtils.java 用于操作文本数据的实用工具类。
    • arouter-compiler

      • src.main.java
        • com.alibaba.android.arouter
          • compiler 编译相关的包
            • entity 实体类相关的包
              • RouteDoc.java 路由文档实体类
            • processor 处理器相关的包
              • AutowiredProcessor.java 自动注入处理器
              • BaseProcessor.java 基础处理器
              • InterceptorProcessor.java 拦截器处理器
              • RouteProcessor.java 路由处理器
            • utils 工具类相关的包
              • Consts.java 常量类
              • Logger.java 日志记录器类
              • TypeUtils.java 类型工具类

router-annotation 路由注解模块

annotation 存放各种注解类的包

Autowired

自动注入的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.alibaba.android.arouter.facade.annotation

/**
* Annotation for field, which need autowired.
* 用于需要自动注入的字段的注解。
*
* @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2017/2/20 下午4:26
*/
@Target(AnnotationTarget.FIELD) // 表示该注解只能用于字段上
@kotlin.annotation.Retention(AnnotationRetention.BINARY) // 表示该注解的生命周期为编译期
annotation class Autowired( // Mark param's name or service name.
// 标记参数的名称或服务名称
val name: String = "", // If required, app will be crash when value is null.
// Primitive type wont be check!
// 如果设置为true,在数值为null时应用程序将崩溃。不会检查原始类型!
val required: Boolean = false, // Description of the field
// 字段的描述信息
val desc: String = ""
)

标记需要注入的字段:在你的类中,标记需要进行自动注入的字段。使用 @Autowired 注解,例如:

1
2
3
4
5
6
7
8
9
10
import com.alibaba.android.arouter.facade.annotation.Autowired;

// ...

public class MyClass {
@Autowired
private MyDependency myDependency;

// ...
}

在这个示例中,myDependency 字段被标记为需要自动注入的依赖对象。

假设你有两个相同类型的依赖对象 MyDependency,但它们在功能上略有不同,你需要指定要注入哪一个。

1
2
3
4
5
6
7
8
9
10
11
12
javaCopy code
import com.alibaba.android.arouter.facade.annotation.Autowired;

public class MyClass {
@Autowired(name = "dependencyA")
private MyDependency dependencyA;

@Autowired(name = "dependencyB")
private MyDependency dependencyB;

// ...
}
Interceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.alibaba.android.arouter.facade.annotation

/**
* 标记拦截器以拦截路由。
* 注意:此注解仅可用于标记#{IInterceptor}的实现!!!
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 16/8/23 14:03
*/
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) // 表示该注解只能用于类上
@kotlin.annotation.Retention(AnnotationRetention.BINARY) // 表示该注解的生命周期为编译期
annotation class Interceptor(
/**
* 拦截器的优先级,ARouter将按优先级执行它们。
*/
val priority: Int,
/**
* 拦截器的名称,可能用于生成javadoc。
*/
val name: String = "Default"
)

要使用 @Interceptor 注解,你需要将其标记在实现了 IInterceptor 接口的类上,以定义一个拦截器。然后,你可以使用这个拦截器来拦截路由操作。下面是使用 @Interceptor 注解的一般步骤:

创建一个实现了 IInterceptor 接口的类,该类将用作拦截器。确保类实现了接口中的 process 方法,该方法定义了拦截器的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kotlinCopy code
import com.alibaba.android.arouter.facade.annotation.Interceptor
import com.alibaba.android.arouter.facade.template.IInterceptor
import com.alibaba.android.arouter.facade.callback.InterceptorCallback

@Interceptor(priority = 1, name = "ExampleInterceptor")
class ExampleInterceptor : IInterceptor {
override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
// 在这里编写拦截器的逻辑
// 可以在进入路由之前或之后执行一些操作

// 最后,调用 callback.onContinue(postcard) 继续路由操作,或者 callback.onInterrupt(exception) 中断路由操作
callback?.onContinue(postcard)
}
}
Param(废弃)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.alibaba.android.arouter.facade.annotation

/**
* 用于标记页面参数的注解。
* 此注解已被弃用,请使用 'Autowired' 代替!
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 2016/11/22 18:01
*/
@Target(AnnotationTarget.FIELD) // 表示该注解只能用于字段上
@kotlin.annotation.Retention(AnnotationRetention.BINARY) // 表示该注解的生命周期为编译期
@Deprecated("") // 表示该注解已被弃用
annotation class Param(
/**
* 字段名称
*/
val name: String = "",
/**
* 字段的描述信息
*/
val desc: String = "No desc."
)

既已废弃,不浪费时间

Route
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.alibaba.android.arouter.facade.annotation

/**
* 标记页面可由路由器路由。
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 16/8/15 下午9:29
*/
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) // 表示该注解只能用于类上
@kotlin.annotation.Retention(AnnotationRetention.BINARY) // 表示该注解的生命周期为编译期
annotation class Route(
/**
* 路由的路径
*/
val path: String,
/**
* 用于合并路由,组名必须使用常见单词!!!
*/
val group: String = "",
/**
* 路由的名称,用于生成javadoc。
*/
val name: String = "",
/**
* 额外的数据,可以由用户设置。
* 注意:您应该使用整数数值表示开关,通过位进行标记。例如:10001010101010
*/
val extras: Int = Int.MIN_VALUE,
/**
* 路由的优先级。
*/
val priority: Int = -1
)

这个类定义了一个用于标记页面可由路由器路由的注解 @Route。该注解包含了五个元素,分别表示路由的路径、组名、名称、额外数据和优先级。

创建一个页面类,并在该类上添加 @Route 注解,以标记这个页面可以被路由器路由。

1
2
3
4
5
6
7
import com.alibaba.android.arouter.facade.annotation.Route

// 使用 @Route 注解标记这个页面
@Route(path = "/example/activity")
class ExampleActivity : AppCompatActivity() {
// 页面的代码...
}

在上面的示例中,我们创建了一个名为 ExampleActivity 的页面类,并使用 @Route 注解标记了这个页面的路由路径为 "/example/activity"

enums 包含各种枚举类型的包

RouteType
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.alibaba.android.arouter.facade.enums

/**
* 路由类型的枚举。
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 16/8/23 22:33
*/
enum class RouteType(// 未知类型的路由
var id: Int, var className: String
) {
ACTIVITY(0, "android.app.Activity"), // Activity类型的路由
SERVICE(1, "android.app.Service"), // Service类型的路由
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"), // Provider类型的路由
CONTENT_PROVIDER(-1, "android.app.ContentProvider"), // ContentProvider类型的路由
BOARDCAST(-1, ""), // Broadcast类型的路由
METHOD(-1, ""), // Method类型的路由
FRAGMENT(-1, "android.app.Fragment"), // Fragment类型的路由
UNKNOWN(-1, "Unknown route type");

fun setId(id: Int): RouteType {
this.id = id
return this
}

fun setClassName(className: String): RouteType {
this.className = className
return this
}

companion object {
fun parse(name: String): RouteType {
for (routeType in values()) {
if (routeType.className == name) {
return routeType
}
}
return UNKNOWN
}
}
}
TypeKind
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.alibaba.android.arouter.facade.enums

/**
* 字段类型的种类枚举。
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 2017-03-16 19:13:38
*/
enum class TypeKind {
// 基本类型
BOOLEAN, // 布尔类型
BYTE, // 字节类型
SHORT, // 短整型
INT, // 整型
LONG, // 长整型
CHAR, // 字符类型
FLOAT, // 单精度浮点型
DOUBLE, // 双精度浮点型

// 其他类型
STRING, // 字符串类型
SERIALIZABLE, // 可序列化类型
PARCELABLE, // Parcelable类型
OBJECT // 对象类型
}

model 包含模型类的包

RouteMeta
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package com.alibaba.android.arouter.facade.model

import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.miao.router.RouteType

/**
* 包含基本路由信息的类。
*
* @author Alex [联系我。](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 16/8/24 09:45
*/
class RouteMeta {
var type: RouteType? = null // 路由类型
private set

private var rawType: javax.lang.model.element.Element? = null // 路由的原始类型

var destination : Class<*>? = null // 目标类
private set

var path : String? = null // 路由路径
private set

var group: String? = null // 路由分组
private set

var priority = -1 // 优先级,数值越小优先级越高
private set

var extra = 0// 额外数据
private set

var paramsType: Map<String?, Int?>? = null // 参数类型
private set

var name: String? = null

var injectConfig: Map<String, Autowired>? = null // 缓存注入配置信息

constructor() {}

/**
* Type
*
* @param route 路由
* @param destination 目标类
* @param type 类型
*/
constructor(
route: Route,
destination: Class<*>?,
type: RouteType?
) : this(
type,
null,
destination,
route.name(),
route.path(),
route.group(),
null,
route.priority(),
route.extras()
) {
}

/**
* Type
*
* @param route 路由
* @param rawType 原始类型
* @param type 类型
* @param paramsType 参数类型
*/
constructor(
route: Route,
rawType: javax.lang.model.element.Element?,
type: RouteType?,
paramsType: Map<String?, Int?>?
) : this(
type,
rawType,
null,
route.name(),
route.path(),
route.group(),
paramsType,
route.priority(),
route.extras()
) {
}

/**
* Type
*
* @param type 类型
* @param rawType 原始类型
* @param destination 目标类
* @param path 路径
* @param group 分组
* @param paramsType 参数类型
* @param priority 优先级
* @param extra 额外数据
*/
constructor(
type: RouteType?,
rawType: javax.lang.model.element.Element?,
destination: Class<*>?,
name: String?,
path: String?,
group: String?,
paramsType: Map<String?, Int?>?,
priority: Int,
extra: Int
) {
this.type = type
this.name = name
this.destination = destination
this.rawType = rawType
this.path = path
this.group = group
this.paramsType = paramsType
this.priority = priority
this.extra = extra
}

fun setParamsType(paramsType: Map<String?, Int?>?): RouteMeta {
this.paramsType = paramsType
return this
}

fun getRawType(): javax.lang.model.element.Element? {
return rawType
}

fun setRawType(rawType: javax.lang.model.element.Element?): RouteMeta {
this.rawType = rawType
return this
}

fun setType(type: RouteType?): RouteMeta {
this.type = type
return this
}

fun setDestination(destination: Class<*>?): RouteMeta {
this.destination = destination
return this
}

fun setPath(path: String?): RouteMeta {
this.path = path
return this
}

fun setGroup(group: String?): RouteMeta {
this.group = group
return this
}

fun setPriority(priority: Int): RouteMeta {
this.priority = priority
return this
}

fun setExtra(extra: Int): RouteMeta {
this.extra = extra
return this
}

override fun toString(): String {
return "RouteMeta{" +
"type=" + type +
", rawType=" + rawType +
", destination=" + destination +
", path='" + path + '\'' +
", group='" + group + '\'' +
", priority=" + priority +
", extra=" + extra +
", paramsType=" + paramsType +
", name='" + name + '\'' +
'}'
}

companion object {
/**
* For versions of 'compiler' less than 1.0.7, contain 1.0.7
*
* @param type 类型
* @param destination 目标类
* @param path 路径
* @param group 分组
* @param priority 优先级
* @param extra 额外数据
* @return this
*/
fun build(
type: RouteType?,
destination: Class<*>?,
path: String?,
group: String?,
priority: Int,
extra: Int
): RouteMeta {
return RouteMeta(type, null, destination, null, path, group, null, priority, extra)
}

/**
* For versions of 'compiler' greater than 1.0.7
*
* @param type 类型
* @param destination 目标类
* @param path 路径
* @param group 分组
* @param paramsType 参数类型
* @param priority 优先级
* @param extra 额外数据
* @return this
*/
fun build(
type: RouteType?,
destination: Class<*>?,
path: String?,
group: String?,
paramsType: Map<String?, Int?>?,
priority: Int,
extra: Int
): RouteMeta {
return RouteMeta(
type,
null,
destination,
null,
path,
group,
paramsType,
priority,
extra
)
}
}
}

private RouteType type:表示路由的类型,可能是普通路由、自动注入、或者其他类型。
private Element rawType:表示路由的原始类型的元素。
private Class<?> destination:表示目标类,即路由到哪个页面。
private String path:表示路由的路径,用于唯一标识一个路由。
private String group:表示路由的分组,通常用于组织路由。
private int priority:表示路由的优先级,数值越小表示优先级越高。
private int extra:表示额外的数据,可以用于传递一些标记或开关。
private Map<String, Integer> paramsType:表示参数类型的映射。
private String name:表示路由的名称。

RouteMeta 是 Route 原子信息与其构建类

TypeWrapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.alibaba.android.arouter.facade.model

import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
* 用于获取目标对象的类型。
*
* @param <T> 泛型类型
* @since 17/10/26 11:56:22
</T> */
class TypeWrapper<T> protected constructor() {
/**
* 获取目标对象的类型。
*
* @return 目标对象的类型
*/
val type: Type // 存储目标对象的类型信息

init {
val superClass = javaClass.genericSuperclass
type =
(superClass as ParameterizedType).actualTypeArguments[0] // 获取泛型类型参数
}
}

通过 Java 反射获取当前类的父类,然后从父类中获取泛型参数的类型信息,并将其赋值给 type 字段。这样,这个类就能够存储目标对象的泛型类型信息。

从零开始写一个 ARouter - base 基础功能 与 core 核心功能

  • ARouter
    • router-annotation 路由注解模块

      • src.main.java
        • com.alibaba.android.arouter
          • facade 提供注解和枚举的包
            • annotation 存放各种注解类的包
              • Autowired.java 自动注入的注解
              • Interceptor.java 拦截器的注解
              • Param.java(废弃) 参数注解: 被 Autowired 淘汰
              • Route.java 路由信息注解
            • enums 包含各种枚举类型的包
              • RouteType.java 路由类型的枚举
              • TypeKind.java 类型种类的枚举
            • model 包含模型类的包
              • RouteMeta.java 路由元信息的模型类
              • TypeWrapper.java 存储目标对象的泛型类型信息的类
    • arouter-api ARouter框架的API模块

      • src.main.java
        • com.alibaba.android.arouter
          • base 基础功能相关的包

          • core 核心功能相关的包

          • exception 异常相关的包

            • HandlerException.java 处理异常的类
            • InitException.java 初始化异常的类
            • NoRouteFoundException.java 未找到路由的异常类
          • facade 提供各种服务和回调的包

            • callback 回调相关的包
              • InterceptorCallback.java 拦截器回调的接口
              • NavigationCallback.java 导航回调的接口
              • NoRouteFoundException.java 未找到路由的异常接口
          • service 服务相关的包

            • AutowiredService.java 自动注入服务的接口
            • ClassLoaderService.java 类加载器服务的接口
            • DegradeService.java 降级服务的接口
            • InterceptorService.java 拦截器服务的接口
            • PathReplaceService.java 路径替换服务的接口
            • PretreatmentService.java 预处理服务的接口
            • SerializationService.java 序列化服务的接口
          • template 模板相关的包

            • IInterceptor.java 拦截器接口
            • IInterceptorGroup.java 拦截器分组接口
            • Ilogger.java 日志记录器接口
            • IPolicy.java 策略接口
            • IProvider.java 提供者接口
            • IProviderGroup.java 提供者分组接口
            • IRouteGroup.java 路由分组接口
            • IRouteRoot.java 路由根接口
            • Isyringe.java 注射器接口
            • Postcard.java 路由信息封装类
          • launcher 启动器: 包含一些用于启动ARouter框架的类和线程管理相关的类。

            • _Arouter.java ARouter框架的内部启动类,用于初始化ARouter。
            • Arouter.java ARouter框架的启动类,用于初始化ARouter。
          • thread (线程)包含了与线程管理相关的类。

            • CancelableCountDownLatch.java 可取消的倒计时计数器,用于线程同步。
            • DefaultPoolExecutor.java 默认的线程池执行器,用于执行异步任务。
            • DefaultThreadFactory.java 默认线程工厂,用于创建线程。
          • utils 工具类。

            • ClassUtils.java 用于操作类的实用工具类。
            • Consts.java 包含一些常量值的类。
            • DefaultLogger.java 默认的日志记录器类。
            • MapUtils.java 用于操作地图数据的实用工具类。
            • PackageUtils.java 用于操作包信息的实用工具类。
            • TextUtils.java 用于操作文本数据的实用工具类。
          • arouter-compiler

            • src.main.java
              • com.alibaba.android.arouter
                • compiler 编译相关的包
                  • entity 实体类相关的包
                    • RouteDoc.java 路由文档实体类
                  • processor 处理器相关的包
                    • AutowiredProcessor.java 自动注入处理器
                    • BaseProcessor.java 基础处理器
                    • InterceptorProcessor.java 拦截器处理器
                    • RouteProcessor.java 路由处理器
                  • utils 工具类相关的包
                    • Consts.java 常量类
                    • Logger.java 日志记录器类
                    • TypeUtils.java 类型工具类

arouter-api ARouter框架的API模块

base 基础功能相关的包

UniqueKeyTreeMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.alibaba.android.arouter.base

import java.util.*

/**
* TreeMap with unique key.
*
* @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2017/2/22 下午5:01
*/
class UniqueKeyTreeMap<K, V>(private val tipText: String) :
TreeMap<K, V>() {
override fun put(key: K, value: V): V? {
return if (containsKey(key)) {
throw RuntimeException(String.format(tipText, key))
} else {
super.put(key, value)
}
}
}

core 核心功能相关的包

AutowiredLifecyleCallback(废弃)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.alibaba.android.arouter.core

import android.annotation.TargetApi
import android.app.Activity
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Build
import android.os.Bundle
import com.alibaba.android.arouter.launcher.ARouter

/**
* LifecycleCallback for autowired.
*
* @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2017/2/21 上午11:28
*/
@Deprecated("")
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
class AutowiredLifecycleCallback : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
ARouter.getInstance().inject(activity)
}

override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
}

已经废弃,不废话

AutowiredServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package com.alibaba.android.arouter.core

import android.content.Context
import android.util.LruCache
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.facade.service.AutowiredService
import com.alibaba.android.arouter.facade.template.ISyringe
import com.alibaba.android.arouter.utils.Consts.SUFFIX_AUTOWIRED

/**
* 自动注入服务的实现。
*
* 作者: zhilong [联系我](mailto:zhilong.lzl@alibaba-inc.com)
* 版本: 1.0
* 创建日期: 2017年2月28日 下午6:08
*/
@Route(path = "/arouter/service/autowired")
class AutowiredServiceImpl : AutowiredService {
private var classCache: LruCache<String, ISyringe>? = null
private var blackList: MutableList<String>? = null

/**
* 初始化方法。
*
* @param context 上下文
*/
fun init(context: Context?) {
classCache = LruCache<String, ISyringe>(50)
blackList = ArrayList()
}

/**
* 自动注入方法。
*
* @param instance 要进行注入的实例
*/
fun autowire(instance: Any) {
doInject(instance, null)
}

/**
* 递归注入方法。
*
* @param instance 调用该方法的实例
* @param parent 父类的Class
*/
private fun doInject(instance: Any, parent: Class<*>?) {
// 如果提供了父类的 Class 对象,则使用父类的 Class;否则,使用实例的 Class
val clazz = parent ?: instance.javaClass
// 获取适用于当前类的注入器实例
val syringe: ISyringe? = getSyringe(clazz)
// 如果存在适用的注入器实例
if (null != syringe) {
// 使用注入器进行注入
syringe.inject(instance)
}
// 获取当前类的父类的 Class 对象
val superClazz = clazz.superclass
// 如果存在父类,并且父类不是 Android 框架类
if (null != superClazz && !superClazz.name.startsWith("android")) {
// 递归调用 doInject 方法,继续为父类进行自动注入
doInject(instance, superClazz)
}
}

/**
* 获取注入器实例。
*
* @param clazz 类的Class
* @return 注入器实例
*/
private fun getSyringe(clazz: Class<*>): ISyringe? {
// 获取类的完整名称
val className = clazz.name
try {
// 检查类名是否在黑名单中,如果不在,则尝试获取对应的注入器
if (!blackList!!.contains(className)) {
// 从缓存中尝试获取注入器实例
var syringeHelper: ISyringe? = classCache!![className]
// 如果缓存中没有该注入器实例
if (null == syringeHelper) {
// 动态加载并创建注入器实例
syringeHelper = Class.forName(
clazz.name + SUFFIX_AUTOWIRED
).getConstructor()
.newInstance() as ISyringe
// 将创建的注入器实例缓存起来,以便下次使用
classCache!!.put(className, syringeHelper)
}
// 返回获取到的注入器实例
return syringeHelper
}
} catch (e: Exception) {
// 如果在获取注入器的过程中发生异常,将类名添加到黑名单中,表示该实例无需自动注入
blackList!!.add(className)
}
// 如果无法获取注入器实例,返回 null
return null
}
}

doInject 方法用于自动注入依赖到给定的实例(instance)中。

InstrumentationHook(废弃)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.alibaba.android.arouter.core

import android.app.Activity
import android.app.Instrumentation
import android.content.Intent
import com.alibaba.android.arouter.launcher.ARouter
import com.alibaba.android.arouter.utils.Consts
import com.alibaba.android.arouter.utils.TextUtils

/**
* 使用 ARouter.getInstance().inject(this) 来进行注入!
*
* Hook Instrumentation,为 Activity 的字段注入值。
* 仅支持普通 Activity,不包括单元测试。
*
* 作者:Alex [联系我](mailto:zhilong.liu@aliyun.com)
* 版本:1.0
* 创建日期:2016年11月24日 16:42
*/
@Deprecated("")
class InstrumentationHook : Instrumentation() {
/**
* Hook Instrumentation 的 newActivity 方法,进行注入。
*
* 执行进程的 [Activity] 对象的实例化。默认实现提供正常的系统行为。
*
* @param cl 用于实例化对象的类加载器。
* @param className 实现 Activity 对象的类的名称。
* @param intent 指定要实例化的活动类的 Intent 对象。
* @return 新创建的 Activity 对象。
*/
@Throws(
InstantiationException::class,
IllegalAccessException::class,
ClassNotFoundException::class
)
override fun newActivity(
cl: ClassLoader, className: String,
intent: Intent
): Activity {

// return (Activity)cl.loadClass(className).newInstance();
// 使用类加载器加载目标 Activity 类的定义
val targetActivity = cl.loadClass(className)
// 创建目标 Activity 的实例
val instanceOfTarget = targetActivity.newInstance()
// 检查是否可以自动注入
if (ARouter.canAutoInject()) {
// 从 Intent 中获取自动注入的参数
val autoInjectParams = intent.getStringArrayExtra(ARouter.AUTO_INJECT)
if (null != autoInjectParams && autoInjectParams.isNotEmpty()) {
// 遍历参数列表
for (paramsName in autoInjectParams) {
// 从 Intent 的 extras 中获取参数值
val value = intent.extras!![TextUtils.getLeft(paramsName)]
if (null != value) {
try {
// 获取目标 Activity 中的字段
val injectField =
targetActivity.getDeclaredField(TextUtils.getLeft(paramsName))
// 设置字段可访问
injectField.isAccessible = true
// 将提取的值注入到目标 Activity 的字段中
injectField[instanceOfTarget] = value
} catch (e: Exception) {
// 记录错误日志,包括异常信息
ARouter.logger.error(
Consts.TAG,
"为 Activity 注入值时发生错误![" + e.message + "]"
)
}
}
}
}
}
// 返回已注入值的目标 Activity 实例
return instanceOfTarget as Activity
}
}

已废弃,不废话

InterceptorServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package com.alibaba.android.arouter.core

import android.content.Context
import com.alibaba.android.arouter.exception.HandlerException
import com.alibaba.android.arouter.facade.Postcard
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.facade.callback.InterceptorCallback
import com.alibaba.android.arouter.facade.service.InterceptorService
import com.alibaba.android.arouter.facade.template.IInterceptor
import com.alibaba.android.arouter.launcher.ARouter.logger
import com.alibaba.android.arouter.thread.CancelableCountDownLatch
import com.alibaba.android.arouter.utils.Consts.TAG
import com.alibaba.android.arouter.utils.MapUtils
import java.util.concurrent.TimeUnit

/**
* 所有拦截器的实现类
*
* @author zhilong [联系我](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2017/2/23 下午2:09
*/
@Route(path = "/arouter/service/interceptor")
class InterceptorServiceImpl : InterceptorService {
/**
* 执行拦截操作
*
* @param postcard 路由信息,包含了路由的相关信息和配置。
* @param callback 拦截回调,用于在拦截器流程中通知下一步操作。
*/
fun doInterceptions(postcard: Postcard, callback: InterceptorCallback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
// 检查拦截器是否已经初始化
checkInterceptorsInitStatus()

// 如果拦截器未初始化完成,中断导航并抛出异常
if (!interceptorHasInit) {
callback.onInterrupt(HandlerException("拦截器初始化花费太长时间。"))
return
}

// 在后台线程中执行拦截器操作
LogisticsCenter.executor.execute(Runnable {
val interceptorCounter = CancelableCountDownLatch(Warehouse.interceptors.size())
try {
// 依次执行拦截器
_execute(0, interceptorCounter, postcard)

// 等待拦截器执行完成,超时时间由路由配置决定
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS)

// 如果有未执行的拦截器,取消导航
if (interceptorCounter.getCount() > 0) { // 如果没有返回任何内容,取消导航。
callback.onInterrupt(HandlerException("拦截器处理超时。"))
} else if (null != postcard.getTag()) { // 如果标签中有异常信息。
callback.onInterrupt(postcard.getTag() as Throwable)
} else {
// 所有拦截器执行完毕,继续路由导航
callback.onContinue(postcard)
}
} catch (e: Exception) {
// 拦截器执行过程中出现异常,中断导航并传递异常信息
callback.onInterrupt(e)
}
})
} else {
// 没有注册拦截器,直接继续路由导航
callback.onContinue(postcard)
}
}

/**
* 初始化拦截器
*
* @param context 上下文对象,用于在拦截器初始化时可能需要的上下文信息。
*/
fun init(context: Context?) {
LogisticsCenter.executor.execute(Runnable {
// 检查是否有已注册的拦截器
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for ((_, interceptorClass): Map.Entry<Int?, Class<out IInterceptor?>> in Warehouse.interceptorsIndex.entrySet()) {
try {
// 实例化拦截器
val iInterceptor: IInterceptor =
interceptorClass.getConstructor().newInstance()

// 调用拦截器的初始化方法,传入上下文信息
iInterceptor.init(context)

// 将拦截器添加到仓库中,以供后续使用
Warehouse.interceptors.add(iInterceptor)
} catch (ex: Exception) {
// 如果初始化过程中发生异常,抛出自定义的异常
throw HandlerException(TAG.toString() + "ARouter初始化拦截器错误!名称 = [" + interceptorClass.name + "],原因 = [" + ex.message + "]")
}
}

// 标记拦截器已经初始化完成
interceptorHasInit = true

// 记录日志,表示拦截器初始化已完成
logger.info(TAG, "ARouter拦截器初始化完成。")

// 通知等待的线程,拦截器已经初始化完成
synchronized(interceptorInitLock) { interceptorInitLock.notifyAll() }
}
})
}

companion object {
private var interceptorHasInit = false
private val interceptorInitLock = Any()

/**
* 执行拦截器
*
* @param index 当前拦截器索引
* @param counter 拦截器计数器
* @param postcard 路由信息
*/
private fun _execute(index: Int, counter: CancelableCountDownLatch, postcard: Postcard) {
if (index < Warehouse.interceptors.size()) {
val iInterceptor: IInterceptor = Warehouse.interceptors.get(index)
iInterceptor.process(postcard, object : InterceptorCallback() {
fun onContinue(postcard: Postcard) {
// 最后一个拦截器执行完成,没有异常。
counter.countDown()
_execute(
index + 1,
counter,
postcard
) // 当计数器减少时,继续执行,但是索引大于拦截器的大小,此时不会继续执行。
}

fun onInterrupt(exception: Throwable?) {
// 最后一个拦截器执行出现严重异常。
postcard.setTag(
exception ?: HandlerException("没有消息。")
) // 保存异常消息以备份。
counter.cancel()
// 注意,可能回调中的线程已经更改,
// 然后捕获块(L207)将无效。
// 最糟糕的情况是线程更改为主线程,然后如果抛出此异常,则应用程序将崩溃!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // 如果线程是主线程,则不应抛出异常。
// throw new HandlerException(exception.getMessage());
// }
}
})
}
}

private fun checkInterceptorsInitStatus() {
synchronized(interceptorInitLock) {
while (!interceptorHasInit) {
try {
interceptorInitLock.wait((10 * 1000).toLong())
} catch (e: InterruptedException) {
throw HandlerException(TAG.toString() + "拦截器初始化花费太长时间错误!原因 = [" + e.message + "]")
}
}
}
}
}
}

这个类是ARouter(Android路由框架)的核心拦截器服务实现类,它具有以下作用:

  1. 拦截器管理: 该类负责管理ARouter框架中的拦截器。拦截器是在路由导航过程中执行的一系列操作,用于处理路由请求或者对路由进行拦截和修改。
  2. 拦截器执行: 通过doInterceptions方法,该类执行了一系列的拦截器操作。在路由导航之前,它会依次调用已注册的拦截器,检查是否需要拦截路由请求或对请求进行修改。如果有拦截器拦截了请求,将触发拦截器的onInterrupt方法,否则,将继续执行下一个拦截器,直到所有拦截器都完成或者发生了异常。
  3. 拦截器初始化: 通过init方法,该类还负责初始化所有的拦截器。在ARouter框架初始化的过程中,会注册各种拦截器,这些拦截器需要在合适的时机进行初始化,以便在路由导航时能够正确地执行。
  4. 线程控制: 该类使用CancelableCountDownLatch来管理拦截器的执行,确保在所有拦截器执行完成或者超时时能够继续路由导航操作。同时,它还使用锁来控制拦截器的初始化过程,以确保在初始化完成之前不会执行路由导航。
LogisticsCenter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
package com.alibaba.android.arouter.core

import android.content.Context
import android.net.Uri
import com.alibaba.android.arouter.exception.HandlerException
import com.alibaba.android.arouter.exception.NoRouteFoundException
import com.alibaba.android.arouter.facade.Postcard
import com.alibaba.android.arouter.facade.enums.TypeKind
import com.alibaba.android.arouter.facade.model.RouteMeta
import com.alibaba.android.arouter.facade.template.IInterceptorGroup
import com.alibaba.android.arouter.facade.template.IProvider
import com.alibaba.android.arouter.facade.template.IProviderGroup
import com.alibaba.android.arouter.facade.template.IRouteGroup
import com.alibaba.android.arouter.facade.template.IRouteRoot
import com.alibaba.android.arouter.launcher.ARouter
import com.alibaba.android.arouter.launcher.ARouter.logger
import com.alibaba.android.arouter.utils.ClassUtils
import com.alibaba.android.arouter.utils.Consts
import com.alibaba.android.arouter.utils.Consts.AROUTER_SP_CACHE_KEY
import com.alibaba.android.arouter.utils.Consts.AROUTER_SP_KEY_MAP
import com.alibaba.android.arouter.utils.Consts.DOT
import com.alibaba.android.arouter.utils.Consts.ROUTE_ROOT_PAKCAGE
import com.alibaba.android.arouter.utils.Consts.SDK_NAME
import com.alibaba.android.arouter.utils.Consts.SEPARATOR
import com.alibaba.android.arouter.utils.Consts.SUFFIX_INTERCEPTORS
import com.alibaba.android.arouter.utils.Consts.SUFFIX_PROVIDERS
import com.alibaba.android.arouter.utils.Consts.SUFFIX_ROOT
import com.alibaba.android.arouter.utils.Consts.TAG
import com.alibaba.android.arouter.utils.MapUtils
import com.alibaba.android.arouter.utils.PackageUtils
import com.alibaba.android.arouter.utils.TextUtils
import java.lang.Boolean
import java.lang.reflect.InvocationTargetException
import java.util.*
import java.util.concurrent.ThreadPoolExecutor
import kotlin.Exception
import kotlin.Int
import kotlin.String
import kotlin.Throwable
import kotlin.Throws
import kotlin.arrayOf

/**
* LogisticsCenter 包含了所有的路由映射信息。
*
* 1. 在首次使用时创建实例。
* 2. 处理多模块之间的映射关系(*)
* 3. 解决重复组定义的复杂逻辑
*
* @author Alex [联系我](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 16/8/23 15:02
*/
object LogisticsCenter {
private var mContext: Context? = null
var executor: ThreadPoolExecutor? = null
private var registerByPlugin = false

/**
* arouter-auto-register 插件将在这个方法中生成代码
* 调用这个方法来注册所有的路由、拦截器和提供者
*/
private fun loadRouterMap() {
registerByPlugin = false
// 自动生成的注册代码由 Gradle 插件 arouter-auto-register 生成
// 看起来像下面这样:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}

/**
* 根据类名注册
* 为了解决主 dex 文件过大的问题,牺牲了一些效率
*/
private fun register(className: String) {
if (!TextUtils.isEmpty(className)) {
try {
val clazz = Class.forName(className)
val obj = clazz.getConstructor().newInstance()
if (obj is IRouteRoot) {
registerRouteRoot(obj as IRouteRoot)
} else if (obj is IProviderGroup) {
registerProvider(obj as IProviderGroup)
} else if (obj is IInterceptorGroup) {
registerInterceptor(obj as IInterceptorGroup)
} else {
logger.info(
TAG, "注册失败,类名:" + className
+ " 应该实现其中一个接口 IRouteRoot/IProviderGroup/IInterceptorGroup。"
)
}
} catch (e: Exception) {
logger.error(TAG, "注册类错误:$className", e)
}
}
}

/**
* 用于 arouter-auto-register 插件注册路由的方法
* @param routeRoot IRouteRoot 实现类,位于包 com.alibaba.android.arouter.core.routers
*/
private fun registerRouteRoot(routeRoot: IRouteRoot?) {
markRegisteredByPlugin()
if (routeRoot != null) {
routeRoot.loadInto(Warehouse.groupsIndex)
}
}

/**
* 用于 arouter-auto-register 插件注册拦截器的方法
* @param interceptorGroup IInterceptorGroup 实现类,位于包 com.alibaba.android.arouter.core.routers
*/
private fun registerInterceptor(interceptorGroup: IInterceptorGroup?) {
markRegisteredByPlugin()
if (interceptorGroup != null) {
interceptorGroup.loadInto(Warehouse.interceptorsIndex)
}
}

/**
* 用于 arouter-auto-register 插件注册提供者的方法
* @param providerGroup IProviderGroup 实现类,位于包 com.alibaba.android.arouter.core.routers
*/
private fun registerProvider(providerGroup: IProviderGroup?) {
markRegisteredByPlugin()
if (providerGroup != null) {
providerGroup.loadInto(Warehouse.providersIndex)
}
}

/**
* 标记已由 arouter-auto-register 插件注册
*/
private fun markRegisteredByPlugin() {
if (!registerByPlugin) {
registerByPlugin = true
}
}

/**
* LogisticsCenter 初始化,加载所有路由信息到内存中,需要在首次使用时进行初始化。
*/
@Synchronized
@Throws(HandlerException::class)
fun init(context: Context, tpe: ThreadPoolExecutor?) {
mContext = context
executor = tpe
try {
var startInit = System.currentTimeMillis()
// 首先通过插件加载路由信息
loadRouterMap()
if (registerByPlugin) {
logger.info(TAG, "通过 arouter-auto-register 插件加载路由映射信息。")
} else {
val routerMap: Set<String>

// 在调试模式或安装新版本时,每次都会重建路由映射
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "在调试模式下或安装新版本,重新构建路由映射。")
// 这些类是由 arouter-compiler 自动生成的
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE)
.edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply()
}
PackageUtils.updateVersion(context) // 保存新版本名,当路由映射更新完成时使用
} else {
logger.info(TAG, "从缓存中加载路由映射信息。")
routerMap = HashSet(
context.getSharedPreferences(
AROUTER_SP_CACHE_KEY,
Context.MODE_PRIVATE
).getStringSet(AROUTER_SP_KEY_MAP, HashSet())
)
}
logger.info(
TAG,
"找到路由映射信息,映射大小 = " + routerMap.size + ",耗时 " + (System.currentTimeMillis() - startInit) + " 毫秒。"
)
startInit = System.currentTimeMillis()
for (className in routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 这是根元素之一,加载根元素
(Class.forName(className).getConstructor()
.newInstance() as IRouteRoot).loadInto(Warehouse.groupsIndex)
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 加载拦截器映射信息
(Class.forName(className).getConstructor()
.newInstance() as IInterceptorGroup).loadInto(Warehouse.interceptorsIndex)
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 加载提供者映射信息
(Class.forName(className).getConstructor()
.newInstance() as IProviderGroup).loadInto(Warehouse.providersIndex)
}
}
}
logger.info(
TAG,
"加载根元素完成,耗时 " + (System.currentTimeMillis() - startInit) + " 毫秒。"
)
if (Warehouse.groupsIndex.size() === 0) {
logger.error(TAG, "未找到映射文件,请检查您的配置!")
}
if (ARouter.debuggable()) {
logger.debug(
TAG,
java.lang.String.format(
Locale.getDefault(),
"LogisticsCenter 已经加载,GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]",
Warehouse.groupsIndex.size(),
Warehouse.interceptorsIndex.size(),
Warehouse.providersIndex.size()
)
)
}
} catch (e: Exception) {
throw HandlerException(TAG.toString() + "ARouter 初始化物流中心异常! [" + e.message + "]")
}
}

/**
* 根据服务名构建 Postcard
*
* @param serviceName 接口名
* @return Postcard
*/
fun buildProvider(serviceName: String?): Postcard? {
val meta: RouteMeta = Warehouse.providersIndex.get(serviceName)
return if (null == meta) {
null
} else {
Postcard(meta.getPath(), meta.getGroup())
}
}

/**
* 根据路由元信息完成 Postcard 对象
*
* @param postcard 不完整的 Postcard,应由此方法完成。
*/
@Synchronized
fun completion(postcard: Postcard?) {
if (null == postcard) {
throw NoRouteFoundException(TAG.toString() + "没有 Postcard!")
}
val routeMeta: RouteMeta = Warehouse.routes.get(postcard.getPath())
if (null == routeMeta) {
// 可能不存在或尚未加载
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw NoRouteFoundException(TAG.toString() + "没有匹配路径 [" + postcard.getPath() + "],在组 [" + postcard.getGroup() + "] 中。")
} else {
// 加载路由信息并缓存到内存中,然后从路由元信息中删除
try {
if (ARouter.debuggable()) {
logger.debug(
TAG,
java.lang.String.format(
Locale.getDefault(),
"开始加载组 [%s],由 [%s] 触发。",
postcard.getGroup(),
postcard.getPath()
)
)
}
addRouteGroupDynamic(postcard.getGroup(), null)
if (ARouter.debuggable()) {
logger.debug(
TAG,
java.lang.String.format(
Locale.getDefault(),
"组 [%s] 已经加载,由 [%s] 触发。",
postcard.getGroup(),
postcard.getPath()
)
)
}
} catch (e: Exception) {
throw HandlerException(TAG.toString() + "加载组元信息时发生致命异常。 [" + e.message + "]")
}
completion(postcard) // 重新加载
}
} else {
postcard.setDestination(routeMeta.getDestination())
postcard.setType(routeMeta.getType())
postcard.setPriority(routeMeta.getPriority())
postcard.setExtra(routeMeta.getExtra())
val rawUri: Uri = postcard.getUri()
if (null != rawUri) { // 尝试将参数设置到 Bundle 中
val resultMap: Map<String, String> = TextUtils.splitQueryParameters(rawUri)
val paramsType: Map<String, Int?> = routeMeta.getParamsType()
if (MapUtils.isNotEmpty(paramsType)) {
// 根据参数类型设置值,仅对使用 @Param 注解的参数有效
for ((key, value): Map.Entry<String, Int?> in paramsType) {
setValue(
postcard,
value,
key,
resultMap[key]
)
}

// 保存需要自动注入的参数名
postcard.getExtras().putStringArray(
ARouter.AUTO_INJECT,
paramsType.keys.toArray(arrayOf<String>())
)
}

// 保存原始 URI
postcard.withString(ARouter.RAW_URI, rawUri.toString())
}
when (routeMeta.getType()) {
PROVIDER -> {
// 这是提供者,所以必须实现 IProvider 接口
val providerMeta: Class<out IProvider?> = routeMeta.getDestination()
var instance: IProvider? = Warehouse.providers.get(providerMeta)
if (null == instance) { // 没有此提供者的实例
val provider: IProvider
try {
provider = providerMeta.getConstructor().newInstance()
provider.init(mContext)
Warehouse.providers.put(providerMeta, provider)
instance = provider
} catch (e: Exception) {
logger.error(TAG, "初始化提供者失败!", e)
throw HandlerException("初始化提供者失败!")
}
}
postcard.setProvider(instance)
postcard.greenChannel() // 提供者应跳过所有拦截器
}
FRAGMENT -> postcard.greenChannel() // Fragment 不需要拦截器
else -> {}
}
}
}

/**
* 根据已知类型设置值
*
* @param postcard postcard
* @param typeDef 类型
* @param key 键
* @param value 值
*/
private fun setValue(postcard: Postcard, typeDef: Int?, key: String, value: String?) {
if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
return
}
try {
if (null != typeDef) {
if (typeDef === TypeKind.BOOLEAN.ordinal()) {
postcard.withBoolean(key, Boolean.parseBoolean(value))
} else if (typeDef === TypeKind.BYTE.ordinal()) {
postcard.withByte(key, value!!.toByte())
} else if (typeDef === TypeKind.SHORT.ordinal()) {
postcard.withShort(key, value!!.toShort())
} else if (typeDef === TypeKind.INT.ordinal()) {
postcard.withInt(key, value!!.toInt())
} else if (typeDef === TypeKind.LONG.ordinal()) {
postcard.withLong(key, value!!.toLong())
} else if (typeDef === TypeKind.FLOAT.ordinal()) {
postcard.withFloat(key, value!!.toFloat())
} else if (typeDef === TypeKind.DOUBLE.ordinal()) {
postcard.withDouble(key, value!!.toDouble())
} else if (typeDef === TypeKind.STRING.ordinal()) {
postcard.withString(key, value)
} else if (typeDef === TypeKind.PARCELABLE.ordinal()) {
// TODO : 如何使用字符串描述可传递的值?
} else if (typeDef === TypeKind.OBJECT.ordinal()) {
postcard.withString(key, value)
} else { // 兼容编译器 SDK 1.0.3,在该版本中,字符串类型 = 18
postcard.withString(key, value)
}
} else {
postcard.withString(key, value)
}
} catch (ex: Throwable) {
logger.warning(Consts.TAG, "LogisticsCenter setValue 失败!" + ex.message)
}
}

/**
* 挂起业务,清除缓存。
*/
fun suspend() {
Warehouse.clear()
}

@Synchronized
@Throws(
NoSuchMethodException::class,
IllegalAccessException::class,
InvocationTargetException::class,
InstantiationException::class
)
fun addRouteGroupDynamic(groupName: String?, group: IRouteGroup?) {
if (Warehouse.groupsIndex.containsKey(groupName)) {
// 如果已包含此组,但尚未加载
// 先加载此组,因为动态路由具有较高的优先级。
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance()
.loadInto(Warehouse.routes)
Warehouse.groupsIndex.remove(groupName)
}

// 覆盖旧组。
if (null != group) {
group.loadInto(Warehouse.routes)
}
}
}

这个类是ARouter库的核心组件之一,它的主要作用是负责管理和维护ARouter路由框架中的各种映射关系和配置信息,以便实现路由功能。以下是这个类的主要作用描述:

  1. 加载路由映射信息: LogisticsCenter负责加载和管理所有的路由映射信息。这些路由映射信息包括路由路径、路由分组、拦截器、提供者等相关信息,它们在ARouter框架中用于实现路由跳转和服务提供。

  2. 支持自动注册: 通过ARouter的插件机制,LogisticsCenter可以支持自动注册路由信息,使得开发者无需手动配置路由信息,提高了开发效率。插件会自动生成代码来调用LogisticsCenter的方法注册路由信息。

  3. 初始化和管理线程池: LogisticsCenter负责初始化和管理线程池,用于异步加载路由信息和执行路由任务。线程池的管理有助于提高ARouter框架的性能和并发处理能力。

  4. 动态加载路由信息: 当需要跳转到某个路由时,LogisticsCenter会根据路由信息动态加载相关的路由元信息,包括路由组信息和拦截器信息,以便进行路由跳转和拦截器处理。

  5. 提供路由跳转和服务提供支持: LogisticsCenter提供了一系列方法,用于构建路由跳转的Postcard对象和获取服务提供者的实例。它还负责处理路由跳转的逻辑,包括路由路径匹配、拦截器处理和服务提供。

  6. 支持路由信息的缓存和更新: LogisticsCenter支持路由信息的缓存和更新,以提高ARouter框架的性能。当路由信息有更新时,可以通过插件机制重新生成路由映射信息。

总之,LogisticsCenter是ARouter框架中的核心组件,它通过加载、管理和动态获取路由信息,实现了路由跳转和服务提供的功能,提供了便捷的路由框架和服务化解决方案。

Warehouse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.alibaba.android.arouter.core

import com.alibaba.android.arouter.base.UniqueKeyTreeMap
import com.alibaba.android.arouter.facade.model.RouteMeta
import com.alibaba.android.arouter.facade.template.IInterceptor
import com.alibaba.android.arouter.facade.template.IProvider
import com.alibaba.android.arouter.facade.template.IRouteGroup

/**
* 路由元信息和其他数据的存储仓库。
*
* Warehouse(仓库)负责存储路由元信息以及其他相关数据。
*
* @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2017/2/23 下午1:39
*/
internal object Warehouse {
// 缓存路由组和路由元信息
var groupsIndex: MutableMap<String, Class<out IRouteGroup?>> =
HashMap<String, Class<out IRouteGroup?>>()
var routes: MutableMap<String, RouteMeta> = HashMap<String, RouteMeta>()

// 缓存服务提供者
var providers: MutableMap<Class<*>, IProvider> = HashMap<Class<*>, IProvider>()
var providersIndex: MutableMap<String, RouteMeta> = HashMap<String, RouteMeta>()

// 缓存拦截器
var interceptorsIndex: MutableMap<Int, Class<out IInterceptor?>> =
UniqueKeyTreeMap("多个拦截器使用相同的优先级 [%s]")
var interceptors: MutableList<IInterceptor> = ArrayList<IInterceptor>()

/**
* 清空仓库中的数据。
*/
fun clear() {
routes.clear()
groupsIndex.clear()
providers.clear()
providersIndex.clear()
interceptors.clear()
interceptorsIndex.clear()
}
}

这个类是ARouter框架内部的一个存储仓库,用于缓存路由元信息、路由组信息、服务提供者信息以及拦截器信息等相关数据。这些数据在ARouter框架中用于实现路由跳转和服务提供功能。

从零开始写一个 ARouter - exception 异常与 callback 回调

  • ARouter
    • router-annotation 路由注解模块
      • src.main.java

        • com.alibaba.android.arouter
          • facade 提供注解和枚举的包
            • annotation 存放各种注解类的包
              • Autowired.java 自动注入的注解
              • Interceptor.java 拦截器的注解
              • Param.java(废弃) 参数注解: 被 Autowired 淘汰
              • Route.java 路由信息注解
            • enums 包含各种枚举类型的包
              • RouteType.java 路由类型的枚举
              • TypeKind.java 类型种类的枚举
            • model 包含模型类的包
              • RouteMeta.java 路由元信息的模型类
              • TypeWrapper.java 存储目标对象的泛型类型信息的类
      • arouter-api ARouter框架的API模块

        • src.main.java
          • com.alibaba.android.arouter
            • base 基础功能相关的包
              • UniqueKeyTreeMap.java 唯一键树形映射的类
            • core 核心功能相关的包
              • AutowiredLifecyleCallback.java(废弃) 自动注入生命周期回调的类
              • AutowiredServiceImpl.java 自动注入服务的实现类
              • InstrumentationHook.java(废弃) 仪表钩子的类
              • InterceptorServiceImpl.java 拦截器服务的实现类
              • LogisticsCenter.java 物流中心的类
              • Warehouse.java 仓库的类
            • exception 异常相关的包
            • facade 提供各种服务和回调的包
            • service 服务相关的包
              • AutowiredService.java 自动注入服务的接口
              • ClassLoaderService.java 类加载器服务的接口
              • DegradeService.java 降级服务的接口
              • InterceptorService.java 拦截器服务的接口
              • PathReplaceService.java 路径替换服务的接口
              • PretreatmentService.java 预处理服务的接口
              • SerializationService.java 序列化服务的接口
            • template 模板相关的包
              • IInterceptor.java 拦截器接口
              • IInterceptorGroup.java 拦截器分组接口
              • Ilogger.java 日志记录器接口
              • IPolicy.java 策略接口
              • IProvider.java 提供者接口
              • IProviderGroup.java 提供者分组接口
              • IRouteGroup.java 路由分组接口
              • IRouteRoot.java 路由根接口
              • Isyringe.java 注射器接口
              • Postcard.java 路由信息封装类
            • launcher 启动器: 包含一些用于启动ARouter框架的类和线程管理相关的类。
              • _Arouter.java ARouter框架的内部启动类,用于初始化ARouter。
              • Arouter.java ARouter框架的启动类,用于初始化ARouter。
            • thread (线程)包含了与线程管理相关的类。
              • CancelableCountDownLatch.java 可取消的倒计时计数器,用于线程同步。
              • DefaultPoolExecutor.java 默认的线程池执行器,用于执行异步任务。
              • DefaultThreadFactory.java 默认线程工厂,用于创建线程。
            • utils 工具类。
              • ClassUtils.java 用于操作类的实用工具类。
              • Consts.java 包含一些常量值的类。
              • DefaultLogger.java 默认的日志记录器类。
              • MapUtils.java 用于操作地图数据的实用工具类。
              • PackageUtils.java 用于操作包信息的实用工具类。
              • TextUtils.java 用于操作文本数据的实用工具类。
      • arouter-compiler

        • src.main.java
          • com.alibaba.android.arouter
            • compiler 编译相关的包
              • entity 实体类相关的包
                • RouteDoc.java 路由文档实体类
              • processor 处理器相关的包
                • AutowiredProcessor.java 自动注入处理器
                • BaseProcessor.java 基础处理器
                • InterceptorProcessor.java 拦截器处理器
                • RouteProcessor.java 路由处理器
              • utils 工具类相关的包
                • Consts.java 常量类
                • Logger.java 日志记录器类
                • TypeUtils.java 类型工具类