插件化加载未安装APK

来源:互联网 发布:中药的软件 编辑:程序博客网 时间:2024/05/21 19:36

主程序专门开一个PluginActivity,在该Activity里通过dexClassLoader动态加载apk,通过反射的方式向被加载apk的mainActivity里传递PluginAvtivity这个参数,相当于在主程序的PluginActivity里画Apk中Activity的View。

PluginActivity中的核心方法:loadApk


DexClassLoader这个类加载器用来从.jar和.apk类型的文件内部加载classes.dex文件。通过这种方式可以用来执行非安装的程序代码,作为程序的一部分进行运行。这个装载类需要一个程序私有的,可写的文件目录去存放优化后的classes文件。通过Contexct.getDir(String, int)来创建这个目录:File dexOutputDir = context.getDir("dex", 0);

public File getDir(String name, int mode):name目录名称、mode权限,如果传入的目录不存在,系统会创建此目录,路径为"/data/data/程序Package Name/app_name",name就是传入的name。

通过以下代码来获得DexClassLoader。dexPath是需要装载的APK的路径

DexClassLoader localDexClassLoader = new DexClassLoader(dexpath, dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader().getParent());

通过以下代码获得应用信息

 PackageInfo pkgInfo = getPackageManager().getPackageArchiveInfo(dexpath,
                PackageManager.GET_ACTIVITIES);


   if ((pkgInfo.activities != null) && (pkgInfo.activities.length > 0)) {                  String activityname = pkgInfo.activities[0].name; //获得应用信息里的第一个Activity名                   localClass = localDexClassLoader.loadClass(activityname);//通过反射加载类型                mActivityClass = localClass;                  Constructor localConstructor = localClass.getConstructor(new Class[] {});//获得构造方法                  instance = localConstructor.newInstance(new Object[] {});  //创建对象实例                mActivityInstance = instance;                                      Method localMethodSetActivity = localClass.getDeclaredMethod("setActivity", new Class[] { Activity.class });                  localMethodSetActivity.setAccessible(true);                  localMethodSetActivity.invoke(instance, new Object[] { this });  //相当于调用MainActivity里的setActivity方法,把PluginActivity传递了进去

此外,还需要获取未安装apk的资源——getApkResoures方法

反射调用android.content.res.AssetManager类,新建个实例,调用隐藏的方法addAssetPath(String path)将为安装APK文件的添加进去,然后用这个AssetManager来构建出一个Resource实例


try {    Class<?> class_AssetManager = Class.forName("android.content.res.AssetManager");    Object assetMag = class_AssetManager.newInstance();    Method method_addAssetPath = class_AssetManager            .getDeclaredMethod("addAssetPath", String.class);    String path = 路径;    String fileName = 文件名;    method_addAssetPath.invoke(assetMag, path + fileName);    Resources res = context.getResources();    Constructor<?> constructor_Resources = Resources.class            .getConstructor(class_AssetManager, res.getDisplayMetrics()            .getClass(), res.getConfiguration().getClass());    res = (Resources) constructor_Resources.newInstance(assetMag,            res.getDisplayMetrics(), res.getConfiguration());} catch (Exception e) {    e.printStackTrace();}

在MainActivity里设一个setResources方法,反射调用它,把getApkResoures方法返回的Resource实例传递进去。


最后反射调用MainActivity的onCreate方法


Method methodonCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });  
                 methodonCreate.setAccessible(true);  
                 methodonCreate.invoke(instance, savedInstanceState); 


二,如何更换应用主题时同时同步到插件里去?

插件加载资源时先判断Activity的资源里有没有它,有就调用,没有再调用自己的Resource

0 0