前语
上文Android功能优化系列-腾讯matrix-TracePlugin发动速度优化之StartupTracer源码剖析提到,ActivityThreadHacker.addListener(this),通过将当时plugin加入到监听中,能够拿到application创立完结的回调-onApplicationCreateEnd()。那么它是怎样做到的,今天咱们能简略剖析下ActivityThreadHacker的实现原理。
IApplicationCreateListener
首要咱们打开ActivityThreadHacker源码,能够看到在类中有一个调集,用来存储IApplicationCreateListener目标。
private static final HashSet<IApplicationCreateListener> listeners = new HashSet<>();
还有两个办法负责添加和移除IApplicationCreateListener目标。
public static void addListener(IApplicationCreateListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public static void removeListener(IApplicationCreateListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
而IApplicationCreateListener的回调办法正是onApplicationCreateEnd。
hackSysHandlerCallback
找到hackSysHandlerCallback办法,搜索它被调用的地方
public static void hackSysHandlerCallback()
会找到AppMethodBeat类中的realExecute办法,而realExecute又被AppMethodBeat本身的i()办法调用,AppMethodBeat的i()办法是在编译期间被插桩到每一个符合插桩条件的办法的进口位置的,详见Android功能优化系列-腾讯matrix-卡顿监控-gradle插件- 字节码插桩代码剖析。
status表明AppMethodBeat本身运转的状况,它一共有6种状况,而它状况的切换是跟随TracePlugin进行的,TracePlugin start的时分,AppMethodBeat进入started状况,TracePlugin stop的时分,AppMethodBeat进入stop状况。默认状况下是STATUS_DEFAULT,也便是等于Integer.MAX_VALUE。
public static void i(int methodId) {
//status表明AppMethodBeat本身运转的状况,他一共有6种状况,详见下方
if (status <= STATUS_STOPPED) {
return;
}
if (methodId >= METHOD_ID_MAX) {
return;
}
//所以这里当第一个被插桩的办法调用时,因为status是满意STATUS_DEFAULT状况的,所以下面的办法被履行,
//履行之后status状况被赋值为STATUS_READY,后边便不会再履行,所以保证了realExecute只会履行一次
if (status == STATUS_DEFAULT) {
synchronized (statusLock) {
if (status == STATUS_DEFAULT) {
realExecute();
status = STATUS_READY;
}
}
}
...
}
status表明AppMethodBeat本身运转的状况,他一共有6种状况:
private static final int STATUS_DEFAULT = Integer.MAX_VALUE;
private static final int STATUS_STARTED = 2;
private static final int STATUS_READY = 1;
private static final int STATUS_STOPPED = -1;
private static final int STATUS_EXPIRED_START = -2;
private static final int STATUS_OUT_RELEASE = -3;
realExecute
看下realExecute, 咱们只看和ActivityThreadHacker相关的,其他的内容会在AppMethodBeat类剖析时再深入来看。这里调用了hackSysHandlerCallback办法,从上边咱们知道realExecute只会履行一次,天然hackSysHandlerCallback也只会履行一次。
private static void realExecute() {
...
ActivityThreadHacker.hackSysHandlerCallback();
...
}
hackSysHandlerCallback
办法主要是在进行hook, 通过反射拿到当时ActivityThread目标中的mH,mH是Handler目标,然后又从Handler中反射拿到mCallback,对mCallback进行署理,并原有的Callback修改为署理后的Callback-HackCallback。
public static void hackSysHandlerCallback() {
try {
//将第一个办法履行的时刻作为sApplicationCreateBeginTime创立的时刻来记载
sApplicationCreateBeginTime = SystemClock.uptimeMillis();
//从AppMethodBeat拿一个标记出来,用于后边从AppMethodBeat取数据,具体内容后期的文章会专门剖析AppMethodBeat
sApplicationCreateBeginMethodIndex = AppMethodBeat.getInstance().maskIndex("ApplicationCreateBeginMethodIndex");
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
Object activityThreadValue = field.get(forName);
Field mH = forName.getDeclaredField("mH");
mH.setAccessible(true);
Object handler = mH.get(activityThreadValue);
Class<?> handlerClass = handler.getClass().getSuperclass();
if (null != handlerClass) {
Field callbackField = handlerClass.getDeclaredField("mCallback");
callbackField.setAccessible(true);
Handler.Callback originalCallback = (Handler.Callback) callbackField.get(handler);
HackCallback callback = new HackCallback(originalCallback);
callbackField.set(handler, callback);
}
MatrixLog.i(TAG, "hook system handler completed. start:%s SDK_INT:%s", sApplicationCreateBeginTime, Build.VERSION.SDK_INT);
} catch (Exception e) {
MatrixLog.e(TAG, "hook system handler err! %s", e.getCause().toString());
}
}
咱们知道Handler中在每个音讯被处理的时分,有这么一个操作。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
HackCallback
当matrix将署理callback注入到Handler后,那么所有的handleMessage都会被HackCallback拦截到,所以咱们进入HackCallback一探终究。HackCallback承继自Handler.Callback,咱们直接看它的handleMessage办法。
handleMessage
public boolean handleMessage(Message msg) {
//第一步
if (IssueFixConfig.getsInstance().isEnableFixSpApply()) {
if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT <= 25) {
if (msg.what == SERIVCE_ARGS || msg.what == STOP_SERVICE
|| msg.what == STOP_ACTIVITY_SHOW || msg.what == STOP_ACTIVITY_HIDE
|| msg.what == SLEEPING) {
MatrixLog.i(TAG, "Fix SP ANR is enabled");
fix();
}
}
}
if (!AppMethodBeat.isRealTrace()) {
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
//第二步
boolean isLaunchActivity = isLaunchActivity(msg);
if (hasPrint > 0) {
MatrixLog.i(TAG, "[handleMessage] msg.what:%s begin:%s isLaunchActivity:%s SDK_INT=%s", msg.what, SystemClock.uptimeMillis(), isLaunchActivity, Build.VERSION.SDK_INT);
hasPrint--;
}
if (!isCreated) {
if (isLaunchActivity || msg.what == CREATE_SERVICE
|| msg.what == RECEIVER) { // todo for provider
ActivityThreadHacker.sApplicationCreateEndTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sApplicationCreateScene = msg.what;
isCreated = true;
sIsCreatedByLaunchActivity = isLaunchActivity;
MatrixLog.i(TAG, "application create end, sApplicationCreateScene:%d, isLaunchActivity:%s", msg.what, isLaunchActivity);
synchronized (listeners) {
for (IApplicationCreateListener listener : listeners) {
listener.onApplicationCreateEnd();
}
}
}
}
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
第一步fix
handleMessage中咱们分两步来看,第一步,针对sharePreference的一个处理,当体系版本在5.0-8.0之间时(包含5.0不包含8.0),当音讯的what为指定的类型时,会去履行一个fix的操作,而fix办法体如下,看起来是拿到QueuedWork类中的sPendingWorkFinishers,然后将其存储的内容清空。这是什么逻辑?
private void fix() {
Class cls = Class.forName("android.app.QueuedWork");
Field field = cls.getDeclaredField("sPendingWorkFinishers");
if (field != null) {
field.setAccessible(true);
ConcurrentLinkedQueue<Runnable> runnables = (ConcurrentLinkedQueue<Runnable>) field.get(null);
runnables.clear();
}
}
其实这个涉及到一个SharePreference潜在的危险点。SharePreference本身是一个轻量型的存储东西,但是在很多使用中被滥用,导致耗时的产生。
Google 体系为了保证数据的跨进程完整性,前期使用能够使用 sp 来做跨进程通讯,在组件销毁或其他生命周期的一起为了保证当时这个写入使命有必要在当时这个组件的生命周期完结写入,此时主线程会在组件销毁或许组件暂停的生命周期内等候 sp 彻底写入到对应的文件中,而这个等候写入的操作便是在QueuedWork.waitToFinish()中,如activity暂停的时分源码,onPause履行后,就会进入QueuedWork.waitToFinish(),等候sp写入完结。
@Override
public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
performPauseActivity(r, finished, reason, pendingActions);
// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
}
所有会履行相关操作的音讯类型如下,也便是上边的if判断条件,也便是说这种音讯履行时,都或许导致长时刻的等候sp写入操作:
publicstaticfinalintSERVICE_ARGS=115;
publicstaticfinalintSTOP_SERVICE=116;
publicstaticfinalintPAUSE_ACTIVITY=101;
publicstaticfinalintSTOP_ACTIVITY_SHOW=103;
publicstaticfinalintSLEEPING=137;
简略看下QueuedWork.waitToFinish(),它内部是从调集中拿到所有Runnable,遍历履行run办法,那么这些Runnable是什么时分放进去的?
咱们知道SharedPreferences供给了两种写入文件的办法,commit同步写入,apply异步写入,其实就产生在apply调用后,咱们看下apply:
public void apply() {
final long startTime = System.currentTimeMillis();
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
//履行的时分等候完结
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
if (DEBUG && mcr.wasWritten) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " applied after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
//加入调集
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
}
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
...
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
apply异步的履行,是将runnable放入了QueuedWork中的行列去履行,当未履行完时,加入这时Activity进入了onPause状况,那么便是一直等候这些使命履行完结,然后或许呈现卡住的状况。
所以这个时分咱们就清楚了这个fix是在做什么了,它自动将行列中的使命清空,避免卡在这里。
第二步回调onApplicationCreateEnd
第二步的逻辑就很直接了,当第一次履行,并且满意以下条件之一时,会回调onApplicationCreateEnd办法,作为application创立完结的机遇,记载时刻戳。
- 当时是在发动activity
- 当时是在创立service
- 当时是在发动receiver
- 当然provider也是能够的,只不过如注释所说,并没有添加
由isCreated这个变量的操控可知,此逻辑只会履行一次,所以onApplicationCreateEnd天然也只会回调一次。
if (!isCreated) {
if (isLaunchActivity || msg.what == CREATE_SERVICE
|| msg.what == RECEIVER) { // todo for provider
ActivityThreadHacker.sApplicationCreateEndTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sApplicationCreateScene = msg.what;
isCreated = true;
sIsCreatedByLaunchActivity = isLaunchActivity;
MatrixLog.i(TAG, "application create end, sApplicationCreateScene:%d, isLaunchActivity:%s", msg.what, isLaunchActivity);
synchronized (listeners) {
for (IApplicationCreateListener listener : listeners) {
listener.onApplicationCreateEnd();
}
}
}
}
总结
回到咱们最初的问题ActivityThreadHacker.addListener(this),通过将当时plugin加入到监听中,能够拿到application创立完结的回调-onApplicationCreateEnd(),它是怎样做到的?
很明显,ActivityThreadHacker凭借了AppMethodBeat类i办法的初次调用,编译期AppMethodBeat i办法被插桩到每一个办法的进口处,然后当AppMethodBeat 的i办法初次调用的时分,
将HackCallback注入到ActivityThread的Handler中的mCallback上,然后实现拦截所有Message,通过判断收到的Message类型,当message满意以下条件之一时,将此机遇作为application创立完结的机遇,回调onApplicationCreateEnd办法。
- 当时是在发动activity
- 当时是在创立service
- 当时是在发动receiver
- 当然provider也是能够的,只不过如注释所说,并没有添加
一起它也趁便处理了SharedPreferences导致的卡顿问题。