一、Android资源分类
Android 资源大概分为两个部分:assets 和 res
1.1 assets 资源
assets 资源放在 assets 目录下,它里面保存一些原始的文件,可以以任何方式来进行组织,这些文件最终会原封不动的被打包进 APK 文件中,通过AssetManager 来获取 asset 资源,代码如下
java
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("fileName");
1.2 res资源
res 资源放在主工程的 res 目录下,这类资源一般都会在编译阶段生成一个资源 ID 供我们使用,res 目录包括 animator、anim、 color、drawable、layout、menu、raw、values、XML等,通过 getResource() 去获取 Resources 对象
java
Resources res = getContext().getResources();
APK 的生成过程中,会生成资源索引表 resources.arsc 文件和 R.java 文件,前者资源索引表 resources.arsc 记录了所有的应用程序资源目录的信息,包括每一个资源名称、类型、值、ID以及所配置的维度信息,后者定义了各个资源 ID 常量,运行时通过 Resources 和 AssetManger 共同完成资源的加载,如果资源是个文件,Resouces 先根据资源 ID 查找出文件名,AssetManger 再根据文件名查找出具体的资源,关于 resources.arsc,可以查看 0xA01 ASOP应用框架:APK 是如何生成的
二、资源的加载解析
应用开发基本都是通过setContentView方法进行Activity布局文件加载的。
java
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2.1 Activity -> PhoneWindow
frameworks/base/core/java/android/app/Activity.java
java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
private Window mWindow;
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
...代码省略...
//注释1
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...代码省略...
}
public Window getWindow() {
return mWindow;
}
public void setContentView(@LayoutRes int layoutResID) {
//注释2,调用PhoneWindow的setContentView方法
getWindow().setContentView(layoutResID);
...代码省略...
}
}
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private LayoutInflater mLayoutInflater;
@Override
public void setContentView(int layoutResID) {
...代码省略...
//注释3
mLayoutInflater.inflate(layoutResID, mContentParent);
...代码省略...
}
}
在注释1处创建PhoneWindow对象实例。在注释2处进一步调用PhoneWindow的setContentView方法。在注释3处调用LayoutInflater的inflate方法。
2.2 PhoneWindow -> LayoutInflater
frameworks/base/core/java/android/view/LayoutInflater.java
java
public abstract class LayoutInflater {
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
/**
* @param resource 要解析的 XML 布局文件 ID
* @param root 表示根布局
* @param attachToRoot 是否要添加到父布局 root 中
* @return 返回布局文件对应的view
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//注释1,预编译
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
//注释2
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
private @Nullable View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
boolean attachToRoot) {
...代码省略...
//获取需要解析的资源文件的 pkg 和 layout
String pkg = res.getResourcePackageName(resource);
String layout = res.getResourceEntryName(resource);
...代码省略...
//根据mPrecompiledClassLoader通过反射获取预编译生成的view对象的Class类
Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);
Method inflater = clazz.getMethod(layout, Context.class, int.class);
View view = (View) inflater.invoke(null, mContext, resource);
if (view != null && root != null) {
//获取XmlResourceParser对象实例
XmlResourceParser parser = res.getLayout(resource);
try {
AttributeSet attrs = Xml.asAttributeSet(parser);
advanceToRootNode(parser);
ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
//是否需要将view添加到根布局中
if (attachToRoot) {
//将生成的view添加到根布局中
root.addView(view, params);
} else {
//将根布局的布局参数设置到生成的view中
view.setLayoutParams(params);
}
} finally {
parser.close();
}
}
return view;
...代码省略...
}
}
inflate方法会进一步调用inflate(resource, root, root != null)方法。然后在注释1处调用tryInflatePrecompiled方法,该方法是一个在编译器运行阶段的一个优化,因为布局文件越复杂 XmlPullParser 解析 XML 越耗时, tryInflatePrecompiled 方法根据 XML 预编译生成compiled_view.dex, 然后通过反射来生成对应的 View,从而减少 XmlPullParser 解析 XML 的时间,然后根据 attachToRoot 参数来判断是否要把view添加到根布局中,还是设置根布局的参数设置给view。注释2处调用 Resources 的 getLayout 方法获取资源解析器 XmlResourceParser。
2.3 LayoutInflater -> Resources
frameworks/base/core/java/android/content/res/Resources.java
java
public class Resources {
private ResourcesImpl mResourcesImpl;
private Resources() {
this(null);
...代码省略...
mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config, new DisplayAdjustments());
}
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
//调用loadXmlResourceParser方法
return loadXmlResourceParser(id, "layout");
}
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
throws NotFoundException {
//获取一个临时的TypedValue实例,用于存储资源的值。
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
//注释1,通过ResourcesImpl实例获取资源的值,并将其存储在value中。资源id和TypedValue被传递进去。
impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
} finally {
//释放临时TypedValue
releaseTempTypedValue(value);
}
}
@NonNull
@UnsupportedAppUsage
XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
String type) throws NotFoundException {
return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
}
}
Resources的getLayout方法进一步调用loadXmlResourceParser方法,在注释1处调用ResourcesImpl的getValue方法获取资源di对应的值,并将其存储到value中,TypedValue是动态的数据容器,主要用来存储 Resource 的资源,获取 XML 资源保存到 TypedValue,之后调用 ResourcesImpl 的 loadXmlResourceParser 方法加载对应的解析器。
2.3.1 ResourcesImpl获取资源
frameworks/base/core/java/android/content/res/ResourcesImpl.java
java
public class ResourcesImpl {
final AssetManager mAssets;
void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
//注释1,调用AssetManager的getResourceValue方法
boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
if (found) {
return;
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
}
}
> /frameworks/base/core/java/android/content/res/AssetManager.java
public final class AssetManager implements AutoCloseable {
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
@NonNull TypedValue outValue, boolean resolveReferences);
boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
Objects.requireNonNull(outValue, "outValue");
synchronized (this) {
ensureValidLocked();
//注释2,调用nativeGetResourceValue方法
final int cookie = nativeGetResourceValue(
mObject, resId, (short) densityDpi, outValue, resolveRefs);
if (cookie <= 0) {
return false;
}
// Convert the changing configurations flags populated by native code.
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) {
return false;
}
}
return true;
}
}
}
2.3.1.1 AssetManager的nativeGetResourceValue方法
cpp
>frameworks/base/core/jni/android_util_AssetManager.cpp
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
jshort density, jobject typed_value,
jboolean resolve_references) {
//注释3,使用 ScopedLock 对资产管理器进行加锁,确保线程安全。
//AssetManagerFromLong(ptr) 将长整型指针转换为 AssetManager2 实例。
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
//注释4,通过资源 ID 和密度从资产管理器中获取资源值。may_be_bag 参数被设置为 false,表示资源不允许是包裹(Bag)类型。
auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
static_cast<uint16_t>(density));
//检查资源是否存在
if (!value.has_value()) {
//如果不存在返回一个无效的Cookie,指示资源未找到
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
if (resolve_references) {
auto result = assetmanager->ResolveReference(value.value());
if (!result.has_value()) {
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
}
//将value指向的内容复制到typed_value对象中,上层可以得到该对象
return CopyValue(env, *value, typed_value);
}
>frameworks/base/libs/androidfw/AssetManager2.cpp
base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
uint32_t resid, bool may_be_bag, uint16_t density_override) const {
auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
false /* ignore_configuration */);
if (!result.has_value()) {
return base::unexpected(result.error());
}
auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry);
if (result_map_entry != nullptr) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return base::unexpected(std::nullopt);
}
// Create a reference since we can't represent this complex type as a Res_value.
return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
resid, result->config);
}
// Convert the package ID to the runtime assigned package ID.
Res_value value = std::get<Res_value>(result->entry);
result->dynamic_ref_table->lookupResourceValue(&value);
return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
resid, result->config);
}
2.3.1.2 AssetManager的getPooledStringForCookie方法
java
public final class AssetManager implements AutoCloseable {
CharSequence getPooledStringForCookie(int cookie, int id) {
// Cookies map to ApkAssets starting at 1.
ApkAssets[] apkAssets = getApkAssets();
ApkAssets apkAssetsItem = apkAssets[cookie - 1];
return apkAssetsItem.getStringFromPool(id);
}
}
>frameworks/base/core/java/android/content/res/ApkAssets.java
public final class ApkAssets {
private final StringBlock mStringBlock;
CharSequence getStringFromPool(int idx) {
if (mStringBlock == null) {
return null;
}
synchronized (this) {
return mStringBlock.get(idx);
}
}
}
>frameworks/base/core/java/android/content/res/StringBlock.java
public final class StringBlock implements Closeable {
private final long mNative;
private CharSequence[] mStrings;
private SparseArray<CharSequence> mSparseStrings;
public CharSequence get(int idx) {
synchronized (this) {
if (mStrings != null) {
CharSequence res = mStrings[idx];
if (res != null) {
return res;
}
} else if (mSparseStrings != null) {
CharSequence res = mSparseStrings.get(idx);
if (res != null) {
return res;
}
} else {
final int num = nativeGetSize(mNative);
if (mUseSparse && num > 250) {
mSparseStrings = new SparseArray<CharSequence>();
} else {
mStrings = new CharSequence[num];
}
}
String str = nativeGetString(mNative, idx);
CharSequence res = str;
int[] style = nativeGetStyle(mNative, idx);
if (localLOGV) Log.v(TAG, "Got string: " + str);
if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style));
if (style != null) {
if (mStyleIDs == null) {
mStyleIDs = new StyleIDs();
}
// the style array is a flat array of <type, start, end> hence
// the magic constant 3.
for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) {
int styleId = style[styleIndex];
if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId
|| styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId
|| styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId
|| styleId == mStyleIDs.subId || styleId == mStyleIDs.supId
|| styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId
|| styleId == mStyleIDs.marqueeId) {
// id already found skip to next style
continue;
}
String styleTag = nativeGetString(mNative, styleId);
if (styleTag.equals("b")) {
mStyleIDs.boldId = styleId;
} else if (styleTag.equals("i")) {
mStyleIDs.italicId = styleId;
} else if (styleTag.equals("u")) {
mStyleIDs.underlineId = styleId;
} else if (styleTag.equals("tt")) {
mStyleIDs.ttId = styleId;
} else if (styleTag.equals("big")) {
mStyleIDs.bigId = styleId;
} else if (styleTag.equals("small")) {
mStyleIDs.smallId = styleId;
} else if (styleTag.equals("sup")) {
mStyleIDs.supId = styleId;
} else if (styleTag.equals("sub")) {
mStyleIDs.subId = styleId;
} else if (styleTag.equals("strike")) {
mStyleIDs.strikeId = styleId;
} else if (styleTag.equals("li")) {
mStyleIDs.listItemId = styleId;
} else if (styleTag.equals("marquee")) {
mStyleIDs.marqueeId = styleId;
}
}
res = applyStyles(str, style, mStyleIDs);
}
if (mStrings != null) mStrings[idx] = res;
else mSparseStrings.put(idx, res);
return res;
}
}
}
三、影响资源加载的配置
在第二节中,可以发现Android应用资源加载最终是通过AssetManager2的GetResource方法,而AssetManager2内部有一个configuration_的成员变量,AssetManager2从这个变量去获取当前屏幕的宽度从而影响我们最终获取那个资源。所以,只要我们能找到这个变量在哪里设置,我们就可以通过修改这个变量来影响应用资源的加载。
3.1 AssetManager2的SetConfiguration方法
cpp
>frameworks/base/libs/androidfw/AssetManager2.cpp
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
const int diff = configuration_.diff(configuration);
//注释1
configuration_ = configuration;
//注释2
if (diff) {
RebuildFilterList();
InvalidateCaches(static_cast<uint32_t>(diff));
}
}
在注释1处为configuration_ 赋值。在注释2处判断如果配置发生了变化,则更新本地缓存数据。
此方法是被android_util_AssetManager的NativeSetConfiguration方法调用的。
3.2 android_util_AssetManager的NativeSetConfiguration方法
cpp
>frameworks/base/core/jni/android_util_AssetManager.cpp
{"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
(void*)NativeSetConfiguration}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
jstring locale, jint orientation, jint touchscreen, jint density,
jint keyboard, jint keyboard_hidden, jint navigation,
jint screen_width, jint screen_height,
jint smallest_screen_width_dp, jint screen_width_dp,
jint screen_height_dp, jint screen_layout, jint ui_mode,
jint color_mode, jint major_version) {
ResTable_config configuration;
memset(&configuration, 0, sizeof(configuration));
configuration.mcc = static_cast<uint16_t>(mcc);
configuration.mnc = static_cast<uint16_t>(mnc);
configuration.orientation = static_cast<uint8_t>(orientation);
configuration.touchscreen = static_cast<uint8_t>(touchscreen);
configuration.density = static_cast<uint16_t>(density);
configuration.keyboard = static_cast<uint8_t>(keyboard);
configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
configuration.navigation = static_cast<uint8_t>(navigation);
configuration.screenWidth = static_cast<uint16_t>(screen_width);
configuration.screenHeight = static_cast<uint16_t>(screen_height);
configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
configuration.screenLayout = static_cast<uint8_t>(screen_layout);
configuration.uiMode = static_cast<uint8_t>(ui_mode);
configuration.colorMode = static_cast<uint8_t>(color_mode);
configuration.sdkVersion = static_cast<uint16_t>(major_version);
...代码省略...
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
assetmanager->SetConfiguration(configuration);
}
android_util_AssetManager的NativeSetConfiguration方法是被AssetManager的setConfiguration方法调用。
3.3 AssetManager的setConfiguration方法
frameworks/base/core/java/android/content/res/AssetManager.java
java
public final class AssetManager implements AutoCloseable {
public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
synchronized (this) {
ensureValidLocked();
nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
colorMode, majorVersion);
}
}
}
AssetManager的setConfiguration是被ResourcesImpl的updateConfiguration方法调用的。
3.4 ResourcesImpl的updateConfiguration方法
frameworks/base/core/java/android/content/res/ResourcesImpl.java
java
public class ResourcesImpl {
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
}
public void updateConfiguration(Configuration config, DisplayMetrics metrics,
CompatibilityInfo compat) {
...代码省略...
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
keyboardHidden, mConfiguration.navigation, width, height,
mConfiguration.smallestScreenWidthDp,
mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
mConfiguration.screenLayout, mConfiguration.uiMode,
mConfiguration.colorMode, Build.VERSION.RESOURCES_SDK_INT);
...代码省略...
}
}
ResourcesImpl的updateConfiguration方法是被Resources的updateConfiguration方法调用的。
3.5 Resources的updateConfiguration方法
frameworks/base/core/java/android/content/res/Resources.java
java
public class Resources {
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
public void updateConfiguration(Configuration config, DisplayMetrics metrics,
CompatibilityInfo compat) {
mResourcesImpl.updateConfiguration(config, metrics, compat);
}
}
Resources的updateConfiguration方法是被ContextImpl的createAppContext方法调用的。
3.6 ContextImpl的createAppContext方法
frameworks/base/core/java/android/app/ContextImpl.java
java
class ContextImpl extends Context {
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
}
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
0, null, opPackageName);
//注释1
context.setResources(packageInfo.getResources());
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
return context;
}
void setResources(Resources r) {
if (r instanceof CompatResources) {
((CompatResources) r).setContext(this);
}
mResources = r;
}
}
frameworks/base/core/java/android/app/ActivityThread.java
java
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
...代码省略...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...代码省略...
}
private void attach(boolean system, long startSeq) {
...代码省略...
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
//注释2
ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
...代码省略...
}
}
在注释1处调用LoadedApk的getResources方法获取Resource对象实例。另外在注释2处,ContextImpl的createAppContext方法最早是被ActivityThread的attach方法调用的。
3.7 LoadedApk 的getResources方法
java
public final class LoadedApk {
public Resources getResources() {
if (mResources == null) {
final String[] splitPaths;
try {
splitPaths = getSplitPaths(null);
} catch (NameNotFoundException e) {
// This should never fail.
throw new AssertionError("null split not found");
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader(), null);
}
return mResources;
}
}
3.8 ResourcesManager的getResources方法
java
public class ResourcesManager {
public @Nullable Resources getResources(
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader,
@Nullable List<ResourcesLoader> loaders) {
...代码省略...
return createResources(activityToken, key, classLoader, assetsSupplier);
}
private @Nullable Resources createResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (this) {
...代码省略...
if (activityToken != null) {
return createResourcesForActivityLocked(activityToken, classLoader,
resourcesImpl, key.mCompatInfo);
} else {
return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
}
}
private @NonNull Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
@NonNull CompatibilityInfo compatInfo) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
cleanupReferences(activityResources.activityResources,
activityResources.activityResourcesQueue);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
activityResources.activityResources.add(
new WeakReference<>(resources, activityResources.activityResourcesQueue));
return resources;
}
private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
cleanupReferences(mResourceReferences, mResourcesReferencesQueue);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
mResourceReferences.add(new WeakReference<>(resources, mResourcesReferencesQueue));
return resources;
}
}