一、什么是Context类
Context 类直译为“上下文”
1、它描述一个应用程序环境的信息。
2、该类是一个抽象类,Android提供了该抽象类的具体实现类(ContextImpl类)
3、通过它可以获取应用程序的资源和类,也包括一些应用级别操作。如,启动一个Activity,发送广播、接收Intent信息等。
二、Context类的继承关系如下:
1、ContextWrapper是一个Context的一个包装类,它内部持有一个ContextImpl类的实例mBase,ContextWrapper中所有Context中方法的实现都是通过mBase来实现的,类似于代理模块式。
public class ContextWrapper extends Context {
Context mBase; //该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值
//创建Application、Service、Activity,会调用该方法给mBase属性赋值
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent); //调用mBase实例方法
}
}
2、ContextImp是Context真正实现类。
class ContextImpl extends Context{
//所有Application程序公用一个mPackageInfo对象
/*package*/ ActivityThread.PackageInfo mPackageInfo;
@Override
public Object getSystemService(String name){
...
else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
}
else if (INPUT_METHOD_SERVICE.equals(name)) {
return InputMethodManager.getInstance(this);
}
}
@Override
public void startActivity(Intent intent) {
...
//开始启动一个Activity
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
}
}
3、由上面的集成关系可知:
Application、Activity、Service都直接或间接继承自ContextWrapper,其内部都持有一个ContextImp对象mBase,Application、Activity、Service中获取资源,startActivity等操作,都是通过ContextImp来实现的。
4、Activity类直接继承自 ContextThemeWrapper 类。
/frameworks/base/core/java/android/view/ContextThemeWrapper.java
该类内部包含了主题(Theme)先关的接口,即android:theme属性指定的。只有Activity需要主题。
public class ContextThemeWrapper extends ContextWrapper {
//该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值
private Context mBase;
//mBase赋值方式同样有一下两种
public ContextThemeWrapper(Context base, int themeres) {
super(base);
mBase = base;
mThemeResource = themeres;
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
mBase = newBase;
}
}
5、ContentImpl何时初始化?
Android中创建ContextImpl的地方有多处:
- PackageInfo.makeApplication()
- hanldBinderAppplication()
- performLaunchActivity()
- handleCreateService()
以Application 对应的Context 为例
应用启动时 会AMS 向ActivityThread发送消息,最终调用hanldBinderAppplication()方法。
private final void handleBindApplication(AppBindData data) {
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
....
data.info = getPackageInfoNoCheck(data.appInfo);
...
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
....
}
data.info是LoadedApk类型的。
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {
return getPackageInfo(ai, null, false, true);
}
里面其实调用的是getPackageInfo
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
packageInfo =
new LoadedApk(this, aInfo, this, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
mPackages是ActivityThread中的属性,首先尝试从mPackages中获取,如果没有,则new一个出来,并放入mPackages里面去。
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
loadedApk 中makeApplication()方法中
- ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); 创建了一个ContextImpl对象
- mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext); 利用Instrumentation创建了Application对象,并将ContextImp 作为参数传递给了newApplication()
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
}
try {
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
return app;
}
我们再看Instrumentation中的newApplication()方法,完成了俄两件事:
- 调用instantiateApplication创建了Applciation类
- 调用app.attach(context);将ContextImp传递给applciation类
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
再看instantiateApplication方法,其实就是利用反射构造了一个Application实例:
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Application) cl.loadClass(className).newInstance();
}
Application类中attach方法,调用attachBaseContext()方法,将ContentImp传递给了Application类中的mBase
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
至此 启动应用过程中,完成了ContextImpl的创建、Application的创建,以及ContextImpl通过attachBaseContext()传递到了Application的mBase属性中。
注意:
- Aplication、Service、Activity都属于Context类。一个应用中Context的数量 等于Activity的个数+Service的个数+1(Applciation类对应的Context实例)
- ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的所有ContextIml实例,都对应同一个packageInfo对象。
三、Resource类
1、ContextImpl中Resouce类的来源:
ContextImpl中通过createAppContext()创建ContextImpl。可知ContextImpl中的Resources是从LoadedApk中获取的。
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null);
context.setResources(packageInfo.getResources());
return context;
}
我们再看LoadedApk中的getResources()方法
//LoadedApk类
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, this);
}
return mResources;
}
2、Resouce类内部
public class Resources {
public CharSequence getText(@StringRes int id) throws NotFoundException {
CharSequence res = mAssets.getResourceText(id);
//......
}
public String[] getStringArray(@ArrayRes int id)
throws NotFoundException {
String[] res = mAssets.getResourceStringArray(id);
//......
}
}
Resources中的getText,getStringArray等方法获取资源类似,都会代理到,都会调用mAssets类的getResourcexxx方法。mAssets是一个AssetManager。
mAssets是一个AssetManager对象是从Resource构造函数中赋值的。
/**
* Create a new Resources object on top of an existing set of assets in an
* AssetManager.
*
* @param assets Previously created AssetManager.
* @param metrics Current display metrics to consider when
* selecting/computing resource values.
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
}
/**
* Creates a new Resources object with CompatibilityInfo.
*
* @param assets Previously created AssetManager.
* @param metrics Current display metrics to consider when
* selecting/computing resource values.
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
* @param compatInfo this resource's compatibility info. Must not be null.
* @hide
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
CompatibilityInfo compatInfo) {
mAssets = assets;
mMetrics.setToDefaults();
if (compatInfo != null) {
mCompatibilityInfo = compatInfo;
}
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
}
- AssetManager有一个关键方法 addAssetPath,可以把额外的apk或目录的资源加入到AssetManager实例中。
- 并且额外的一个关键点,AssetManager是一个单例。
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path);
makeStringBlocks(mStringBlocks);
return res;
}
}
因此 如何实现插件化中的Resource资源共享?
1、方案一:我们如果AssetManager单例加入插件的资源或宿主的资源,并Hook替换掉原来的AssetManager就可以实现 插件化当中 宿主与插件Resource资源的共享。
2、方案二:Hook掉LoadedApk中的mResouce,替换成我么你自己的Resouce,也可以实现插件和宿主资源文件的合并共享。
四、参考文章:
https://segmentfault.com/a/1190000013048236
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1223/2205.html