其他
AndroidManifest中uses-library怎么起作用的?
The following article is from 牛晓伟 Author 牛晓伟
本文摘要
本文大纲
1.1 使用共享库
<uses-library android:name="com.google.android.maps"
android:required="false" />
<uses-library android:name="com.here.android"
android:required="false" />
1.2 声明共享库
<library android:name="android.ext.shared" />
1.3 特殊的共享库
首先不需要配置就可以直接使用framework.jar中的类,大家可以仔细想想在开发App的时候是不是没有在AndroidManifest.xml文件中使用uses-library标签来引入该库,但是却可以使用它包含的类。这要得益于zygote进程的预加载机制,zygote进程会把framework.jar中的类提前预加载到ClassLoader,这样App进程被fork成功后,App进程与zygote进程共用一个ClassLoader。 其次framework.jar中的各种类被加载后在所有进程中都共享一份内存,这也得益于zygote进程的预加载机制。而其他共享库可不是共享一份内存。 最后framework.jar是最大的共享库,它包含的类可是最多最多的。
1.4 小结
2.1 管理共享库
2.1.1 内置共享库
2.1.2 声明的共享库
2.1.3 如何存储共享库
//SharedLibrariesImpl类
//mSharedLibraries存储了所有的共享库,key值是共享库的名字,value是共享库的信息
private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibraries;
2.1.4 管理共享库
2.2 提供共享库服务
<uses-library android:name="com.google.android.maps"
android:required="false" />
<uses-library android:name="com.here.android"
android:required="false" />
2.3 小结
共享库模块是PackageManagerService服务的一个模块,它的主要工作是管理共享库和提供共享库服务,管理共享库也就是会把内置共享库、声明的共享库、静态共享库都收集起来进行管理。提供共享库服务也就是为共享库使用者提供共享库的查询服务。
3.1 App进程请求ApplicationInfo信息
3.2 ActivityManagerService返回ApplicationInfo信息
PackageInfoUtils类
public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
@PackageManager.ApplicationInfoFlagsBits long flags,
@NonNull PackageUserStateInternal state, @UserIdInt int userId,
@Nullable PackageStateInternal pkgSetting) {
省略代码······
ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(pkg,
flags, state, userId, false /* assignUserFields */);
initForUser(info, pkg, userId);
if (pkgSetting != null) {
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
info.hiddenUntilInstalled = pkgState.isHiddenUntilInstalled();
//pkgState.getUsesLibraryFiles()和pkgState.getUsesLibraryInfos()获取的是共享库的信息,而这些信息在扫描完毕apk后就有值了
List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
List<SharedLibraryInfo> usesLibraryInfos = pkgState.getUsesLibraryInfos();
//存储共享库信息则
info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
? null : usesLibraryFiles.toArray(new String[0]);
info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
省略代码······
}
省略代码······
return info;
}
3.3 App进程拿到ApplicationInfo信息
//PathClassLoader 类
public class PathClassLoader extends BaseDexClassLoader {
}
public class BaseDexClassLoader extends ClassLoader {
//共享库相关的ClassLoader
protected final ClassLoader[] sharedLibraryLoaders;
protected final ClassLoader[] sharedLibraryLoadersAfter;
protected Class<?> findClass(String name) throws ClassNotFoundException {
//查找某个类时,先从共享库对应的ClassLoader中查找,找到则返回,否则从当前apk中查找
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
}
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c != null) {
return c;
}
// Now, check whether the class is present in the "after" shared libraries.
if (sharedLibraryLoadersAfter != null) {
for (ClassLoader loader : sharedLibraryLoadersAfter) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
}
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
}
3.4 小结
共享库模块会根据共享库名字、版本信息等查询到对应的共享库信息,并且把共享库信息交给对应的Apk (其实保存在PackageSetting对象)。 当App进程启动后,会从ActivityManagerService拿到对应的ApplicationInfo信息,而ApplicationInfo存储了apk路径、包名、apk版本号、共享库信息等等非常关键的信息。 App进程会把ApplicationInfo中的apk路径、native lib路径、共享库文件路径等信息交给PathClassLoader,PathClassLoader的父类BaseDexClassLoader会持有关于共享库相关的ClassLoader。 在查找某个类时,会先从共享库对应的ClassLoader查找,找到了返回;否则从BaseDexClassLoader中查找。
用一句简单的话总结就是:共享库文件会被加载到App进程的PathClassLoader中,这样App进程就可以找到共享库的类了。从此也可以看出除framework.jar外的共享库是在每个App进程都占用自己的内存空间,没有共享内存。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!