Android 亮屏慢问题处理

问题:

【指纹】灭屏下使用正确的指纹解锁,解锁成功背光亮但屏幕没亮

【偶现】滑动解锁后只显示壁纸,图标在4S后加载出来

抖音有个activity在灭屏的时候都会启动,如果出现如下情况:

06-2513:08:14.4899802799I am_create_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,NULL,NULL,NULL,276824064]

06-2513:08:14.5459802799I am_restart_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]

06-2513:08:14.5539802799I am_set_resumed_activity: [0,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,minimalResumeActivityLocked]

06-2513:08:14.5859802799I am_pause_activity: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]

06-2513:08:14.62698013170I am_set_resumed_activity: [0,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,resumeTopActivityInnerLocked]

06-2513:08:14.64298013170I am_resume_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]

06-2513:08:14.8329802973I am_finish_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,app-request]

06-2513:08:14.8369802973I am_focused_stack: [0,0,1,finishActivity adjustFocus]

06-2513:08:14.8539802973I am_pause_activity: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]

06-2513:08:14.94898011678I am_set_resumed_activity: [0,com.android.launcher3/com.android.searchlauncher.SearchLauncher,resumeTopActivityInnerLocked]

06-2513:08:14.97298011678I am_resume_activity: [0,257364849,3,com.android.launcher3/com.android.searchlauncher.SearchLauncher]

06-2513:08:15.1359802973I am_failed_to_pause: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,(none)]

06-2513:08:15.27835713571I am_on_resume_called: [0,com.android.searchlauncher.SearchLauncher,RESUME_ACTIVITY]

06-2513:08:20.3799801055I am_destroy_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,finish-imm]

06-2513:08:20.38983158315I am_on_stop_called: [0,com.ss.android.message.sswo.SswoActivity,destroy]

06-2513:08:28.8349801054I am_pss : [3571,10027,com.android.launcher3,106524672,89603072,5540864]

pause failed导致AMS这边会有一个超时等待的动作,需继续查找为啥会出现pause 超时。

导致问题流程如下:

1, 待机过程中,抖音会启动

com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,由于处于待机状态,该activity会立刻进入pause状态;后来得知SswoActivity这个类是抖音专门用来保活的一个透明类。

2,正常情况下,该activity在唤醒时会立刻finish(app request主动请求),然后在唤醒过程中Top Activity依然是Launcher,如果没有及时finish,则Top activity是该activity;

3,如果Top activity是 抖音activity,最终还是会主动请求activity finish,触发抖音activity--->Launcher activity的切换过程;

4,步骤3中的切换过程在 Keyguard lock情况下进行,这种情况下,在显示Launcher activity之前需要将将抖音的UI show过程走一次(WMS 在有keyguard locked情况下的APP 切换逻辑);

5,由于该抖音activity没有UI show过程(该activity没有UI),所以该等待会一直存在,直到5s超时,从而导致issue中的情况。

点亮解锁逻辑流程如下:

设备点亮流程

frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java

唤醒源 -> updatePowerStateLocked ->

updateDisplayPowerStateLocked -> requestPowerState

requestPowerState的实现在DisplayPowerController.java中

在这里有一个blockScreen的逻辑动作,为的就是当screen内容准备好后,才点亮屏幕。

相关接口及主要代码:

animateScreenStateChange -> setScreenState -> blockScreenOn

private void blockScreenOn() {

if (mPendingScreenOnUnblocker == null) {

        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWERSCREEN_ON_BLOCKED_TRACE_NAME, 0);

mPendingScreenOnUnblocker new ScreenOnUnblocker();

mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();

        Slog.i(TAG"Blocking screen on until initial contents have been drawn.");

    }

}


ScreenOnUnblocker的作用是在回调触发的时候,发送一个消息MSG_SCREEN_ON_UNBLOCKED出去,执行unblock的动作。


private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {

@Override

public void onScreenOn() {

Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKEDthis);

        msg.setAsynchronous(true);

mHandler.sendMessage(msg);

    }

}


回到setScreenState中,主要代码:

mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);

screenTurningOn的实现在PhoneWindowmanager中,screenTurningOn回调结束后,屏点亮,unblock之前的逻辑,打印相关消息。

private void unblockScreenOn() {

if (mPendingScreenOnUnblocker != null) {

mPendingScreenOnUnblocker null;

long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;

Slog.i(TAG"Unblocked screen on after " + delay + " ms");

//所以可以根据这里来判断屏亮耗时

        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWERSCREEN_ON_BLOCKED_TRACE_NAME, 0);

    }

}



// Called on the DisplayManager's DisplayPowerController thread.

@Override

