Android知识总结
一、系统实例化View的流程
我们进入AppCompatActivity
中的onCreate
方法:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//创建 AppCompatDelegate
final AppCompatDelegate delegate = getDelegate();
//初始化 Factory
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
1.1、创建 AppCompatDelegate
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
1.2、初始化 AppCompatDelegate
在抽象类AppCompatDelegate
的实现类AppCompatDelegateImpl
中实现
public void installViewFactory() {
//构建 LayoutInflater
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
//当 Factory == null 时,设置 Factory2
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
执行 LayoutInflaterCompat#setFactory2
构建 Factory2
public static void setFactory2(
@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
//设置 factory
inflater.setFactory2(factory);
//sdk 版本小于 21 执行 下面代码
if (Build.VERSION.SDK_INT < 21) {
final LayoutInflater.Factory f = inflater.getFactory();
if (f instanceof LayoutInflater.Factory2) {
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
} else {
forceSetFactory2(inflater, factory);
}
}
}
在LayoutInflater#setFactory2
中执行
public void setFactory2(Factory2 factory) {
// Factory2 只能创建一次,当创建后再执行抛出异常
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
//标志位,使 m 只能够创建一次
mFactorySet = true;
if (mFactory == null) {
//设置 mFactory
mFactory = mFactory2 = factory;
} else {
//当我们 HOOK 时执行,前提利用反射把标志位 mFactorySet 至 false
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
我们用的动态换肤,就是利用HOOK技术。有两种设置方法:
- 1)、在
super.onCreate(savedInstanceState)
方法之前执行 setFactory2 重新设置我们自己 Factory2 添加到里面。 - 2)、利用反射技术把标志位
mFactorySet
置为false,然后setFactory2我们自己的 Factory。这样再次设置 Factory2 就不会抛异常了。
private static class FactoryMerger implements Factory2 {
//再次设至的 Factory
private final Factory mF1, mF2;
//第一次设至的 Factory
private final Factory2 mF12, mF22;
FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
mF1 = f1;
mF2 = f2;
mF12 = f12;
mF22 = f22;
}
@Nullable
public View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}
@Nullable
public View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
: mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
: mF2.onCreateView(name, context, attrs);
}
}
1.3、Factory 类
public interface Factory2 extends Factory {
@Nullable
View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs);
}
public interface Factory {
@Nullable
View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs);
}
上面的代码我们可以看到 Factory2
是继承Factory
的接口。
1.4、初始化 xml 怎么利用 Factory2
setContentView()
的过程就不跟踪了,我们直接看createViewFromTag
方法。
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
//解析view标签
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
//如果需要该标签与主题相关,需要对context进行包装,
//将主题信息加入context包装类ContextWrapper
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
//设置Factory,来对View做额外的拓展,这块属于可定制的内容
try {
View view = tryCreateView(parent, name, context, attrs);
//如果此时不存在Factory,不管Factory还是Factory2,
//还是mPrivateFactory都不存在,那么会直接对name直接进行解析
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
//如果name中包含.即为自定义View,
//否则为原生SDK的View控件
if (-1 == name.indexOf('.')) {
view = onCreateView(context, parent, name, attrs);
} else {
view = createView(context, name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
//返回创建的 View
return view;
}
}
利用定义的 Factory 创建 View
public final View tryCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {
if (name.equals(TAG_1995)) {
//BlinkLayout是一种闪烁的FrameLayout,它包裹的内容会一直闪烁,类似QQ提示消息那种。
return new BlinkLayout(context, attrs);
}
View view;
if (mFactory2 != null) {
//利用 Factory2 接口创建
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
//利用 Factory 接口创建
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
//利用自定义的 Factory
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
return view;
}
然后执行 AppCompatDelegateImpl
里面重载的 onCreateView
方法。
//Factory2 里面的
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return createView(parent, name, context, attrs);
}
//Factory 里面的
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);
}
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
//获取属性
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
String viewInflaterClassName =
a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
if (viewInflaterClassName == null) {
mAppCompatViewInflater = new AppCompatViewInflater();
} else {
try {
Class> viewInflaterClass = Class.forName(viewInflaterClassName);
mAppCompatViewInflater =
(AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
.newInstance();
} catch (Throwable t) {
Log.i(TAG, "Failed to instantiate custom view inflater "
+ viewInflaterClassName + ". Falling back to default.", t);
//创建 AppCompatViewInflater
mAppCompatViewInflater = new AppCompatViewInflater();
}
}
}
boolean inheritContext = false;
//把 xx 控件转换成 AppCompatxx 控件
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read app:theme as a fallback at all times for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
);
}
然后在AppCompatViewInflater
的createView
进行转换
final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
case "EditText":
view = createEditText(context, attrs);
verifyNotNull(view, name);
break;
case "Spinner":
view = createSpinner(context, attrs);
verifyNotNull(view, name);
break;
case "ImageButton":
view = createImageButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckBox":
view = createCheckBox(context, attrs);
verifyNotNull(view, name);
break;
case "RadioButton":
view = createRadioButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckedTextView":
view = createCheckedTextView(context, attrs);
verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
view = createAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
view = createMultiAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "RatingBar":
view = createRatingBar(context, attrs);
verifyNotNull(view, name);
break;
case "SeekBar":
view = createSeekBar(context, attrs);
verifyNotNull(view, name);
break;
case "ToggleButton":
view = createToggleButton(context, attrs);
verifyNotNull(view, name);
break;
default:
// The fallback that allows extending class to take over view inflation
// for other tags. Note that we don't check that the result is not-null.
// That allows the custom inflater path to fall back on the default one
// later in this method.
view = createView(context, name, attrs);
}
if (view == null && originalContext != context) {
// If the original context does not equal our themed context, then we need to manually
// inflate it using the name so that android:theme takes effect.
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
// If we have created a view, check its android:onClick
checkOnClickListener(view, attrs);
}
return view;
}
二、Factory 的使用
override fun onCreate(savedInstanceState: Bundle?) {
LayoutInflater.from(this).factory2 = object : LayoutInflater.Factory2{
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? {
if (TextUtils.equals(name, "TextView")) {
val btn = Button(this@MyActivity)
btn.text = "我是一个按钮"
return btn
}
return null
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return null
}
}
super.onCreate(savedInstanceState)
setContentView(R.layout.my_lactivity)
}
上面的代码就是 HOOK 技术的简单运用,我们利用自己创建的Factory2
,把控件 TextView 转换为控件 Button 。
2.1、在生命周期中创建
我们也可以onActivityCreated
中创建 Factory2
。动态换肤就是利用这一点。
我们看Application
内部的ActivityLifecycleCallbacks
的源码:
public interface ActivityLifecycleCallbacks {
default void onActivityPreCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { }
//监听 Activity 的创建
void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
default void onActivityPostCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {}
default void onActivityPreStarted(@NonNull Activity activity) { }
void onActivityStarted(@NonNull Activity activity);
default void onActivityPostStarted(@NonNull Activity activity) {}
default void onActivityPreResumed(@NonNull Activity activity) { }
void onActivityResumed(@NonNull Activity activity);
default void onActivityPostResumed(@NonNull Activity activity) { }
default void onActivityPrePaused(@NonNull Activity activity) { }
void onActivityPaused(@NonNull Activity activity);
default void onActivityPostPaused(@NonNull Activity activity) { }
default void onActivityPreStopped(@NonNull Activity activity) { }
void onActivityStopped(@NonNull Activity activity);
default void onActivityPostStopped(@NonNull Activity activity) { }
default void onActivityPreSaveInstanceState(@NonNull Activity activity,@NonNull Bundle outState) { }
void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
default void onActivityPostSaveInstanceState(@NonNull Activity activity,@NonNull Bundle outState) { }
default void onActivityPreDestroyed(@NonNull Activity activity) {}
void onActivityDestroyed(@NonNull Activity activity);
default void onActivityPostDestroyed(@NonNull Activity activity{}
}
我们可以实现接口 ActivityLifecycleCallbacks
来监听Activity 的生命周期。
Activity 的创建我们在Activity#onCreate
中执行监听
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
mFragments.dispatchCreate();
dispatchActivityCreated(savedInstanceState);
...
}
private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
getApplication().dispatchActivityCreated(this, savedInstanceState);
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i = 0; i < callbacks.length; i++) {
// 调用 onActivityCreated,进行创建监听
((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
savedInstanceState);
}
}
}
- 利用 map 存储监听事件
private final ArrayList mActivityLifecycleCallbacks =
new ArrayList();
- 注册监听
public void registerActivityLifecycleCallbacks(
@NonNull Application.ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
- 注销监听
public void unregisterActivityLifecycleCallbacks(
@NonNull Application.ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.remove(callback);
}
}
三、AssetManager 创建流程
首先我们执行ActivityThread#performLaunchActivity
方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
...
return activity;
}
执行ActivityThread#createBaseContextForActivity
方法
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId;
try {
displayId = ActivityTaskManager.getService().getDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
//创建 ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
r.overrideConfig);
r.mPendingFixedRotationAdjustments = null;
}
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
执行ContextImpl#createActivityContext
方法,创建 Context
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
//创建 ContextImpl, ContextImpl是 Context的实现类
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mIsUiContext = true;
context.mIsAssociatedWithDisplay = true;
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
// Create the base resources for which all configuration contexts for this Activity
// 设置 Resources 资源
context.setResources(resourcesManager.createBaseTokenResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader,
packageInfo.getApplication() == null ? null
: packageInfo.getApplication().getResources().getLoaders()));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
执行ContextImpl#createBaseTokenResources
方法
public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
@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 loaders) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createBaseActivityResources");
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
overlayDirs,
libDirs,
displayId,
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
if (DEBUG) {
Slog.d(TAG, "createBaseActivityResources activity=" + token
+ " with key=" + key);
}
synchronized (this) {
// Force the creation of an ActivityResourcesStruct.
getOrCreateActivityResourcesStructLocked(token);
}
// Update any existing Activity Resources references.
updateResourcesForActivity(token, overrideConfig, displayId,
false /* movedToDifferentDisplay */);
rebaseKeyForActivity(token, key);
synchronized (this) {
Resources resources = findResourcesForActivityLocked(token, key,
classLoader);
if (resources != null) {
return resources;
}
}
// Now request an actual Resources object.
return createResources(token, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
执行ContextImpl#createResources
方法创建Resources
private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
List resourcesLoader) {
final String[] splitResDirs;
final ClassLoader classLoader;
try {
splitResDirs = pi.getSplitPaths(splitName);
classLoader = pi.getSplitClassLoader(splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
//利用单例模式获取 Resources
return ResourcesManager.getInstance().getResources(activityToken,
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
pi.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfig,
compatInfo,
classLoader,
resourcesLoader);
}
我们看 ResourcesManager
里面的方法
//单例,懒汉式
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
if (sResourcesManager == null) {
sResourcesManager = new ResourcesManager();
}
return sResourcesManager;
}
}
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 loaders) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
//创建 key,根据key 获取 ResourcesImpl
final ResourcesKey key = new ResourcesKey(
resDir, //资源文件
splitResDirs,
overlayDirs,
libDirs,
displayId,
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
if (activityToken != null) {
rebaseKeyForActivity(activityToken, key);
}
//创建资源 Resources
return createResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
获取 Resources
资源
private @Nullable Resources createResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
synchronized (this) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
}
//根据 key 获取 ResourcesImpl
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
if (resourcesImpl == null) {
return null;
}
//创建 Resources
if (activityToken != null) {
return createResourcesForActivityLocked(activityToken, classLoader,
resourcesImpl, key.mCompatInfo);
} else {
return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
}
}
执行ResourcesManager#findOrCreateResourcesImplForKeyLocked
方法
private final ArrayMap> mResourceImpls =
new ArrayMap<>();
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key) {
//从缓存中获取 ResourcesImpl
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
//创建 ResourcesImpl
impl = createResourcesImpl(key);
if (impl != null) {
//加入缓存
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
执行ResourcesManager#createResourcesImpl
方法,,创建AssetManager
,并包装到 ResourcesImpl
中
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
//创建 AssetManager
final AssetManager assets = createAssetManager(key);
if (assets == null) {
return null;
}
final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
final Configuration config = generateConfig(key, dm);
//创建ResourcesImpl
final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
}
return impl;
}
通过Builder模式创建 AssetManager
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
final AssetManager.Builder builder = new AssetManager.Builder();
if (key.mResDir != null) {
try {
builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
false /*overlay*/) /*资源路径,默认APP下的资源*/);
} catch (IOException e) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
}
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
try {
builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
false /*overlay*/));
} catch (IOException e) {
Log.e(TAG, "failed to add split asset path " + splitResDir);
return null;
}
}
}
if (key.mLibDirs != null) {
for (final String libDir : key.mLibDirs) {
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
try {
builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
false /*overlay*/));
} catch (IOException e) {
Log.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
// continue.
}
}
}
}
if (key.mOverlayDirs != null) {
for (final String idmapPath : key.mOverlayDirs) {
try {
builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
true /*overlay*/));
} catch (IOException e) {
Log.w(TAG, "failed to add overlay path " + idmapPath);
// continue.
}
}
}
if (key.mLoaders != null) {
for (final ResourcesLoader loader : key.mLoaders) {
builder.addLoader(loader);
}
}
return builder.build();
}
执行ResourcesManager#loadApkAssets
方法创建 ApkAssets
包资源
private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
throws IOException {
final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
ApkAssets apkAssets = null;
if (mLoadedApkAssets != null) {
apkAssets = mLoadedApkAssets.get(newKey);
if (apkAssets != null && apkAssets.isUpToDate()) {
return apkAssets;
}
}
// Optimistically check if this ApkAssets exists somewhere else.
final WeakReference apkAssetsRef = mCachedApkAssets.get(newKey);
if (apkAssetsRef != null) {
apkAssets = apkAssetsRef.get();
if (apkAssets != null && apkAssets.isUpToDate()) {
if (mLoadedApkAssets != null) {
mLoadedApkAssets.put(newKey, apkAssets);
}
return apkAssets;
} else {
// Clean up the reference.
mCachedApkAssets.remove(newKey);
}
}
// We must load this from disk.
if (overlay) {
apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/);
} else {
apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
if (mLoadedApkAssets != null) {
mLoadedApkAssets.put(newKey, apkAssets);
}
mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
return apkAssets;
}
最后我们看下createResourcesLocked
方法,可见 Resources 里面维护 ResourcesImpl
private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
cleanupReferences(mResourceReferences, mResourcesReferencesQueue);
//创建 Resources
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
//把 ResourcesImpl 设置到 Resources 里面,进行维护
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
mResourceReferences.add(new WeakReference<>(resources, mResourcesReferencesQueue));
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
}
return resources;
}
四、资源加载流程
Resouce
类可以说成是一个中介类,他并没做什么实质性的东西,大部分的操作被他转到了ResourcesImpl
中,ResourcesImpl
中管理着AssetManager
、DisplayMetrics
、Configuration
这三个对象,这三个对象我们应该或多或少的都了解过吧,由于篇幅有限这里就不讲他们的作用了,被Resouce转到ResourcesImpl的操作又根据具体类型被转到这三个对象中了。 我们通常通过Resouce去获取颜色、字符串、图片等资源文件最终其实都是调用AssetManager的方法执行的。
- 我们以
Resources#getString()
讲解
public String getString(@StringRes int id) throws NotFoundException {
return getText(id).toString();
}
@NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
throw new NotFoundException("String resource ID #0x"
+ Integer.toHexString(id));
}
通过getAssets()
个以获得AssetManager
对象,然后在AssetManager
中执行 getResourceText
根据资源 id 获取资源
@Nullable CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
//获取资源
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
Objects.requireNonNull(outValue, "outValue");
synchronized (this) {
ensureValidLocked();
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) {
//从ApkAssets中获取资源
outValue.string = getPooledStringForCookie(cookie, outValue.data);
}
return true;
}
}
根据Native方法,从C++ 方法原始的资源
// Primitive resource native methods.
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
@NonNull TypedValue outValue, boolean resolveReferences);
从ApkAssets
中获取资源
CharSequence getPooledStringForCookie(int cookie, int id) {
// Cookies map to ApkAssets starting at 1.
//根据 id 获取资源
return getApkAssets()[cookie - 1].getStringFromPool(id);
}
在 ApkAssets
中的具体步骤就不做跟踪了。
从上面的资源加载流程可得,Resources 类也是通过 AssetManager 类来访问那些被编译过的应用程序资源文件的,不过在访问之前,它会先根据资源 ID 查找得到对应的资源文件名。 而 AssetManager 对象既可以通过文件名访问那些被编译过的,也可以访问没有被编译过的应用程序资源文件。
- 根据名字获取资源包的资源
// app原始的resource
private Resources mAppResources;
// 皮肤包的resource
private Resources mSkinResources;
/**
* 1.通过原始app中的resId(R.color.XX)获取到自己的 名字
* 2.根据名字和类型获取皮肤包中的ID
*/
public int getIdentifier(int resId) {
if (isDefaultSkin) {
return resId;
}
//通过原始app中的resId,找到 resName
String resName = mAppResources.getResourceEntryName(resId);
//通过原始app中的resId,找到类型 layout\drawable
String resType = mAppResources.getResourceTypeName(resId);
//获取皮肤包对应的资源 ID
int skinId = mSkinResources.getIdentifier(resName, resType, mSkinPkgName);
return skinId;
}