转载请注明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_)
【Android插件化探索(一)类加载器DexClassLoader】
【Android插件化探索(二)资源加载】
前情提要
在上一篇中有一个细节没有提到,那就是getResourcesForApplication和AssetManager的区别。
getResourcesForApplication
getResourcesForApplication(String packageName),很显然需要传入一个包名,换言之,这个插件必须已经被安装在系统内,然后才能通过包名来获取资源。你可能会想,不安装照样可以获取包名啊。的确,通过pm.getPackageArchiveInfo()
可以获取安装包信息。但是,这些包都是没有在PMS中注册的。如果仍然这样获取,会提示如下错误。
android.content.pm.PackageManager$NameNotFoundException: com.maplejaw.hotplugin
现在我们就从源码角度来分析getResourcesForApplication。源码在ApplicationPackageManager中,如下:
@Override
public Resources getResourcesForApplication(String appPackageName)
throws NameNotFoundException {
return getResourcesForApplication(
getApplicationInfo(appPackageName, sDefaultFlags));
}
可以看出内部调用了重载方法。getApplicationInfo返回的是ApplicationInfo对象。
@Override
public Resources getResourcesForApplication(@NonNull ApplicationInfo app){
//...
//省略了部分源码
final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
null, mContext.mPackageInfo);
if (r != null) {
return r;
}
}
最终走的仍旧是ActivityThread的getTopLevelResources,ActivityThread里面的相关源码我就不分析了,跟上一篇是一样的也是调用ResourcesManager中的getTopLevelResources,这里不做赘述。
现在我们主要来看看getApplicationInfo里面做了什么?
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException {
ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, mContext.getUserId());
//...
//省略了部分源码
throw new NameNotFoundException(packageName);
}
mPM的初始化源码如下,可以看出是一个PMS(PackageManagerService)对象。
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
继续深究,找出PMS中相关源码。
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return