Bleoo

志同道合,一杯酒。

  • 主页
  • 随笔
  • Android

Bleoo

志同道合,一杯酒。

  • 主页
  • 随笔
  • Android

Android动态加载Apk资源

2017-04-28

通过DexClassLoader加载未安装的apk,提供资源供宿主app使用。因为apk是未安装的,不可能通过createPackageContext(…)方法来构建出一个context,所以这时只有在Resource上下功夫,来获取插件apk的Resource对象。

综述

关于动态加载未安装的apk

  • 获取插件apk存放在哪个目录下,然后分别得到插件apk的信息(名称、包名等),然后显示可用的插件,最后动态加载apk获得资源。

实验步骤

1.先准备一个测试工程

2.在工程的mipmap目录下放入one.png

3.打包工程build apk,改名为test.apk

4.adb push test.apk的文件路径 /sdcard/

5.准备动态加载工程

6.获取test.apk的包信息

1
2
3
4
5
6
7
8
9
10
11
12
13
private String getUninstallApkInfo(Context context, String archiveFilePath) {
PackageManager pm = context.getPackageManager();
PackageInfo pkgInfo = pm.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
if (pkgInfo != null) {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
String versionName = pkgInfo.versionName;//版本号
Drawable icon = pm.getApplicationIcon(appInfo);//图标
String appName = pm.getApplicationLabel(appInfo).toString();//app名称
String pkgName = appInfo.packageName;//包名
return pkgName;
}
return null;
}

7.利用反射得到目标Resources对象

1
2
3
4
5
6
7
8
9
10
11
12
13
private Resources getPluginResources(String apkName) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);//反射调用方法addAssetPath(String path)
addAssetPath.invoke(assetManager, apkDir + File.separator + apkName);//将未安装的Apk文件的添加进AssetManager中,第二个参数为apk文件的路径带apk名
Resources superRes = this.getResources();
Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
return mResources;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

8.动态加载apk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void dynamicLoadApk(String apkDir, String apkName, String apkPackageName) throws Exception {
// 第一种方法动态加载apk,通过反射获取resId
File optimizedDirectoryFile = getDir("dex", Context.MODE_PRIVATE);//在应用安装目录下创建一个名为app_dex文件夹目录,如果已经存在则不创建
//参数:1、包含dex的apk文件或jar文件的路径,2、apk、jar解压缩生成dex存储的目录,3、本地library库目录,一般为null,4、父ClassLoader
DexClassLoader dexClassLoader = new DexClassLoader(apkDir+File.separator+apkName, optimizedDirectoryFile.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
//通过使用apk自己的类加载器,反射出R类中相应的内部类进而获取我们需要的资源id
Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$mipmap");
Field field = clazz.getDeclaredField("one");//得到名为one的这张图片字段
int resId = field.getInt(R.id.class);//得到图片id
Resources mResources = getPluginResources(apkName);//得到插件apk中的Resource
// 第二种方法 获取resId
//int resId = mResources.getIdentifier("one", "mipmap", apkPackageName);
if (mResources != null) {
//通过插件apk中的Resource得到resId对应的资源
findViewById(R.id.iv_pic).setBackground(mResources.getDrawable(resId));
}
}

注意

此处需要注意DexClassLoader的四个参数:

  • dexPath:待加载的dex文件路径,如果是外存路径,一定要加上读外存文件的权限(<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 6.0应注意动态申请权限 )。
  • optimizedDirectory:解压后的dex存放位置,此位置一定要是可读写且仅该应用可读写(安全性考虑),所以只能放在data/data下。本文getDir(“dex1”, 0)会在/data/data/**package/下创建一个名叫”app_dex1“的文件夹,其内存放的文件是自动生成output.dex。
  • libraryPath:指向包含本地库(so)的文件夹路径,可以设为null。
  • parent:父级类加载器,一般可以通过Context.getClassLoader获取到,也可以通过ClassLoader.getSystemClassLoader()取到。
  • Android

扫一扫,分享到微信

微信分享二维码
Android 共享元素动画的简单实现
Android动态加载Dex文件入门
© 2017 Bleoo
Hexo Theme Yilia by Litten