WMS常见问题一(Activity displayed延迟)

转载地址: https://blog.csdn.net/kc58236582/article/details/60134836

我们经常碰到问题比如状态栏是有的,但是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。


  
  
  1. void startSpecificActivityLocked(ActivityRecord r,
  2. boolean andResume, boolean checkConfig) {
  3. // Is this activity's application already running?
  4. ProcessRecord app = mService.getProcessRecordLocked(r.processName,
  5. r.info.applicationInfo.uid, true);
  6. r.task. stack.setLaunchTime(r); //设置Launch的time
  7. if (app != null && app.thread != null) {
  8. try {
  9. if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
  10. || ! "android".equals(r.info.packageName)) {
  11. app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
  12. mService.mProcessStats);
  13. }
  14. realStartActivityLocked(r, app, andResume, checkConfig); //会调用ActivityThread的handleLaunchActivity
  15. return;
  16. } catch (RemoteException e) {
  17. Slog.w(TAG, "Exception when starting activity "
  18. + r.intent.getComponent().flattenToShortString(), e);
  19. }
  20. }
  21. mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
  22. "activity", r.intent.getComponent(), false, false, true); //启动进程
  23. }

在ActivityThread的handleLaunchActivity函数中,会先调用performLaunchActivity函数,这里函数里面会创建Activity,然后调用其onCreate函数。然后会调用handleResumeActivity函数,在这个函数中先调用了performResumeActivity函数(会调用Activity的onResume函数),然后handleResumeActivity还会调用WindowManager的addView函数(这个函数会到ViewRootImpl的setView函数,然后再到WMS的addWindow增加窗口,然后ViewRootImpl里面再布局、绘制等)

我们再来看看设置Launchtime是保存在mLaunchStartTime中。这个时间就是应用启动的时间


  
  
  1. void setLaunchTime(ActivityRecord r) {
  2. if (r.displayStartTime == 0) {
  3. r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis();
  4. if (mLaunchStartTime == 0) {
  5. startLaunchTraces(r.packageName);
  6. mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime;
  7. }
  8. } else if (mLaunchStartTime == 0) {
  9. startLaunchTraces(r.packageName);
  10. mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis();
  11. }
  12. }


