Android Configuration

本文深入剖析了Android系统中Configuration的工作原理,包括Service Configuration和服务覆盖配置(Override Configuration)的区别及作用。探讨了Configuration如何影响资源加载,并介绍了Activity启动时Configuration参数的具体传递过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  1. 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值