unity黑屏现状问题总结
总结下unity的游戏接sdk时的黑屏问题现状
现象:
目前黑屏有两种情况:
1. 点击登录面板,从面板中点击跳到三方界面或者跳到第三个界面;再点击返回键时会出现下面的游戏界面黑屏;
2. 点击登录面板之后,按桌面键返回桌面;然后再从最近任务键进入会发现下面的游戏界面黑屏;
原因:
以第一种情况为例,一般情况下出现:
05-28 10:27:04.642 1000 2154 3441 I am_create_activity: [0,41511060,4450,com.ss.android.ugc.aweme/.openauthorize.AwemeAuthorizedActivity,NULL,NULL,NULL,872448000]`
05-28 10:27:04.642 1000 2154 3441 I wm_task_moved: [4450,0,2147483647]`
05-28 10:27:04.649 1000 2154 3441 I am_pause_activity: [25025,39231624,com.hermes.fgame/com.bytedance.ttgame.sdk.module.account.login.ui.LoginActivity,userLeaving=true]`
05-28 10:27:04.654 10454 25025 25025 I am_on_paused_called: [0,com.bytedance.ttgame.sdk.module.account.login.ui.LoginActivity,performPause]`
05-28 10:27:04.663 1000 2154 3329 I am_proc_bound: [0,25874,com.ss.android.ugc.aweme:push]`
05-28 10:27:04.715 1000 2154 3441 I configuration_changed: 536872064`
05-28 10:27:04.778 1000 2154 2194 I am_stop_activity: [0,214407528,com.hermes.fgame/.SDKBridgeActivity]`
05-28 10:27:04.793 1000 2154 2194 I sysui_count: [window_time_0,7]`
`05-28 10:27:04.793 1000 2154 2194 I sysui_multi_action: [757,803,799,window_time_0,802,7]`
05-28 10:27:04.804 10454 25025 25025 I am_on_stop_called: [0,com.hermes.fgame.SDKBridgeActivity,STOP_ACTIVITY_ITEM]`
05-28 10:27:05.975 10421 25905 25905 I am_on_resume_called: [0,com.ss.android.ugc.aweme.openauthorize.AwemeAuthorizedActivity,RESUME_ACTIVITY]`
黑屏的原因是游戏界面调用了onstop,则会出现黑屏;直到其重新调用onResume才能重新可见;
想法:
1. 在stop的时候调用onResume
2. 想办法起到三方授权界面的时候不调用onstop
3. 想办法只生成一个界面
方案1已经被游戏否决了,其实也能理解;改变Activity的生命周期总是要承担很大的风险的,那么方案2和方案3
方案2:如何使调起三方授权界面时不再调用unity游戏界面的onstop
`1554 private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {`
`1555 ActivityRecord prev = mPausingActivity;`
`1556 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);`
`1557`
`1558 if (prev != null) {`
`1559 prev.setWillCloseOrEnterPip(false);`
`1560 final boolean wasStopping = prev.isState(STOPPING);`
`1561 prev.setState(PAUSED, "completePausedLocked");`
`1562 if (prev.finishing) {`
`1563 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);`
`1564 prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,`
`1565 "completedPausedLocked");`
`1566 } else if (prev.app != null) {`
`1567 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev`
`1568 + " wasStopping=" + wasStopping + " visible=" + prev.visible);`
`1569 if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {`
`1570 if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,`
`1571 "Complete pause, no longer waiting: " + prev);`
`1572 }`
`1573 if (prev.deferRelaunchUntilPaused) {`
`1574 // Complete the deferred relaunch that was waiting for pause to complete.`
`1575 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);`
`1576 prev.relaunchActivityLocked(false /* andResume */,`
`1577 prev.preserveWindowOnDeferredRelaunch);`
`1578 } else if (wasStopping) {`
`1579 // We are also stopping, the stop request must have gone soon after the pause.`
`1580 // We can't clobber it, because the stop confirmation will not be handled.`
`1581 // We don't need to schedule another stop, we only need to let it happen.`
`1582 prev.setState(STOPPING, "completePausedLocked");`
`1583 } else if (!prev.visible || shouldSleepOrShutDownActivities()) {`
`1584 // Clear out any deferred client hide we might currently have.`
`1585 prev.setDeferHidingClient(false);`
`1586 // If we were visible then resumeTopActivities will release resources before`
`1587 // stopping.`
`1588 addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);`
`1589 }`
`1590 } else {`
`1591 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);`
`1592 prev = null;`
`1593 }`
`1594 // It is possible the activity was freezing the screen before it was paused.`
`1595 // In that case go ahead and remove the freeze this activity has on the screen`
`1596 // since it is no longer visible.`
`1597 if (prev != null) {`
`1598 prev.stopFreezingScreenLocked(true /*force*/);`
`1599 }`
`1600 mPausingActivity = null;`
`1601 }`
`1602`
`1603 if (resumeNext) {`
`1604 final ActivityStack topStack = mStackSupervisor.getFocusedStack();`
`1605 if (!topStack.shouldSleepOrShutDownActivities()) {`
`1606 mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);`
`1607 } else {`
`1608 checkReadyForSleep();`
`1609 ActivityRecord top = topStack.topRunningActivityLocked();`
`1610 if (top == null || (prev != null && top != prev)) {`
`1611 // If there are no more activities available to run, do resume anyway to start`
`1612 // something. Also if the top activity on the stack is not the just paused`
`1613 // activity, we need to go ahead and resume it to ensure we complete an`
`1614 // in-flight app switch.`
`1615 mStackSupervisor.resumeFocusedStackTopActivityLocked();`
`1616 }`
`1617 }`
`1618 }`
`1619`
`1620 if (prev != null) {`
`1621 prev.resumeKeyDispatchingLocked();`
`1622`
`1623 if (prev.app != null && prev.cpuTimeAtResume > 0`
`1624 && mService.mBatteryStatsService.isOnBattery()) {`
`1625 long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)`
`1626 - prev.cpuTimeAtResume;`
`1627 if (diff > 0) {`
`1628 BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();`
`1629 synchronized (bsi) {`
`1630 BatteryStatsImpl.Uid.Proc ps =`
`1631 bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,`
`1632 prev.info.packageName);`
`1633 if (ps != null) {`
`1634 ps.addForegroundTimeLocked(diff);`
`1635 }`
`1636 }`
`1637 }`
`1638 }`
`1639 prev.cpuTimeAtResume = 0; // reset it`
`1640 }`
`1641`
`1642 // Notify when the task stack has changed, but only if visibilities changed (not just`
`1643 // focus). Also if there is an active pinned stack - we always want to notify it about`
`1644 // task stack changes, because its positioning may depend on it.`
`1645 if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause`
`1646 || getDisplay().hasPinnedStack()) {`
`1647 mService.mTaskChangeNotificationController.notifyTaskStackChanged();`
`1648 mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;`
`1649 }`
`1650`
`1651 mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);`
`1652 }`
想要不被stop那么不能被调用到addToStopping;这就有两种可能
1. 使unity activity调用relaunch的逻辑
prev.relaunchActivityLocked(false /* andResume */, prev.preserveWindowOnDeferredRelaunch);
2. 使unity Activity的visable属性为true
结论
1. 经调研不太好实现,因为unity实现了对所有config change的监听以便于属性变化时及时变化游戏界面
2. 需要三方配合;将三方授权界面Activity的fullscreen属性置为false;如写成Dialog style的形式
经试验,方案2对情况1可行;缺点: 1. 需要授权sdk方的配合 2.对情况2还是没办法解决;
方案3; 将游戏activity和登陆面板合并,将登陆面板以Dialog形式显示,依附于游戏unity activity,此时两种情况应该都能解决; 缺点:改动太大;对于sdk限制也太大
业内方案:
想了下,想要根治这个问题可能只能讲登陆面板置位dialog形式;但是这种方式局限太大了,业界的sdk不可能都是以style形式实现的吧?至少我接的uc渠道就有这个问题;
因此用unity demo继承下uc的sdk,发现情况1和情况2都能复现
bilibili黑屏情况1.jpg uc黑屏情况2.jpg因此这个问题确实也是正常的,属于AMS管理Activity的生命周期的正常逻辑
结论:
-
如果要有两个Activity;那么点击登录面板之后,按桌面键返回桌面;然后再从最近任务键进入游戏界面黑屏;
-
如果有三个activity,且第三个activity为全屏的,那么返回时如果登陆面板不自动取消,那么就会出现unity activity的黑屏;同时,个人认为三方授权界面不要写成fullscreen格式的比较好