public void screenTurningOn(final ScreenOnListener screenOnListener) {

if (DEBUG_WAKEUP) Slog.i(TAG"Screen turning on...");

//所以可以根据这里来判断屏亮开始触发点亮

。。。

//这里走有锁屏的流程

if (mKeyguardDelegate != null) {

mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);

mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,

                    getKeyguardDrawnTimeout());

mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);

else {

if (DEBUG_WAKEUP) Slog.d(TAG,

"null mKeyguardDelegate: setting mKeyguardDrawComplete.");

            finishKeyguardDrawn();

        }

    }

}


从主要代码可以看到,这里触发PhoneWindowManager的screenTurningOn接口,等待Keyguard绘制完成,并设置了一个1s的timeout机制,超时系统就不会继续等待,强制执行finishKeyguardDrawn(),当keyguard正常绘制完成,回调到mKeyguardDrawnCallback,继续执行finishKeyguardDrawn()

case MSG_KEYGUARD_DRAWN_COMPLETE:

if (DEBUG_WAKEUP) Slog.w(TAG"Setting mKeyguardDrawComplete");

    finishKeyguardDrawn();

break;

case MSG_KEYGUARD_DRAWN_TIMEOUT:

    Slog.w(TAG"Keyguard drawn timeout. Setting mKeyguardDrawComplete");

    finishKeyguardDrawn();


当keyguard绘制完成的时候,接着会等待后台所有visible的window绘制完成

private void finishKeyguardDrawn() {

 。。。

// ... eventually calls finishWindowsDrawn which will finalize our screen turn on

    // as well as enabling the orientation change logic/sensor.

    mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,

WAITING_FOR_DRAWN_TIMEOUT);

}


WAITING_FOR_DRAWN_TIMEOUT是一个1s的延时逻辑。

如果时间到了,还没有绘制完成,系统也不会一直等下去

@Override

public void waitForAllWindowsDrawn(Runnable callback, long timeout) {

boolean allWindowsDrawn = false;

synchronized (mWindowMap) {

mWaitingForDrawnCallback = callback;

        getDefaultDisplayContentLocked().waitForAllWindowsDrawn();

mWindowPlacerLocked.requestTraversal();

mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);

if (mWaitingForDrawn.isEmpty()) {

allWindowsDrawn = true;

else {

mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);

            checkDrawnWindowsLocked();

        }

    }

if (allWindowsDrawn) {

        callback.run();

    }

}




超时后,会清掉mWaitingForDrawn,在执行回调的run方法

case WAITING_FOR_DRAWN_TIMEOUT: {

Runnable callback = null;

synchronized (mWindowMap) {

        Slog.w(TAG_WM"Timeout waiting for drawn: undrawn=" mWaitingForDrawn);

mWaitingForDrawn.clear();

callback = mWaitingForDrawnCallback;

mWaitingForDrawnCallback null;

    }

if (callback != null) {

        callback.run();

    }

break;

}


在看callback的实现,发送一个消息触发window绘制完成finishWindowsDrawn使能屏幕:

final Runnable mWindowManagerDrawCallback new Runnable() {

@Override

public void run() {

if (DEBUG_WAKEUP) Slog.i(TAG"All windows ready for display!");

mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE);

    }

};



private void finishWindowsDrawn() {

synchronized (mLock) {

if (!mScreenOnEarly || mWindowManagerDrawComplete) {

return// Screen is not turned on or we did already handle this case earlier.

        }

mWindowManagerDrawComplete true;

    }

finishScreenTurningOn();

}


所以,从这里可以看出,上层影响屏亮的因素:

锁屏绘制时长

Visible的window绘制的时长

到此,背光点亮。


Activity转场显示主要逻辑(锁屏状态下)

在锁屏显示状态下,当activityA->activityB切换的时候,会存在切换动画。

AMS startActivity会调用到ActivityStackSupervisor.java中的realStartActivityLocked,这里只看主要逻辑:

if (mKeyguardController.isKeyguardLocked()) {

    r.notifyUnknownVisibilityLaunched();

}


notifyUnknownVisibilityLaunched的实现在ActivityRecord中

void notifyUnknownVisibilityLaunched() {

// No display activities never add a window, so there is no point in waiting them for

    // relayout.

    if (!noDisplay) {

mWindowContainerController.notifyUnknownVisibilityLaunched();

    }

}


接着看AppWindowContainerController中的实现:

public void notifyUnknownVisibilityLaunched() {

synchronized(mWindowMap) {

if (mContainer != null) {

mService.mUnknownAppVisibilityController.notifyLaunched(mContainer);

        }

    }

}


最终实现在:

/**

 * Notifies that {@param appWindow} has been launched behind Keyguard, and we need to wait until

 * it is resumed and relaid out to resolve the visibility.

 */

