前言
xpoedinstall及安装器,它的作用有几个:安装xposed框架、激活xposed模块、搜索模块等等
如果我们已经安装了xposed框架,或者说我们自己二次开发了这个框架,通过手动的方式安装。然后需要使用我们自己的xposed模块,这时xposedinstall的作用就只剩激活模块了,那么我们是否可以摆脱它来激活模块?
源码
先来看看它如何激活模块的
在它的源码中有一个ModuleUtil类,其中
public void setModuleEnabled(String packageName, boolean enabled) { if (enabled) mPref.edit().putInt(packageName, 1).apply(); else mPref.edit().remove(packageName).apply(); } 复制代码
可以看到将激活的模块的包名存到了sharepreference中,然后还有另外一个类
public synchronized void updateModulesList(boolean showToast) { try { Log.i(XposedApp.TAG, "updating modules.list"); int installedXposedVersion = XposedApp.getInstalledXposedVersion(); PrintWriter modulesList = new PrintWriter(MODULES_LIST_FILE); PrintWriter enabledModulesList = new PrintWriter(XposedApp.ENABLED_MODULES_LIST_FILE); List<InstalledModule> enabledModules = getEnabledModules(); for (InstalledModule module : enabledModules) { if (module.minVersion > installedXposedVersion || module.minVersion < MIN_MODULE_VERSION) continue; modulesList.println(module.app.sourceDir); try { String installer = mPm.getInstallerPackageName(module.app.packageName); if (!PLAY_STORE_PACKAGE.equals(installer)) { enabledModulesList.println(module.app.packageName); } } catch (IllegalArgumentException ignored) { // In rare cases, the package might not be installed anymore at this point, // so the PackageManager can't return its installer package name. } } modulesList.close(); enabledModulesList.close(); FileUtils.setPermissions(MODULES_LIST_FILE, 00664, -1, -1); FileUtils.setPermissions(XposedApp.ENABLED_MODULES_LIST_FILE, 00664, -1, -1); if (showToast) showToast(R.string.xposed_module_list_updated); } catch (IOException e) { Log.e(XposedApp.TAG, "cannot write " + MODULES_LIST_FILE, e); Toast.makeText(mApp, "cannot write " + MODULES_LIST_FILE + e, Toast.LENGTH_SHORT).show(); } } 复制代码
这里有一个getEnableModules函数,如下:
public List<InstalledModule> getEnabledModules() { LinkedList<InstalledModule> result = new LinkedList<InstalledModule>(); for (String packageName : mPref.getAll().keySet()) { InstalledModule module = getModule(packageName); if (module != null) result.add(module); else setModuleEnabled(packageName, false); } return result; } 复制代码
可以看到这个函数正是从刚才sharepreference中读取激活的模块包名
然后回到updateModulesList中,可以看到将这些激活的模块的sourceDir保存到MODULES_LIST_FILE这个文件中,即:
private static final String MODULES_LIST_FILE = XposedApp.BASE_DIR + "conf/modules.list"; 复制代码
以上就是xposedinstaller激活模块的流程,可以看到就是将要激活的模块的apk地址保存到一个modules.list文件中。看来真正激活使用模块是在xposedbridge中,在它的main函数中(XposedBridge类)
protected static void main(String[] args) { // Initialize the Xposed framework and modules try { if (!hadInitErrors()) { initXResources(); SELinuxHelper.initOnce(); SELinuxHelper.initForProcess(null); runtime = getRuntime(); XPOSED_BRIDGE_VERSION = getXposedVersion(); if (isZygote) { XposedInit.hookResources(); XposedInit.initForZygote(); } XposedInit.loadModules(); } else { Log.e(TAG, "Not initializing Xposed because of previous errors"); } } catch (Throwable t) { Log.e(TAG, "Errors during Xposed initialization", t); disableHooks = true; } // Call the original startup code if (isZygote) { ZygoteInit.main(args); } else { RuntimeInit.main(args); } } 复制代码
主要是XposedInit.loadModules()这句,这个函数的代码:
static void loadModules() throws IOException { final String filename = BASE_DIR + "conf/modules.list"; BaseService service = SELinuxHelper.getAppDataFileService(); if (!service.checkFileExists(filename)) { Log.e(TAG, "Cannot load any modules because " + filename + " was not found"); return; } ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER; ClassLoader parent; while ((parent = topClassLoader.getParent()) != null) { topClassLoader = parent; } InputStream stream = service.getFileInputStream(filename); BufferedReader apks = new BufferedReader(new InputStreamReader(stream)); String apk; while ((apk = apks.readLine()) != null) { loadModule(apk, topClassLoader); } apks.close(); } 复制代码
这里可以看到从modules.list文件中读取已激活模块的apk地址,然后执行loadModule将其加载进来,loadModule中会有一些判断,比如说是否有assets/xposed_init文件等等,如果都正常就会将这个apk的class加载进内存中,后续就会调用他们,实际上就是将模块的代码注入到xposedbridge中来使用。这部分代码就不细说了。
(实际上,真正hook都是在xposedbridge中的,如果我们直接在这里写hook代码也是可以的,只是这样就无法灵活修改了。所以我们将hook代码写在apk中,然后在这里将这些类加载进来,然后xposedbridge再调用它的hookLoadPackage之类的代码就可以了)
结论
所以通过上面我们可以知道,我们完全可以抛开xposedinstaller,当我们安装app并打开时,可以自己创建或修改modules.list文件,将自己app加进入就可以了。当然还要重启手机,从上面可以看到,因为加载模块的代码是在main函数中执行的,所以只有重启手机才会再次执行。
当然这个默认的modules.list我们可能需要root权限才能读写,但是如果我们自己二次开发xposed,可以在xposedbridge中将这个文件路径改成sd卡下可以任意读写的路径,这样我们的app就可以随意进行改写了。
或者我们提前准好这个文件手动放到手机中,文件内容是一个未安装的apk的路径,比如/sdcard/123.apk,这样不需要安装应该就可以直接使用。这个我没有测试,效果待验证。