Android开发:基于Package Visibility实现App B无感加载App A的库
分步实现流程:基于Package Visibility的跨App无感知库加载
一、配置App A(库宿主)
- 确定App A的包名(例如
com.example.libraryhost),将需要共享的库(自定义工具类、第三方SDK、原生so等)放入APK的assets、res/raw或libs目录,保证APK可正常安装。 - (可选)在
AndroidManifest.xml中声明自定义权限,限制只有授权App能访问:<permission android:name="com.example.libraryhost.PERMISSION_ACCESS_LIBRARY" android:protectionLevel="normal" />
二、配置App B的Package Visibility
- 在App B的
AndroidManifest.xml中添加包可见性声明,让系统允许B获取App A的安装信息:<queries> <package android:name="com.example.libraryhost" /> </queries> - (可选)如果App A声明了自定义权限,在App B中添加权限引用:
<uses-permission android:name="com.example.libraryhost.PERMISSION_ACCESS_LIBRARY" />
三、App B中动态加载App A的库
1. 获取App A的APK路径
通过PackageManager获取App A的安装路径:
PackageManager pm = getPackageManager(); try { ApplicationInfo appInfo = pm.getApplicationInfo("com.example.libraryhost", 0); String apkPath = appInfo.sourceDir; // App A的APK绝对路径 } catch (PackageManager.NameNotFoundException e) { // 处理App A未安装的异常,比如静默引导安装或提示用户 e.printStackTrace(); }
2. 加载Dex类库
使用DexClassLoader加载APK中的dex文件,需指定App B的私有缓存目录用于解压dex:
File dexCacheDir = getCacheDir(); DexClassLoader dexLoader = new DexClassLoader( apkPath, dexCacheDir.getAbsolutePath(), null, // 若有so库,可指定so目录路径 getClassLoader() // 父类加载器,保证类加载优先级 ); // 加载并调用目标类 try { Class<?> targetClass = dexLoader.loadClass("com.example.libraryhost.utils.MyUtils"); Object instance = targetClass.getDeclaredConstructor().newInstance(); Method method = targetClass.getMethod("doBusinessLogic"); method.invoke(instance); } catch (Exception e) { e.printStackTrace(); }
3. 加载原生SO库
若App A包含so库,需先将so从APK解压到App B私有目录,再加载:
// 解压APK中的so文件 ZipFile zipFile = new ZipFile(apkPath); ZipEntry soEntry = zipFile.getEntry("lib/armeabi-v7a/libmylib.so"); if (soEntry != null) { InputStream is = zipFile.getInputStream(soEntry); File soDestFile = new File(getDir("libs", MODE_PRIVATE), "libmylib.so"); FileOutputStream os = new FileOutputStream(soDestFile); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } os.close(); is.close(); zipFile.close(); // 加载so库 System.load(soDestFile.getAbsolutePath()); }
四、实现无感知体验优化
- 启动检查:App B启动时先判断App A是否安装,未安装则静默触发安装(需申请安装权限),避免用户感知。
- 缓存复用:第一次加载后,将解压的dex/so缓存到私有目录,后续启动直接读取缓存,减少耗时。
- API封装:把跨App加载逻辑封装成单例工具类,对外暴露和App B本地库一致的接口,业务代码无需感知库来源。
五、关键注意事项
- 版本兼容:确保App A和App B的minSdkVersion匹配,处理不同Android版本的类加载差异。
- 权限控制:所有文件操作使用App B私有目录,避免权限错误。
- 混淆配置:App A中需要被加载的类,要在混淆规则中排除,防止混淆后无法反射调用。
- 签名校验:让App A和App B使用同一签名,代码中验证签名,防止恶意App冒充库宿主。
内容的提问来源于stack exchange,提问作者Muthyam Sriram
相关产品推荐
相关产品推荐

