asiontang / XposedNoRebootModuleSample

Xposed Module Sample No Need To Reboot When Debug 不需要频繁重启调试的Xposed 模块源码例子

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

XposedNoRebootModuleSample

Xposed Module Sample No Need To Reboot When Debug 不需要频繁重启调试的Xposed 模块源码例子

实际可用的完整项目

部分核心代码

  1. 78.Xposed/BaseXposedHookLoadPackage.java at master · asiontang/78.Xposed
  2. 78.Xposed/DebugModeUtils.java at master · asiontang/78.Xposed
    /**
     * 在调试模式为了不频繁重启,使用反射的方式调用指定函数。来动态触发 Hook.
     */
    private boolean invokeHandleLoadPackage4release(final XC_LoadPackage.LoadPackageParam loadPackageParam)//
            throws IOException, NoSuchMethodException, IllegalAccessException, InstantiationException, ClassNotFoundException, InvocationTargetException
    {
        /*【方法5】*/
        {
            //需要解决新版本系统调试版本的路径随机化的问题:
            //Android 8拿到的调试包地址为: /data/app/cn.asiontang.xposed.auto_js_pro-TbJBoU7ixhS0Fiv8Z6qf7g==/base.apk 地址
            final String selfPackageName = this.getClass().getPackage().getName();
            final String hookPackageName = loadPackageParam.packageName;
            final String newApkFullPath = DebugModeUtils.getNewApkFullPathAcrossProcessByRoot(selfPackageName, hookPackageName);
            if (newApkFullPath == null)
            {
                LogEx.log(TAG, loadPackageParam.packageName, "Error:在指定目录(/data/data/{hookPackageName}/cache/!Ye.DebugModeUtils)获取不到.apk包最新地址");
                return false;
            }
            File file = new File(newApkFullPath);
            if (!file.exists())
            {
                LogEx.log(TAG, loadPackageParam.packageName, "Error:在/data/app找不到插件对应的.apk包", "apkFilePath=", newApkFullPath);
                return false;
            }
            LogEx.log(TAG, loadPackageParam.packageName, "newApkFullPath=", newApkFullPath);

            ClassLoader parentClassLoader;
            //在Android9.0报错:java.lang.ClassNotFoundException: Didn't find class "de.robv.android.xposed.IXposedHookLoadPackage"
            //NO:parentClassLoader = ClassLoader.getSystemClassLoader();
            //NO:parentClassLoader = loadPackageParam.classLoader;
            //在Android9.0会导致反射失效,无法拿到最新的代码.
            //NO:parentClassLoader = this.getClass().getClassLoader();
            parentClassLoader = XC_LoadPackage.LoadPackageParam.class.getClassLoader();
            LogEx.log(TAG, "parentClassLoader=", parentClassLoader);

            final PathClassLoader pathClassLoader = new PathClassLoader(newApkFullPath, parentClassLoader);
            LogEx.log(TAG, "pathClassLoader=", pathClassLoader);

            //在Android9.0上使用 getClassNameFromAsset(pathClassLoader) 会报错
            // java.io.FileNotFoundException: File doesn't exist: /data/app/cn.asiontang.xposed.heart_study-IBxQU_4AA4zvKX8qrEr-SA==/base.apk
            //因为当存在 parentClassLoader 时,会优先找父的资源,结果此时APK文件已经删除掉了,所以报错找不到.
            //所以需要一个单独的 resourcesLoader 使用 ClassLoader.getSystemClassLoader 作为parentClassLoader即可.
            final PathClassLoader resourcesLoader = new PathClassLoader(newApkFullPath, ClassLoader.getSystemClassLoader());
            final Class<?> aClass = Class.forName(getClassNameFromAsset(resourcesLoader), true, pathClassLoader);

            final Method aClassMethod = aClass.getMethod("handleLoadPackage4release", XC_LoadPackage.LoadPackageParam.class);
            return (Boolean) aClassMethod.invoke(aClass.newInstance(), loadPackageParam);
        }
        /*【方法4】
        {
            //因为新旧版本的APK所在目录不一样,所以换一种更加通用的办法获取所在路径.
            // 1.旧版本路径是:/data/app/%s-%s.apk
            // 2.新版本路径是:/data/app/%s-%s/base.apk

            //TEST1: // com.thirdparty.superuser|XposedBridge.BOOTCLASSLOADER|dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/XposedBridge.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib, /system/lib/arm]]]
            //TEST1: LogEx.log(loadPackageParam.packageName, "XposedBridge.BOOTCLASSLOADER", XposedBridge.BOOTCLASSLOADER);

            //TEST2: // com.thirdparty.superuser|loadPackageParam.classLoader|dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/Superuser/Superuser.apk"],nativeLibraryDirectories=[/system/app/Superuser/lib/x86, /vendor/lib, /system/lib, /system/lib/arm]]]
            //TEST2: LogEx.log(loadPackageParam.packageName, "loadPackageParam.classLoader", loadPackageParam.classLoader);

            //TEST3: // com.thirdparty.superuser|this.getClass().getPackage().getName()|cn.asiontang.xposed.auto_js_pro
            //TEST3: LogEx.log(loadPackageParam.packageName, "this.getClass().getPackage().getName()", this.getClass().getPackage().getName());

            //TEST4: // com.thirdparty.superuser|this.getClass().getPackage().getClassLoader()|dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/cn.asiontang.xposed.auto_js_pro-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib, /system/lib/arm]]]
            //TEST4: LogEx.log(loadPackageParam.packageName, "this.getClass().getPackage().getClassLoader()", this.getClass().getClassLoader());

            //输入:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/cn.asiontang.xposed.auto_js_pro-1/base.apk"],nativeLibraryDirectories
            String apkFilePath = String.valueOf(getClass().getClassLoader());
            //结果:/data/app/cn.asiontang.xposed.auto_js_pro-1/base.apk
            apkFilePath = apkFilePath.substring(apkFilePath.indexOf("/data/app"), apkFilePath.indexOf(".apk\"]") + 4);
            if (!new File(apkFilePath).exists())
            {
                //每次DEBUG运行时,就会自动增加到2.所以需要适配.
                if (apkFilePath.contains("1/base.apk"))
                    apkFilePath = apkFilePath.replace("1/base.apk", "2/base.apk");
                else if (apkFilePath.contains("2/base.apk"))
                    apkFilePath = apkFilePath.replace("2/base.apk", "1/base.apk");
                else
                {
                    LogEx.log(loadPackageParam.packageName, "Error:意料外的路径格式,文件夹尾字符既不是1也不是2", "apkFilePath=", apkFilePath);
                    return false;
                }

                if (!new File(apkFilePath).exists())
                {
                    LogEx.log(TAG, loadPackageParam.packageName, "Error:在/data/app找不到插件对应的.apk包", "apkFilePath=", apkFilePath);
                    return false;
                }
            }

            final PathClassLoader pathClassLoader = new PathClassLoader(apkFilePath, ClassLoader.getSystemClassLoader());
            final Class<?> aClass = Class.forName(getClassNameFromAsset(pathClassLoader), true, pathClassLoader);
            final Method aClassMethod = aClass.getMethod("handleLoadPackage4release", XC_LoadPackage.LoadPackageParam.class);
            return (Boolean) aClassMethod.invoke(aClass.newInstance(), loadPackageParam);
        }*/

        /*【方法3】*/
        /************************************************************
         *获取当前APK的包名,而不是当前框架的包名
         *以下方法都不行:返回值为 cn.asiontang.xposed
         * 1.BaseXposedHookLoadPackage.class.getPackage().getName();
         * 2.BuildConfig.APPLICATION_ID
         ************************************************************/
        //final String packageName = this.getClass().getPackage().getName();
        //String apkFilePath = String.format("/data/app/%s-%s.apk", packageName, 1);
        //if (!new File(apkFilePath).exists())
        //{
        //    apkFilePath = String.format("/data/app/%s-%s.apk", packageName, 2);
        //    if (!new File(apkFilePath).exists())
        //    {
        //        LogEx.log(loadPackageParam.packageName, "Error:在/data/app找不到.apk包" + packageName);
        //        return false;
        //    }
        //}
        //final PathClassLoader pathClassLoader = new PathClassLoader(apkFilePath, ClassLoader.getSystemClassLoader());
        //final Class<?> aClass = Class.forName(getClassNameFromAsset(pathClassLoader), true, pathClassLoader);
        //final Method aClassMethod = aClass.getMethod("handleLoadPackage4release", XC_LoadPackage.LoadPackageParam.class);
        //return (Boolean) aClassMethod.invoke(aClass.newInstance(), loadPackageParam);

        /*【方法2】*/
        //final String packageName = 项目的类.class.getPackage().getName();
        //String apkFilePath = String.format("/data/app/%s-%s.apk", packageName, 1);
        //if (!new File(apkFilePath).exists())
        //{
        //    apkFilePath = String.format("/data/app/%s-%s.apk", packageName, 2);
        //    if (!new File(apkFilePath).exists())
        //    {
        //        LogEx.log(loadPackageParam.packageName, "Error:在/data/app找不到APK文件" + packageName);
        //        return;
        //    }
        //}
        //final PathClassLoader pathClassLoader = new PathClassLoader(apkFilePath, ClassLoader.getSystemClassLoader());
        //final Class<?> aClass = Class.forName(getClassNameFromAsset(pathClassLoader), true, pathClassLoader);
        //final Method aClassMethod = aClass.getMethod("handleLoadPackage4release", XC_LoadPackage.LoadPackageParam.class);
        //Object isHandled = aClassMethod.invoke(aClass.newInstance(), loadPackageParam);

        /*【方法1】:无法达到效果*/
        //final Class<MainActivity> pathClassLoader = MainActivity.class;
        //final Class<?> aClass = Class.forName(pathClassLoader.getPackage().getName() + "." + XposedHookLoadPackage.class.getSimpleName(), true, pathClassLoader.getClassLoader());
        //final Method aClassMethod = aClass.getMethod("handleLoadPackage4release", XC_LoadPackage.LoadPackageParam.class);
        //aClassMethod.invoke(aClass.newInstance(), mLoadPackageParam);
    }

About

Xposed Module Sample No Need To Reboot When Debug 不需要频繁重启调试的Xposed 模块源码例子

License:Creative Commons Zero v1.0 Universal