Android Configuration学习随笔
在学习资源加载时发现Configuration有决定性的作用,所以详细看了下Configuration相关的内容。
看Configuration时需要先明白一个设计:系统中有两个configuration, 一个是common的configuration,暂且叫service configuration, 一个是Activity的override configuration.
原则是override configuration的优先级比较高,如果存在override configuration会优先使用。
所有的Configuration初始化从WMS开始,先从DisplayManagerService去拿基本的display信息,然后WMS中也会根据用户设置进行一些初始化。并把得到的Configuration设置重新设置给DMS,同时也会设置给AMS。
另外AMS在启动新的Task时也会为根据每个task的bounds计算override configuration。
Launch一个新的Activity时给的configuration参数:
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
Service Configuration
- Service configuration的初始化
WMS起来时,如果display已经准备好,就开始对configuration进行初始化
1). 从DMS获取display info对DisplayConent初始化
2). 读取用户设置的display信息进行设置
3). 对AMS的configuration进行设置
4). AMS中的更新会再借助WMS完成计算,由于WMS中可能再对configuration做调整,所以会再把更新后的Configuration sync会DMS. overrideconfiguration.
public void displayReady() {
for (Display display : mDisplays) {
displayReady(display.getDisplayId());
}
synchronized(mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
readForcedDisplayPropertiesLocked(displayContent);
mDisplayReady = true;
}
try {
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
}
从DMS获取最原始的Display信息,给DisplayContent进行设置
void initializeDisplayBaseInfo() {
// Bootstrap the default logical display from the display manager.
final DisplayInfo newDisplayInfo =
mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
if (newDisplayInfo != null) {
mDisplayInfo.copyFrom(newDisplayInfo);
}
mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
}
ActivityManagerService
AMS内的config通过WMS计算得到
public void updateConfiguration(Configuration values) {
synchronized(this) {
if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
values = mWindowManager.computeNewConfiguration();
}
}
WindowManagerService
WMS内部计算时,
1. 先更新display信息
2. 此时,displayInfo已经是更新过的,用来再计算config
private Configuration computeNewConfigurationLocked() {
if (!mDisplayReady) {
return null;
}
Configuration config = new Configuration();
config.fontScale = 0;
computeScreenConfigurationLocked(config);
return config;
}
/* Do not call if mDisplayReady == false /
void computeScreenConfigurationLocked(Configuration config) {
final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(
config.uiMode);
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
Configuration.ORIENTATION_LANDSCAPE;
config.screenWidthDp =
(int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation, config.uiMode) /
mDisplayMetrics.density);
config.screenHeightDp =
(int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation, config.uiMode) /
mDisplayMetrics.density);
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh,
mDisplayMetrics.density, config);
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
? Configuration.SCREENLAYOUT_ROUND_YES
: Configuration.SCREENLAYOUT_ROUND_NO);
config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode,
mDisplayMetrics, dw, dh);
config.densityDpi = displayInfo.logicalDensityDpi;
// Update the configuration based on available input devices, lid switch,
// and platform configuration.
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.keyboard = Configuration.KEYBOARD_NOKEYS;
config.navigation = Configuration.NAVIGATION_NONAV;
// Let the policy update hidden states.
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
先拿到displayContent, DisplayContent中的一些内容可能已经被WMS做了新的设置,(例如PhoneWindowManager的设置),再计算其他一些更新
然后和DisplayManagerService同步,将更新后的overrideConfig设置过去
displayContent中的其他内容更新
最后还计算了一下CompatibleScreenScale,不晓得有啥用?
/* Do not call if mDisplayReady == false /
DisplayInfo updateDisplayAndOrientationLocked(int uiMode) {
// TODO(multidisplay): For now, apply Configuration to main screen only.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
final int realdw = rotated ?
displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
final int realdh = rotated ?
displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;
int dw = realdw;
int dh = realdh;
// Update application display metrics.
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
displayInfo.rotation = mRotation;
displayInfo.logicalWidth = dw;
displayInfo.logicalHeight = dh;
displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
displayInfo.appWidth = appWidth;
displayInfo.appHeight = appHeight;
displayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
displayInfo.getAppMetrics(mDisplayMetrics);
mDisplayManagerInternal.**setDisplayInfoOverrideFromWindowManager**(
displayContent.getDisplayId(), displayInfo);
displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
if (false) {
Slog.i(TAG_WM, "Set app display size: " + appWidth + " x " + appHeight);
}
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
mCompatDisplayMetrics);
return displayInfo;
}
设置OverrideDisplayInfo到DisplayManagerService
public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
if (info != null) {
if (mOverrideDisplayInfo == null) {
mOverrideDisplayInfo = new DisplayInfo(info);
mInfo = null;
return true;
}
if (!mOverrideDisplayInfo.equals(info)) {
mOverrideDisplayInfo.copyFrom(info);
mInfo = null;
return true;
}
} else if (mOverrideDisplayInfo != null) {
mOverrideDisplayInfo = null;
mInfo = null;
return true;
}
return false;
}
override configuration
根据bounds算的override config
Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
mFullscreen = bounds == null;
if (mFullscreen) {
if (mBounds != null && StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
mTmpRect.set(bounds);
adjustForMinimalTaskDimensions(mTmpRect);
if (mBounds == null) {
mBounds = new Rect(mTmpRect);
} else {
mBounds.set(mTmpRect);
}
if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
mOverrideConfig = **calculateOverrideConfig**(mTmpRect, insetBounds,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
如下函数可以看到override configuration的计算过程,例如根据宽高直接计算到新的的orientation.
所以对一个Activity做resize, 如果width > height, 会重新加载landscape的资源。
private Configuration calculateOverrideConfig(Rect bounds, Rect insetBounds,
boolean overrideWidth, boolean overrideHeight) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
subtractNonDecorInsets(
mTmpNonDecorBounds, insetBounds != null ? insetBounds : bounds,
overrideWidth, overrideHeight);
subtractStableInsets(
mTmpStableBounds, insetBounds != null ? insetBounds : bounds,
overrideWidth, overrideHeight);
// For calculating screenWidthDp, screenWidthDp, we use the stable inset screen area,
// i.e. the screen area without the system bars.
final Configuration serviceConfig = mService.mConfiguration;
final Configuration config = new Configuration(Configuration.EMPTY);
// TODO(multidisplay): Update Dp to that of display stack is on.
final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
config.screenWidthDp =
Math.min((int)(mTmpStableBounds.width() / density), serviceConfig.screenWidthDp);
config.screenHeightDp =
Math.min((int)(mTmpStableBounds.height() / density), serviceConfig.screenHeightDp);
// TODO: Orientation?
config.orientation = (config.screenWidthDp <= config.screenHeightDp)
? Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
// Always set fontScale to be euqal to global. Can't set to 0, as that makes the override
// config not equal to EMPTY. Also can't set to 1, as Configuration.updateFrom will use
// the override scale as long as it's non-zero, and we'll always use 1.
config.fontScale = serviceConfig.fontScale;
// For calculating screen layout, we need to use the non-decor inset screen area for the
// calculation for compatibility reasons, i.e. screen area without system bars that could
// never go away in Honeycomb.
final int compatScreenWidthDp = (int)(mTmpNonDecorBounds.width() / density);
final int compatScreenHeightDp = (int)(mTmpNonDecorBounds.height() / density);
final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);;
config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
config.smallestScreenWidthDp = mService.mWindowManager.getSmallestWidthForTaskBounds(
insetBounds != null ? insetBounds : bounds);
return config;
}