插件化方式(hook)

主要原理:将插件的dex和宿主的dex融为一体
这里涉及到连个classloader,DexClassLoader和PathClassLoader他们都继承了BaseDexClassLoader,
DexClassLoader可以加载任何目录的dex,PathClassLoader只能加载系统安装的dex.
dex在内存中的表现形式为Element,在BasedexClassLoader下有个成员变量pathList,
PathList类中有个成员变量dexElements,是Element的集合。也就是所有的dex在内存中会被加载成Element集合,于是我们可以将插件中的dexElements 融合到系统的dexElents数组。

public class HookUtil {
  public void injectPluginClass() {
        String cachePath = context.getCacheDir().getAbsolutePath();
        String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";
        DexClassLoader dexClassLoader = new DexClassLoader(apkPath, cachePath, cachePath, context.getClassLoader());

      //     第一步   找到    插件的Elements数组  dexPathlist  ----?dexElement

        try {
            Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
            Field  myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
            myPathListFiled.setAccessible(true);
            Object myPathListObject =myPathListFiled.get(dexClassLoader);


            Class  myPathClazz=myPathListObject.getClass();
            Field  myElementsField = myPathClazz.getDeclaredField("dexElements");
            myElementsField.setAccessible(true);
//          自己插件的  dexElements[]
            Object myElements=myElementsField.get(myPathListObject);

            //     第二步   找到    系统的Elements数组    dexElements
            PathClassLoader pathClassLoader= (PathClassLoader) context.getClassLoader();
            Class baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
            Field pathListFiled = baseDexClazzLoader.getDeclaredField("pathList");
            pathListFiled.setAccessible(true);
            Object pathListObject = pathListFiled.get(pathClassLoader);

            Class systemPathClazz = pathListObject.getClass();
            Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
            systemElementsField.setAccessible(true);
            //系统的  dexElements[]
            Object systemElements = systemElementsField.get(pathListObject);
            //     第三步  上面的dexElements  数组  合并成新的  dexElements     然后通过反射重新注入系统的Field (dexElements )变量中

//       新的     Element[] 对象
//            dalvik.system.Element

            int systemLength = Array.getLength(systemElements);
            int myLength = Array.getLength(myElements);
//            找到 Element  的Class类型   数组    每一个成员的类型
            Class sigleElementClazz = systemElements.getClass().getComponentType();
            int newSysteLength = myLength + systemLength;
            Object newElementsArray=Array.newInstance(sigleElementClazz, newSysteLength);
//融合
            for (int i = 0; i < newSysteLength; i++) {
//                先融合 插件的Elements
                if (i < myLength) {
                    Array.set(newElementsArray, i, Array.get(myElements, i));
                }else {
                    Array.set(newElementsArray,i,Array.get(systemElements,i-myLength));
                }
            }
            Field  elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
            elementsField.setAccessible(true);
//            将新生成的EleMents数组对象重新放到系统中去
            elementsField.set( pathListObject,newElementsArray);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

application中注册hook,并重写Resources 和AssetManager

public class MyApplication extends Application {
    private static Context sContext;
    private static final String TAG = "xxx";
    private AssetManager assetManager;
    private Resources newResource;
    private Resources.Theme mTheme;
    @Override
    public void onCreate() {
        super.onCreate();
        HookUtil hookUtil = new HookUtil();
        hookUtil.hookStartActivity(this);
        hookUtil.hookHookMh(this);
        hookUtil.injectPluginClass();
//        重构
        String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";


        try {
            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);
//            插件的StringBloac被实例化了
            Resources supResource = getResources();
            newResource = new Resources(assetManager, supResource.getDisplayMetrics(), supResource.getConfiguration());
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public AssetManager getAssetManager() {
        return assetManager==null?super.getAssets():assetManager;
    }

    @Override
    public Resources getResources() {
        return newResource==null?super.getResources():newResource;
    }
    //    resource
}

在插件中创建baseActivity 获取新的AssetManager和Resources

public class BaseActivity extends Activity{

    @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();
    }
//    @Override
//    public Resources.Theme getTheme() {
//        if(getApplication() != null && getApplication().getTheme() != null){
//            return getApplication().getTheme();
//        }
//        return super.getTheme();
//    }
}

你可能感兴趣的:(插件化方式(hook))