Application 启动时长计算
时长计算 StartTime定位
查看源码,Application实例化时调用的是newApplication()方法(具体原理参考另外一篇博客,Application启动流程分析 https://blog.csdn.net/binghelonglong123/article/details/88414051)
form Instrumentation.java
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
Application的attach 最终 调用 attachBaseContext()
from Application.java
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
因此冷启动Application的计时开始时间 可以在attachBaseContext方法中进行startTime的记录
时长计算 EndTime定位
Application启动结束的时间,可以理解为应用完全启动并展示主Activity第一帧的时间
详见下面的“Activity启动到第一帧展示时长计算”
Activity启动时长计算(onCreat耗时)
以API28之前的源码为例
参考这两篇文章(这是我见过分析的最好的 当然API28有变化,还没有细致研究)
https://blog.csdn.net/android_jianbo/article/details/86487752
https://blog.csdn.net/android_jianbo/article/details/86488828
Activity启动入口
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
.........................................
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
...............................................................
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
.............................................................
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
从上述代码中可以看出
Activity被实例化后的调用顺序是
1:实例化Activity对象
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
2:调用attach() 这里其实最终调用的是Activity的attachBaseContext()
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
3:调用onCreat
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
4:完成create之后紧接着调用了onStart()
activity.performStart();
从上述代码中我们可以得出结论 要想完成一次Activity的启动耗时记录
StartTime:Activity的attachBaseContext()
EndTime:Activity的onStart()开始调用的时候可视为Activity的onCreat()完成启动
Activity启动到开始绘制的时长计算
这个时长计算的关键地方就是如何获取开始绘制的时机
从网上查询到可以如下代码来监听绘制开始的时机
activity.getWindow().getDecorView().post(new FirstFrameRunnable(activity, startType, startTime));
具体原理,我们参照源码分析一下
DecorView 其实就继承自FramLayout
最终的post方法其实是View里面的post方法 来看下源码
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
第一个判断比较简单 有AttachInfo的时候就用AttachInfo的Handler执行post 如果没有我们再看RunQueue
/**
* The run queue is used to enqueue pending work from Views when no Handler is
* attached. The work is executed during the next call to performTraversals on
* the thread.
* @hide
*/
static final class RunQueue {
private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
void post(Runnable action) {
postDelayed(action, 0);
}
void postDelayed(Runnable action, long delayMillis) {
HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
handlerAction.delay = delayMillis;
synchronized (mActions) {
mActions.add(handlerAction);
}
}
void removeCallbacks(Runnable action) {
final HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
while (actions.remove(handlerAction)) {
// Keep going
}
}
}
void executeActions(Handler handler) {
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
final int count = actions.size();
for (int i = 0; i < count; i++) {
final HandlerAction handlerAction = actions.get(i);
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
actions.clear();
}
}
代码比较简单 主要执行的地方在executeActions(handler)
从方法的注释里面也可以看到是在performTraversals()方法 里面调用的
performTraversals()方法就不多说了 有兴趣的同学自己去查下资料(https://www.jianshu.com/p/ada6b5f18b5d)
这里说一下网上查到的资料
View真正的绘制流程是从ViewRootImpl的performTraversals方法开始, 里面会经过measure, layout和draw三个过程. 其中
measure用来测量View的宽高, layout用来确定View的位置, 而draw负责渲染View到屏幕上
也就是说当performTraversals被调用的时候,Activity已经开始进入绘制流程了 所以使用getDecorView().post()可以实现对这个时
间点的监听,同样这个原理可以用在Activity启动性能优化上面,使用getDecorView().post()方法来处理耗时操作,可以避免在
Activity启动时将Activity卡住