void notifyLaunched(@NonNull AppWindowToken appWindow) {

if (DEBUG_UNKNOWN_APP_VISIBILITY) {

        Slog.d(TAG"App launched appWindow=" + appWindow);

    }

    mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RESUME);

}

同样在AMS执行activity Resume生命周期的时候,会触发:

/**

 * Notifies that {@param appWindow} has finished resuming.

 */

void notifyAppResumedFinished(@NonNull AppWindowToken appWindow) {

if (mUnknownApps.containsKey(appWindow)

&& mUnknownApps.get(appWindow) == UNKNOWN_STATE_WAITING_RESUME) {

if (DEBUG_UNKNOWN_APP_VISIBILITY) {

            Slog.d(TAG"App resume finished appWindow=" + appWindow);

        }

mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RELAYOUT);

    }

}


当relayout完成时,会清掉mUnknownApps

/**

 * Notifies that {@param appWindow} has relaid out.

 */

void notifyRelayouted(@NonNull AppWindowToken appWindow) {

if (!mUnknownApps.containsKey(appWindow)) {

return;

    }

if (DEBUG_UNKNOWN_APP_VISIBILITY) {

        Slog.d(TAG"App relayouted appWindow=" + appWindow);

    }

int state = mUnknownApps.get(appWindow);

if (state == UNKNOWN_STATE_WAITING_RELAYOUT) {

mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);

mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated);

    }

}


接着我们直接看AppTransition.java中,关于切换流程prepareAppTransitionLocked

mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);

mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUTAPP_TRANSITION_TIMEOUT_MS);

APP_TRANSITION_TIMEOUT_MS是一个5s的timeout机制,当超时了,系统也不会继续等待,而是强制继续执行。

case APP_TRANSITION_TIMEOUT: {

synchronized (mWindowMap) {

if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()

                    || !mClosingApps.isEmpty()) {

if (DEBUG_APP_TRANSITIONS || mWindowManagerDebugger.WMS_DEBUG_USER)

                    Slog.v(TAG_WM"*** APP TRANSITION TIMEOUT."

                    " isTransitionSet()=" mAppTransition.isTransitionSet()

" mOpeningApps.size()=" mOpeningApps.size()

" mClosingApps.size()=" mClosingApps.size());

mAppTransition.setTimeout();//设置超时flag

mWindowPlacerLocked.performSurfacePlacement();//强制绘制,不等待

        }

    }


直接看performSurfacePlacement的主要逻辑

// If we are ready to perform an app transition, check through all of the app tokens to be

// shown and see if they are ready to go.

if (mService.mAppTransition.isReady()) {

    defaultDisplay.pendingLayoutChanges |=

            surfacePlacer.handleAppTransitionReadyLocked();

if (DEBUG_LAYOUT_REPEATS)

        surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked",

                defaultDisplay.pendingLayoutChanges);

}



int handleAppTransitionReadyLocked() {

int appsCount = mService.mOpeningApps.size();

//如果这里return了,后面就不会触发,界面就没内容

if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {

return 0;

    }

    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER"AppTransitionReady");

if (DEBUG_APP_TRANSITIONS) Slog.v(TAG"**** GOOD TO GO");

int transit = mService.mAppTransition.getAppTransition();

if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {

        transit = AppTransition.TRANSIT_UNSET;

    }

mService.mSkipAppTransitionAnimation false;

mService.mNoAnimationNotifyOnTransitionFinished.clear();

    //界面显示成功,转场完成,remove掉之前的timeout消息

mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);

}

transitionGoodToGo的实现,这里只看与该问题相关的逻辑代码:

private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {

if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,

"Checking " + appsCount + " opening apps (frozen="

                    mService.mDisplayFrozen " timeout="

                    mService.mAppTransition.isTimeout() + ")...");

if (!mService.mAppTransition.isTimeout()) {

        ...

        //如果这里mUnknownApps不为空,那么说明relayout没有完成,直接return false

不会触发后面GOOD TO GO的逻辑显示

if (!mService.mUnknownAppVisibilityController.allResolved()) {

if (DEBUG_APP_TRANSITIONS) {

                Slog.v(TAG"unknownApps is not empty: "

                        mService.mUnknownAppVisibilityController.getDebugMessage());

            }

return false;

        }

        ...

    }

return true;

}



此该问题的相关逻辑已经梳理完成。

目前针对这个问题在transitionGoodToGo做了一个规避操作

if(!mService.mUnknownAppVisibilityController.allResolved() &&

!mService.mUnknownAppVisibilityController.getDebugMessage().contains("com.ss.android.message.sswo.SswoActivity")) {

if(DEBUG_APP_TRANSITIONS) {

Slog.v(TAG,"fix unknownApps is not empty: "

                            + mService.mUnknownAppVisibilityController.getDebugMessage());

                }

returnfalse;

            }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354