接着我们再来看AppWindowToken的updateReportedVisibilityLocked函数,这个函数会在WMS的多个地方调用,但凡有窗口变化必然会调用这个函数,我们来看下它先会遍历该AppWindowToken下所有的窗口,然后计算器绘制的窗口数量,当所有的窗口已经绘制过了,就会将nowDrawn的状态置为true,如果这个这个这个状态发生了改变就会发送REPORT_APPLICATION_TOKEN_DRAWN消息。


  
  
  1. void updateReportedVisibilityLocked() {
  2. .....
  3. final int N = allAppWindows.size();
  4. for ( int i= 0; i<N; i++) { //遍历这个AppWindowToken下所有的窗口
  5. WindowState win = allAppWindows.get(i);
  6. if (win == startingWindow || win.mAppFreezing
  7. || win.mViewVisibility != View.VISIBLE
  8. || win.mAttrs.type == TYPE_APPLICATION_STARTING
  9. || win.mDestroying) {
  10. continue;
  11. }
  12. numInteresting++;
  13. if (win.isDrawnLw()) {
  14. numDrawn++; //绘制的窗口数量
  15. if (!win.mWinAnimator.isAnimating()) {
  16. numVisible++;
  17. }
  18. nowGone = false;
  19. } else if (win.mWinAnimator.isAnimating()) {
  20. nowGone = false;
  21. }
  22. }
  23. boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting; //所有窗口绘制了
  24. boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
  25. if (!nowGone) {
  26. // If the app is not yet gone, then it can only become visible/drawn.
  27. if (!nowDrawn) {
  28. nowDrawn = reportedDrawn;
  29. }
  30. if (!nowVisible) {
  31. nowVisible = reportedVisible;
  32. }
  33. }
  34. if (nowDrawn != reportedDrawn) { //状态改变就会发送消息
  35. if (nowDrawn) {
  36. Message m = service.mH.obtainMessage(
  37. H.REPORT_APPLICATION_TOKEN_DRAWN, this);
  38. service.mH.sendMessage(m);
  39. }
  40. reportedDrawn = nowDrawn;
  41. }

我们看看判断窗口是否已经绘制的函数,READY_TO_SHOW和HAS_DRAWN状态都可以。


  
  
  1. public boolean isDrawnLw() {
  2. return mHasSurface && !mDestroying &&
  3. (mWinAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW
  4. || mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN);
  5. }

我们再来看看WMS中对这个REPORT_APPLICATION_TOKEN_DRAWN消息的处理主要就是调用了AppWindowToken中的AppToken的windowsDrawn方法


  
  
  1. case REPORT_APPLICATION_TOKEN_DRAWN: {
  2. final AppWindowToken wtoken = (AppWindowToken)msg.obj;
  3. try {
  4. if (DEBUG_VISIBILITY) Slog.v(
  5. TAG, "Reporting drawn in " + wtoken);
  6. wtoken.appToken.windowsDrawn();
  7. } catch (RemoteException ex) {
  8. }
  9. } break;

这个函数在ActivityRecord的Token类中实现,其又是调用了ActivityRecord 的windowsDrawnLocked函数


  
  
  1. @ Override
  2. public void windowsDrawn () {
  3. synchronized (mService) {
  4. ActivityRecord r = tokenToActivityRecordLocked( this);
  5. if (r != null) {
  6. r.windowsDrawnLocked();
  7. }
  8. }
  9. }

ActivityRecord 的windowsDrawnLocked函数主要是调用了reportLaunchTimeLocked函数,注意这个时候我们把当前时间传入了reportLaunchTimeLocked函数


  
  
  1. void windowsDrawnLocked() {
  2. if (displayStartTime != 0) {
  3. reportLaunchTimeLocked(SystemClock.uptimeMillis());
  4. //add by LC, startActivity over, disable boost
  5. if(mStackSupervisor.mIsBoostEnable == true)
  6. {
  7. mStackSupervisor.mPerf.setBoostEnable_native( 0);
  8. mStackSupervisor.mIsBoostEnable = false;
  9. }
  10. }
  11. mStackSupervisor.sendWaitingVisibleReportLocked( this);
  12. startTime = 0;
  13. finishLaunchTickingLocked();
  14. if (task != null) {
  15. task.hasBeenVisible = true;
  16. }
  17. }

reportLaunchTimeLocked函数会把当前的时间减去 Launchtime的时间(Activity启动到显示的时间差),然后打印displayed的延迟时间的log。这个时间差就是Activity的Launchtime时间到这个AppWindowToken下的所有窗口都到一个准备显示状态的时间差。所有窗口到准备显示状态还需要VSync信号过来再进行显示的。


  
  
  1. private void reportLaunchTimeLocked(final long curTime) {
  2. final ActivityStack stack = task. stack;
  3. if ( stack == null) {
  4. return;
  5. }
  6. final long thisTime = curTime - displayStartTime;
  7. final long totalTime = stack.mLaunchStartTime != 0 //当前时间减去Launchtime
  8. ? (curTime - stack.mLaunchStartTime) : thisTime;
  9. if (SHOW_ACTIVITY_START_TIME) {
  10. Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
  11. EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
  12. userId, System.identityHashCode( this), shortComponentName,
  13. thisTime, totalTime);
  14. StringBuilder sb = service.mStringBuilder;
  15. sb.setLength( 0);
  16. sb.append( "Displayed ");
  17. sb.append(shortComponentName);
  18. sb.append( ": ");
  19. TimeUtils.formatDuration(thisTime, sb);
  20. if (thisTime != totalTime) {
  21. sb.append( " (total ");
  22. TimeUtils.formatDuration(totalTime, sb);
  23. sb.append( ")");
  24. }
  25. Log.i(TAG, sb.toString());
  26. }
  27. mStackSupervisor.reportActivityLaunchedLocked( false, this, thisTime, totalTime);
  28. if (totalTime > 0) {
  29. //service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
  30. }
  31. displayStartTime = 0;
  32. stack.mLaunchStartTime = 0;
  33. }


这样我们再来分析下如下的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的界面是黑屏。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值