1 插件化的出现主要是由65535问题的出现。用于查分多个dex并动态加载dex来防止65535问题
2插件化其实就是动态加载,动态加载又包括了代码加载和资源加载。
3 热修复也是动态加载。
插件化中动态加载必须解决 代码及资源动态加载,还有生命周期的问题。
由Activity启动原理 可以知道 代码的加载需要加载dex文件,要用到类PassClasslLoader及DexClassLoader。而资源的加载需要用到AssetManager及隐藏方法addAsssetPath。
具体步骤 这些步骤需要到Application 中的attachBaseContext中执行。
1 合并宿主dex文件及插件dex文件
public static void inject(DexClassLoader loader){ //拿到本应用的ClassLoader PathClassLoader pathLoader = (PathClassLoader) MyApplication.getContext().getClassLoader(); try { //获取宿主pathList Object suZhuPathList = getPathList(pathLoader); Object chaJianPathList = getPathList(loader); Object dexElements = combineArray( //获取本应用ClassLoader中的dex数组 getDexElements(suZhuPathList), //获取插件CassLoader中的dex数组 getDexElements(chaJianPathList)); setField(suZhuPathList, suZhuPathList.getClass(), "dexElements", dexElements); } catch (Exception e) { e.printStackTrace(); } }
2 运用反射及Proxy 拦截IActivityManger方法StartActivity方法,替换Intent中的TargetActivity为占坑Activity。绕开Android系统校验。
public static void hookActivityManagerNative() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { //获取ActivityManagerNative的类 Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); //拿到gDefault字段 Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); //从gDefault字段中取出这个对象的值 Object gDefault = gDefaultField.get(null); // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段 Class<?> singleton = Class.forName("android.util.Singleton"); //这个gDefault是一个Singleton类型的,我们需要从Singleton中再取出这个单例的AMS代理 Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); //ams的代理对象 Object rawIActivityManager = mInstanceField.get(gDefault); // 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活,这里我们使用动态代理 Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager)); mInstanceField.set(gDefault, proxy); }
3 生成一个Handler.Callback 的继承类 并重写其handMessage方法 把占坑Activity换会之前的TargetActivity。
4 资源文件的加载 要知道插件在使用资源时是默认使用宿主资源 这样就会出现报错。 只要能提供给插件资源就可以了。
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/chajian_demo.apk"; assetManager = AssetManager.class.newInstance(); Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class); addAssetPathMethod.setAccessible(true); addAssetPathMethod.invoke(assetManager, apkPath); Method ensureStringBlocks = AssetManager.class.getDeclaredMethod("ensureStringBlocks"); ensureStringBlocks.setAccessible(true); ensureStringBlocks.invoke(assetManager); Resources supResource = getResources(); Log.e("Main", "supResource = " + supResource); newResource = new Resources(assetManager, supResource.getDisplayMetrics(), supResource.getConfiguration()); Log.e("Main", "设置 getResource = " + getResources()); mTheme = newResource.newTheme(); mTheme.setTo(super.getTheme());
宿主Application提供获取插件资源的方法 供插件调用。
@Override public AssetManager getAssets() { return assetManager == null ? super.getAssets() : assetManager; } @Override public Resources getResources() { return newResource == null ? super.getResources() : newResource; }
在插件的Activity中需要重写 getResources()及getAssets();
@Override public Resources getResources() { if(getApplication() != null && getApplication().getResources() != null){ return getApplication().getResources(); } return super.getResources(); } @Override public AssetManager getAssets() { if(getApplication() != null && getApplication().getAssets() != null){ return getApplication().getAssets(); } return super.getAssets(); }