有时候我们也可以强制应用横频,这又是如何做到的?
就是调用了AMS的setRequestedOrientation接口,这个接口先是调用WMS的setAppOrientation函数设置这个Activity在WMS中的方向。然后在调用WMS的updateOrientationFromAppTokens函数旋转屏幕,最后在调用updateConfigurationLocked这个函数之前博客分析过就是是否让Activity重新Launch等。
- @Override
- public void setRequestedOrientation(IBinder token, int requestedOrientation) {
- synchronized (this) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- if (r.task != null && r.task.mResizeable) {
- // Fixed screen orientation isn't supported with resizeable activities.
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
- if (config != null) {
- r.frozenBeforeDestroy = true;
- if (!updateConfigurationLocked(config, r, false, false)) {
- mStackSupervisor.resumeTopActivitiesLocked();
- }
- }
- Binder.restoreCallingIdentity(origId);
- }
- }
我们先看下WMS的setAppOrientation函数,很简单就是找到这个apptoken的APPWindowToken,然后将其requestedOrientation值赋值。
- @Override
- public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "setAppOrientation()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- synchronized(mWindowMap) {
- AppWindowToken atoken = findAppWindowToken(token.asBinder());
- if (atoken == null) {
- Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
- return;
- }
-
- atoken.requestedOrientation = requestedOrientation;
- }
- }
然后我们再来看WMS的updateOrientationFromAppTokens函数,这个函数主要调用了updateOrientationFromAppTokensLocked函数,这个函数先调用另一个updateOrientationFromAppTokensLocked函数,根据这个函数的返回值,返回true代表要旋转,就调用computeNewConfigurationLocked计算Configuration返回。
- private Configuration updateOrientationFromAppTokensLocked(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
- if (!mDisplayReady) {
- return null;
- }
- Configuration config = null;
-
- if (updateOrientationFromAppTokensLocked(false)) {
- if (freezeThisOneIfNeeded != null) {
- AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded);
- if (atoken != null) {
- startAppFreezingScreenLocked(atoken);
- }
- }
- config = computeNewConfigurationLocked();
-
- } else if (currentConfig != null) {
- // No obvious action we need to take, but if our current
- // state mismatches the activity manager's, update it,
- // disregarding font scale, which should remain set to
- // the value of the previous configuration.
- mTempConfiguration.setToDefaults();
- mTempConfiguration.fontScale = currentConfig.fontScale;
- computeScreenConfigurationLocked(mTempConfiguration);
- if (currentConfig.diff(mTempConfiguration) != 0) {
- mWaitingForConfig = true;
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.layoutNeeded = true;
- int anim[] = new int[2];
- if (displayContent.isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mPolicy.selectRotationAnimationLw(anim);
- }
- startFreezingDisplayLocked(false, anim[0], anim[1]);
- config = new Configuration(mTempConfiguration);
- }
- }
-
- return config;
- }
我们来看下updateOrientationFromAppTokensLocked函数,我们先调用了getOrientationLocked函数获取上次强制设置的方向,如果不同就调用updateRotationUncheckedLocked函数,这个函数之前博客分析过了,流程就一样了。最后就到DisplayManagerService中设置设备的方向。
- boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
- long ident = Binder.clearCallingIdentity();
- try {
- int req = getOrientationLocked();
- if (req != mForcedAppOrientation) {
- mForcedAppOrientation = req;
- //send a message to Policy indicating orientation change to take
- //action like disabling/enabling sensors etc.,
- mPolicy.setCurrentOrientationLw(req);
- if (updateRotationUncheckedLocked(inTransaction)) {
- // changed
- return true;
- }
- }
-
- return false;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
最后我们回到AMS的setRequestedOrientation函数,我们调用updateConfigurationLocked函数,当返回false,就是现在的状态要改变(比如重启Activity),然后就调用ActivityStackSupervisor的resumeTopActivitiesLocked函数来启动最上面的Activity。
- public void setRequestedOrientation(IBinder token, int requestedOrientation) {
- synchronized (this) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- if (r.task != null && r.task.mResizeable) {
- // Fixed screen orientation isn't supported with resizeable activities.
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
- if (config != null) {
- r.frozenBeforeDestroy = true;
- if (!updateConfigurationLocked(config, r, false, false)) {
- mStackSupervisor.resumeTopActivitiesLocked();
- }
- }
- Binder.restoreCallingIdentity(origId);
- }
- }