主程序专门开一个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传递了进去
反射调用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的onCreate方法
Method methodonCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });
methodonCreate.setAccessible(true);
methodonCreate.invoke(instance, savedInstanceState);
二,如何更换应用主题时同时同步到插件里去?
插件加载资源时先判断Activity的资源里有没有它,有就调用,没有再调用自己的Resource