美文网首页
插件化(按需加载(loadApk))

插件化(按需加载(loadApk))

作者: 爬行的蚂蚁2725 | 来源:发表于2019-02-20 15:12 被阅读0次

    在应用启动后通过融合插件dex的方式实现插件化的弊端:当插件太多的时候回造成app卡顿,用户体验不好,这时候可以采用按需加载插件的方式实现。
    app在内存中的表现形式是LoadApk对象。
    当调用ActivityThread的scheduleLaunchActivity方法会通过handler发送一个LAUNCH_ACTIVITY消息,handler在handlemessage中处理这个消息时会调用getPackageInfoNoCheck生成一个LoadApk,ActivityThread继续调用getPackageInfo 检查目前操作的是不是同一个用户和之前实例化没有new LoadApk。

    LoadApk在ActivityThread中的存在形式:ArrayMap<String, WeakReference<LoadedApk>> mPackages
    其中key为包名,我们只需要将插件对应的loadApk 添加到这个mPackages,加载的使用使用对应的dexclassLoader加载即可

    public class HookUtil {
    
        private Context context;
    
        public  void hookHookMh(Context context  ) {
            try {
                Class<?> forName = Class.forName("android.app.ActivityThread");
                Field currentActivityThreadField = forName.getDeclaredField("sCurrentActivityThread");
                currentActivityThreadField.setAccessible(true);
    //            还原系统的ActivityTread   mH
                Object activityThreadObj=currentActivityThreadField.get(null);
    
                Field handlerField = forName.getDeclaredField("mH");
                handlerField.setAccessible(true);
    //            hook点找到了
                Handler mH= (Handler) handlerField.get(activityThreadObj);
                Field callbackField = Handler.class.getDeclaredField("mCallback");
    
                callbackField.setAccessible(true);
    
                callbackField.set(mH,new ActivityMH(mH));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    
        public  void hookStartActivity(Context context) {
    //        还原 gDefault 成员变量  反射  调用一次
            this.context = context;
            try {
                Class<?> ActivityManagerNativecls=Class.forName("android.app.ActivityManagerNative");
                Field gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
                gDefault.setAccessible(true);
    //            因为是静态变量  所以获取的到的是系统值  hook   伪hook
                Object defaltValue=gDefault.get(null);
                //mInstance对象
                Class<?> SingletonClass=Class.forName("android.util.Singleton");
    
                Field mInstance = SingletonClass.getDeclaredField("mInstance");
    //        还原 IactivityManager对象  系统对象
                mInstance.setAccessible(true);
                Object iActivityManagerObject=mInstance.get(defaltValue);
                Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
                startActivty startActivtyMethod = new startActivty(iActivityManagerObject);
    //            第二参数  是即将返回的对象 需要实现那些接口
                Object oldIactivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
                        , new Class[]{IActivityManagerIntercept, View.OnClickListener.class}
                        , startActivtyMethod);
    //            将系统的iActivityManager  替换成    自己通过动态代理实现的对象   oldIactivityManager对象  实现了 IActivityManager这个接口的所有方法
                mInstance.set(defaltValue, oldIactivityManager);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        class ActivityMH implements  Handler.Callback{
            private  Handler mH;
    
            public ActivityMH(Handler mH) {
                this.mH = mH;
            }
    
            @Override
            public boolean handleMessage(Message msg) {
    //LAUNCH_ACTIVITY ==100 即将要加载一个activity了
                if (msg.what == 100) {
    //加工 --完  一定丢给系统  secondActivity  -hook->proxyActivity---hook->    secondeActivtiy   2
                    handleLuachActivity(msg);
                }
    //做了真正的跳转
                mH.handleMessage(msg);
                return  true;
            }
    
            private void handleLuachActivity(Message msg) {
    //            还原   ActivityClientRecord obj
                Object obj = msg.obj;
                try {
                    Field intentField=obj.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    //  ProxyActivity   2
                    Intent realyIntent = (Intent) intentField.get(obj);
    //                sconedActivity  1
                    Intent oldIntent = realyIntent.getParcelableExtra("oldIntent");
                    if (oldIntent != null) {
                        MyApplication.isPlugin = true;
    //                    集中式登录
                        SharedPreferences share = context.getSharedPreferences("xxx",
                                Context.MODE_PRIVATE);
                        if (share.getBoolean("login",false)) {
    
    //                      登录  还原  把原有的意图    放到realyIntent   SecondeActivity
                            realyIntent.setComponent(oldIntent.getComponent());
                        }else {
                            MyApplication.isPlugin = false;
                            ComponentName componentName = new ComponentName(context,LoginActivity.class);
                            realyIntent.putExtra("extraIntent", oldIntent.getComponent()
                                    .getClassName());
                            realyIntent.setComponent(componentName);
                        }
                        realyIntent.setComponent(oldIntent.getComponent());
    
                        Field activityInfoField= obj.getClass().getDeclaredField("activityInfo");
                        activityInfoField.setAccessible(true);
                        ActivityInfo activityInfo= (ActivityInfo) activityInfoField.get(obj);
    //              插件的class  packageName--->loadeApk   系统   第一次 IPackageManager ----》activitry  -——》包名   ---》
    //                    不够 IPackageManage.getPackageInfo()
                        activityInfo.applicationInfo.packageName = oldIntent.getPackage() == null ? oldIntent.getComponent().getPackageName()
                                : oldIntent.getPackage();
                        hookPackgeManager();
                    }else {
                        MyApplication.isPlugin = false;
                    }
    //              msg.obj      真实类型   ActivityClientRecord
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
    
            }
        }
    
        private void hookPackgeManager() {
    //          hook   方法  IPackageManager.getPackgeInfo
    
            // 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
            // 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查.
    
            Class<?> activityThreadClass = null;
            try {
                activityThreadClass = Class.forName("android.app.ActivityThread");
                Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
                currentActivityThreadMethod.setAccessible(true);
                Object currentActivityThread = currentActivityThreadMethod.invoke(null);
    
                // 获取ActivityThread里面原始的 sPackageManager
                Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
                sPackageManagerField.setAccessible(true);
                Object sPackageManager = sPackageManagerField.get(currentActivityThread);
    
                Log.i("xxx", " handleMessage之前发生啦   ");
                // 准备好代理对象, 用来替换原始的对象
                Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
                Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader()
                        , new Class[]{iPackageManagerInterface},new IPackageManagerHandler(sPackageManager) );
                sPackageManagerField.set(currentActivityThread,proxy);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        class startActivty implements InvocationHandler {
            private  Object iActivityManagerObject;
    
            public startActivty(Object iActivityManagerObject) {
                this.iActivityManagerObject = iActivityManagerObject;
            }
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                Log.i("INFO","invoke    "+method.getName());
                if ("startActivity".equals(method.getName())) {
                    Log.i("INFO","-----------------startActivity--------------------------");
    //瞒天过海
    //                寻找传进来的intent
                    Intent intent = null;
                    int index=0;
                    for (int i=0;i<args.length;i++) {
    //                    intent
                        Object arg = args[i];
                        if (arg instanceof Intent) {
                            intent = (Intent) args[i];
                            index = i;
                        }
                    }
    //目的  ---载入acgtivity  将它还原
                    Intent newIntent = new Intent();
                    ComponentName componentName = new ComponentName(context, ProxyActivity.class);
                    newIntent.setComponent(componentName);
    //                真实的意图 被我隐藏到了  键值对
                    newIntent.putExtra("oldIntent", intent);
                    args[index] = newIntent;
                }
    
                return method.invoke(iActivityManagerObject, args);
            }
        }
    
    //    通过  修改  mPackages   ArrayMap<String, WeakReference<LoadedApk>>
    //    LoadedApk ---->插件
    //    2   实例化 LoadedApk
    //    3   put
    
    
        public void putLoadedApk(String path) {
            try {
    
                Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
    //            先还原activityThread对象
                Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
                currentActivityThreadMethod.setAccessible(true);
                Object currentActivityThread = currentActivityThreadMethod.invoke(null);
    
    //    再来还原  mPackages  对象
                // 获取到 mPackages 这个静态成员变量, 这里缓存了apk包的信息
                Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
                mPackagesField.setAccessible(true);
                Map mPackages = (Map) mPackagesField.get(currentActivityThread);
    
    //z找到  getPackageInfoNoCheck   method 方法
                Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
                Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod(
                        "getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);
    
    //        得到 CompatibilityInfo  里面的  静态成员变量       DEFAULT_COMPATIBILITY_INFO  类型  CompatibilityInfo
                Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
                Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
    
                ApplicationInfo applicationInfo = parseReceivers(context, path);
    
    //            一个问题  传参 ApplicationInfo ai 一定是与插件相关    ApplicationInfo----》插件apk文件
    //LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo)
                Object loadedApk=getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);
    
    
    
                String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath();
                String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath();
    
                ClassLoader classLoader = new CustomClassLoader(path,odexPath,libDir,context.getClassLoader());
                Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
                mClassLoaderField.setAccessible(true);
                mClassLoaderField.set(loadedApk,classLoader);
                WeakReference weakReference = new WeakReference(loadedApk);
    
    //     最终目的  是要替换ClassLoader  不是替换LoaderApk
                mPackages.put(applicationInfo.packageName,weakReference);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        private ApplicationInfo   parseReceivers(Context context, String path) {
    
    //        Package对象
    //        PackageParser pp = new PackageParser();
    
    //        PackageParser.Package  pkg = pp.parsePackage(scanFile, parseFlags);
    
    
            try {
    
                Class   packageParserClass = Class.forName("android.content.pm.PackageParser");
                Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
                Object packageParser = packageParserClass.newInstance();
                Object packageObj=  parsePackageMethod.invoke(packageParser, new File(path), PackageManager.GET_ACTIVITIES);
    
    //
    
    
    
                Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
                Field intentsField = componentClass.getDeclaredField("intents");
    
    
                // 调用generateActivityInfo 方法, 把PackageParser.Activity 转换成
                Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
    //            generateActivityInfo方法
                Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
                Object defaltUserState= packageUserStateClass.newInstance();
                Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
                        packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
                Class<?> userHandler = Class.forName("android.os.UserHandle");
                Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
                int userId = (int) getCallingUserIdMethod.invoke(null);
    //目的     generateApplicationInfo  方法  生成  ApplicationInfo
                // 需要调用 android.content.pm.PackageParser#generateActivityInfo(android.content.pm.ActivityInfo, int, android.content.pm.PackageUserState, int)
                //      generateApplicationInfo
                Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo",
                        packageObj.getClass(),
                        int.class,
                        packageUserStateClass);
                ApplicationInfo applicationInfo= (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser, packageObj, 0, defaltUserState);
    
                applicationInfo.sourceDir = path;
                applicationInfo.publicSourceDir = path;
                return applicationInfo;
                //generateActivityInfo
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
    
        }
    
    
    
    
    //插件的apk的  dex  -->   Element
    
        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();
            }
    
    
    
    
    
    
    
        }
    
    
    }
    

    相关文章

      网友评论

          本文标题:插件化(按需加载(loadApk))

          本文链接:https://www.haomeiwen.com/subject/ozrhyqtx.html