我们经常碰到问题比如状态栏是有的,但是Activity的界面是黑屏。而logcat中也有如下log:
02-27 16:07:47.816929 2667 2733 I ActivityManager: Displayed com.android.settings/.SubSettings: +30s71ms
这样的问题我们如何分析,这里我们从代码角度分析下。当然我们追查log的时候是查log的源码,再通过哪里调用一步步反推的,这里我们直接从一开始的顺序分析。这里逻辑比较清楚。
我们以前在分析AMS启动Activity的时候,先会调用ActivityStackSupervisor的startSpecificActivityLocked方法,这个方法里面会先启动进程还是去ActivityThrread调用handleLaunchActivity等。在这里我们要注意一个地方是设置Activity的Launch time。
-
void startSpecificActivityLocked(ActivityRecord r,
-
boolean andResume, boolean checkConfig) {
-
// Is this activity's application already running?
-
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
-
r.info.applicationInfo.uid,
true);
-
-
r.task.
stack.setLaunchTime(r);
//设置Launch的time
-
-
if (app != null && app.thread != null) {
-
try {
-
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) ==
0
-
|| !
"android".equals(r.info.packageName)) {
-
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
-
mService.mProcessStats);
-
}
-
realStartActivityLocked(r, app, andResume, checkConfig);
//会调用ActivityThread的handleLaunchActivity
-
return;
-
}
catch (RemoteException e) {
-
Slog.w(TAG,
"Exception when starting activity "
-
+ r.intent.getComponent().flattenToShortString(), e);
-
}
-
-
}
-
-
mService.startProcessLocked(r.processName, r.info.applicationInfo,
true,
0,
-
"activity", r.intent.getComponent(),
false,
false,
true);
//启动进程
-
}
在ActivityThread的handleLaunchActivity函数中,会先调用performLaunchActivity函数,这里函数里面会创建Activity,然后调用其onCreate函数。然后会调用handleResumeActivity函数,在这个函数中先调用了performResumeActivity函数(会调用Activity的onResume函数),然后handleResumeActivity还会调用WindowManager的addView函数(这个函数会到ViewRootImpl的setView函数,然后再到WMS的addWindow增加窗口,然后ViewRootImpl里面再布局、绘制等)
我们再来看看设置Launchtime是保存在mLaunchStartTime中。这个时间就是应用启动的时间
-
void setLaunchTime(ActivityRecord r) {
-
if (r.displayStartTime ==
0) {
-
r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis();
-
if (mLaunchStartTime ==
0) {
-
startLaunchTraces(r.packageName);
-
mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime;
-
}
-
}
else
if (mLaunchStartTime ==
0) {
-
startLaunchTraces(r.packageName);
-
mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis();
-
}
-
}
接着我们再来看AppWindowToken的updateReportedVisibilityLocked函数,这个函数会在WMS的多个地方调用,但凡有窗口变化必然会调用这个函数,我们来看下它先会遍历该AppWindowToken下所有的窗口,然后计算器绘制的窗口数量,当所有的窗口已经绘制过了,就会将nowDrawn的状态置为true,如果这个这个这个状态发生了改变就会发送REPORT_APPLICATION_TOKEN_DRAWN消息。
-
void updateReportedVisibilityLocked() {
-
.....
-
final
int N = allAppWindows.size();
-
for (
int i=
0; i<N; i++) {
//遍历这个AppWindowToken下所有的窗口
-
WindowState win = allAppWindows.get(i);
-
if (win == startingWindow || win.mAppFreezing
-
|| win.mViewVisibility != View.VISIBLE
-
|| win.mAttrs.type == TYPE_APPLICATION_STARTING
-
|| win.mDestroying) {
-
continue;
-
}
-
-
numInteresting++;
-
if (win.isDrawnLw()) {
-
numDrawn++;
//绘制的窗口数量
-
if (!win.mWinAnimator.isAnimating()) {
-
numVisible++;
-
}
-
nowGone =
false;
-
}
else
if (win.mWinAnimator.isAnimating()) {
-
nowGone =
false;
-
}
-
}
-
-
boolean nowDrawn = numInteresting >
0 && numDrawn >= numInteresting;
//所有窗口绘制了
-
boolean nowVisible = numInteresting >
0 && numVisible >= numInteresting;
-
if (!nowGone) {
-
// If the app is not yet gone, then it can only become visible/drawn.
-
if (!nowDrawn) {
-
nowDrawn = reportedDrawn;
-
}
-
if (!nowVisible) {
-
nowVisible = reportedVisible;
-
}
-
}
-
-
if (nowDrawn != reportedDrawn) {
//状态改变就会发送消息
-
if (nowDrawn) {
-
Message m = service.mH.obtainMessage(
-
H.REPORT_APPLICATION_TOKEN_DRAWN,
this);
-
service.mH.sendMessage(m);
-
}
-
reportedDrawn = nowDrawn;
-
}
我们看看判断窗口是否已经绘制的函数,READY_TO_SHOW和HAS_DRAWN状态都可以。
-
public boolean isDrawnLw() {
-
return mHasSurface && !mDestroying &&
-
(mWinAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW
-
|| mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN);
-
}
我们再来看看WMS中对这个REPORT_APPLICATION_TOKEN_DRAWN消息的处理主要就是调用了AppWindowToken中的AppToken的windowsDrawn方法
-
case REPORT_APPLICATION_TOKEN_DRAWN: {
-
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-
-
try {
-
if (DEBUG_VISIBILITY) Slog.v(
-
TAG,
"Reporting drawn in " + wtoken);
-
wtoken.appToken.windowsDrawn();
-
}
catch (RemoteException ex) {
-
}
-
}
break;
这个函数在ActivityRecord的Token类中实现,其又是调用了ActivityRecord 的windowsDrawnLocked函数
-
@
Override
-
public
void
windowsDrawn
() {
-
synchronized (mService) {
-
ActivityRecord r = tokenToActivityRecordLocked(
this);
-
if (r != null) {
-
r.windowsDrawnLocked();
-
}
-
}
-
}
ActivityRecord 的windowsDrawnLocked函数主要是调用了reportLaunchTimeLocked函数,注意这个时候我们把当前时间传入了reportLaunchTimeLocked函数
-
void windowsDrawnLocked() {
-
if (displayStartTime !=
0) {
-
reportLaunchTimeLocked(SystemClock.uptimeMillis());
-
-
//add by LC, startActivity over, disable boost
-
if(mStackSupervisor.mIsBoostEnable ==
true)
-
{
-
mStackSupervisor.mPerf.setBoostEnable_native(
0);
-
mStackSupervisor.mIsBoostEnable =
false;
-
}
-
}
-
mStackSupervisor.sendWaitingVisibleReportLocked(
this);
-
startTime =
0;
-
finishLaunchTickingLocked();
-
if (task != null) {
-
task.hasBeenVisible =
true;
-
}
-
}
reportLaunchTimeLocked函数会把当前的时间减去 Launchtime的时间(Activity启动到显示的时间差),然后打印displayed的延迟时间的log。这个时间差就是Activity的Launchtime时间到这个AppWindowToken下的所有窗口都到一个准备显示状态的时间差。所有窗口到准备显示状态还需要VSync信号过来再进行显示的。
-
private void reportLaunchTimeLocked(final long curTime) {
-
final ActivityStack
stack = task.
stack;
-
if (
stack == null) {
-
return;
-
}
-
final
long thisTime = curTime - displayStartTime;
-
final
long totalTime =
stack.mLaunchStartTime !=
0
//当前时间减去Launchtime
-
? (curTime -
stack.mLaunchStartTime) : thisTime;
-
if (SHOW_ACTIVITY_START_TIME) {
-
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"launching: " + packageName,
0);
-
EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
-
userId, System.identityHashCode(
this), shortComponentName,
-
thisTime, totalTime);
-
StringBuilder sb = service.mStringBuilder;
-
sb.setLength(
0);
-
sb.append(
"Displayed ");
-
sb.append(shortComponentName);
-
sb.append(
": ");
-
TimeUtils.formatDuration(thisTime, sb);
-
if (thisTime != totalTime) {
-
sb.append(
" (total ");
-
TimeUtils.formatDuration(totalTime, sb);
-
sb.append(
")");
-
}
-
Log.i(TAG, sb.toString());
-
}
-
mStackSupervisor.reportActivityLaunchedLocked(
false,
this, thisTime, totalTime);
-
if (totalTime >
0) {
-
//service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
-
}
-
displayStartTime =
0;
-
stack.mLaunchStartTime =
0;
-
}
这样我们再来分析下如下的log,这个Activity的displayed延迟了30多秒。
02-27 16:07:47.816929 2667 2733 I ActivityManager: Displayed com.android.settings/.SubSettings: +30s71ms
基于这样的问题,我们回顾上面的代码,在Activity的handleLaunchActivity中先后会调用Activity的onCreate和onResume函数,然后才是到WMS的addWindow创建窗口(窗口创建了之后才会把所有和这个AppWindowToken的窗口置为一个准备显示的状态,这个时候就会去打印这个log,也会计算这个延时)并且最后的显示界面还是靠VSync信号驱动的。所以这中间应用的onCreate和onResume耗时的可能性比较大才会最终导致打印这个log,现象也是状态栏能显示而Activity的界面是黑屏。