【Android 组件化】路由组件 ( 运行时获取 注解处理器 生成的路由表 )
2023-06-13 09:17:50 时间
文章目录
组件化系列博客 :
- 【Android 组件化】从模块化到组件化
- 【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
- 【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )
- 【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )
- 【Android 组件化】路由组件 ( 路由组件结构 )
- 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
- 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
- 【Android 组件化】路由组件 ( 注解处理器参数选项设置 )
- 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )
- 【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )
- 【Android 组件化】路由组件 ( 组件间共享的服务 )
- 【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )
一、获取应用的 APK
获取应用的 APK 文件 :
首先 , 获取 ApplicationInfo 应用信息 ;
ApplicationInfo applicationInfo = null;
try {
applicationInfo = mContext.getPackageManager().getApplicationInfo(mContext
.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
然后 , 从应用中获取 APK 的安装路径 ;
// 使用集合存放应用安装的 APK 文件
List<String> sourcePaths = new ArrayList<>();
// 一般情况下 , 一个应用只有一个安装 APK
sourcePaths.add(applicationInfo.sourceDir);
最后 , 考虑 instant run 的情况 , 可能存在多个 APK 文件 , 如果有多个 , 也一并放入路径字符串集合中 ;
// 如果是 instant run 形式安装的 , 则有多个 APK 文件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (null != applicationInfo.splitSourceDirs) {
sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
}
}
代码示例 :
ApplicationInfo applicationInfo = null;
try {
applicationInfo = mContext.getPackageManager().getApplicationInfo(mContext
.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
// 使用集合存放应用安装的 APK 文件
List<String> sourcePaths = new ArrayList<>();
// 一般情况下 , 一个应用只有一个安装 APK
sourcePaths.add(applicationInfo.sourceDir);
// 如果是 instant run 形式安装的 , 则有多个 APK 文件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (null != applicationInfo.splitSourceDirs) {
sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
}
}
二、获取所有 APK 中 kim.hsl.router 包的类
获取所有 APK 中 kim.hsl.router 包的类 :
首先 , 根据 APK 的地址 , 创建 DexFile 对象 ;
// 获取 APK 下的 dex 文件
DexFile dexfile = null;
try {
dexfile = new DexFile(path);
} catch (IOException e) {
e.printStackTrace();
}
然后 , 遍历 DexFile 对象中的所有的类 , 调用 dexfile.entries() 获取 Enumeration<String> 对象进行遍历 , 将 “kim.hsl.router” 包名下的类记录下来 ;
Enumeration<String> dexEntries = dexfile.entries();
// 遍历 DEX 文件中的所有的类
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith("kim.hsl.router")) {
classNames.add(className);
}
}
代码示例 :
/*
根据获取所有 APK 下的类
根据 kim.hsl.router 包名, 获取该包名下的所有路由类
*/
// 获取查找的 kim.hsl.router 包下的类 的 类名
Set<String> classNames = new HashSet<>();
// 遍历所有的 APK 路径 , 查找其中的 DEX 中的类
for (final String path : sourcePaths) {
// 获取 APK 下的 dex 文件
DexFile dexfile = null;
try {
dexfile = new DexFile(path);
} catch (IOException e) {
e.printStackTrace();
}
Enumeration<String> dexEntries = dexfile.entries();
// 遍历 DEX 文件中的所有的类
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith("kim.hsl.router")) {
classNames.add(className);
}
}
}
三、获取其它 Module 中的路由表
之前已经获取了 “kim.hsl.router” 包中的所有类 , 将 " kim.hsl.router.Router_Root_ " 开头的类记录下来 , 使用反射获取相关类 , 可以直接创建 Root 表对应的对象 ;
代码示例 :
// 最终所有的 kim.hsl.router 包下的类都存放到了 Set<String> classNames 变量中
for (String className : classNames){
/*
这是打印出来的类
kim.hsl.router_annotation.model.RouteBean$Type
kim.hsl.router.Router_Group_app
kim.hsl.router_annotation.Route
kim.hsl.router.Router_Root_library2
kim.hsl.router.Router_Root_app
kim.hsl.router.Router_Group_library2
kim.hsl.router_annotation.model.RouteBean
*/
Log.i(TAG, "loadInfo : " + className);
// 如果该类以 " Router_Root_ " 开头 , 说明这是 Root 表类
if (className.startsWith("kim.hsl.router.Router_Root_")) {
// root中注册的是分组信息 将分组信息加入仓库中
try {
// 获取 IRouteRoot 类
Class<IRouteRoot> clazz = (Class<IRouteRoot>) Class.forName(className);
// 获取构造函数
Constructor<IRouteRoot> constructor = clazz.getConstructor();
// 创建 IRouteRoot 类
IRouteRoot routeRoot = constructor.newInstance();
// 将 Root 表的信息装载到 Warehouse.groupsIndex 集合中
routeRoot.loadInto(Warehouse.groupsIndex);
// 打印 Root 表
for ( Map.Entry<String, Class<? extends IRouteGroup>> entry : Warehouse.groupsIndex.entrySet()){
Log.i(TAG, "loadInfo : " + entry.getKey() + " : " + entry.getValue().getName());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
最终打印出的数据 :
loadInfo : kim.hsl.router_annotation.model.RouteBean$Type
loadInfo : kim.hsl.router.Router_Group_app
loadInfo : kim.hsl.router_annotation.Route
loadInfo : kim.hsl.router.Router_Root_library2
loadInfo : library2 : kim.hsl.router.Router_Group_library2
loadInfo : kim.hsl.router.Router_Root_app
loadInfo : app : kim.hsl.router.Router_Group_app
loadInfo : library2 : kim.hsl.router.Router_Group_library2
loadInfo : kim.hsl.router.Router_Group_library2
loadInfo : kim.hsl.router_annotation.model.RouteBean
获取了 “app” 分组的路由表 kim.hsl.router.Router_Group_app 类 ,
获取了 “library2” 分组的路由表 kim.hsl.router.Router_Group_library2 类 ,
已知路由表的类名 , 可以使用反射创建两个路由表 , 并拿到路由表中的数据 ;
四、Router 路由加载类代码
Router 现阶段完整代码 :
package kim.hsl.route_core;
import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import dalvik.system.DexFile;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.route_core.template.IRouteRoot;
public class Router {
private static final String TAG = "Router";
/**
* 上下文
*/
private static Application mContext;
/**
* 单例类
*/
private static Router instance;
private Router() {
}
/**
* 初始化路由表
* @param application
*/
public static void init(Application application) {
mContext = application;
loadInfo();
}
/**
* 加载 分组 路由表 数据
* 每个分组对应一个路由表
*/
private static void loadInfo(){
/*
获取程序的所有 APK 安装文件
*/
ApplicationInfo applicationInfo = null;
try {
applicationInfo = mContext.getPackageManager().getApplicationInfo(mContext
.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
// 使用集合存放应用安装的 APK 文件
List<String> sourcePaths = new ArrayList<>();
// 一般情况下 , 一个应用只有一个安装 APK
sourcePaths.add(applicationInfo.sourceDir);
// 如果是 instant run 形式安装的 , 则有多个 APK 文件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (null != applicationInfo.splitSourceDirs) {
sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
}
}
/*
根据获取所有 APK 下的类
根据 kim.hsl.router 包名, 获取该包名下的所有路由类
*/
// 获取查找的 kim.hsl.router 包下的类 的 类名
Set<String> classNames = new HashSet<>();
// 遍历所有的 APK 路径 , 查找其中的 DEX 中的类
for (final String path : sourcePaths) {
// 获取 APK 下的 dex 文件
DexFile dexfile = null;
try {
dexfile = new DexFile(path);
} catch (IOException e) {
e.printStackTrace();
}
Enumeration<String> dexEntries = dexfile.entries();
// 遍历 DEX 文件中的所有的类
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith("kim.hsl.router")) {
classNames.add(className);
}
}
}
// 最终所有的 kim.hsl.router 包下的类都存放到了 Set<String> classNames 变量中
for (String className : classNames){
/*
这是打印出来的类
kim.hsl.router_annotation.model.RouteBean$Type
kim.hsl.router.Router_Group_app
kim.hsl.router_annotation.Route
kim.hsl.router.Router_Root_library2
kim.hsl.router.Router_Root_app
kim.hsl.router.Router_Group_library2
kim.hsl.router_annotation.model.RouteBean
*/
Log.i(TAG, "loadInfo : " + className);
// 如果该类以 " Router_Root_ " 开头 , 说明这是 Root 表类
if (className.startsWith("kim.hsl.router.Router_Root_")) {
// root中注册的是分组信息 将分组信息加入仓库中
try {
// 获取 IRouteRoot 类
Class<IRouteRoot> clazz = (Class<IRouteRoot>) Class.forName(className);
// 获取构造函数
Constructor<IRouteRoot> constructor = clazz.getConstructor();
// 创建 IRouteRoot 类
IRouteRoot routeRoot = constructor.newInstance();
// 将 Root 表的信息装载到 Warehouse.groupsIndex 集合中
routeRoot.loadInto(Warehouse.groupsIndex);
// 打印 Root 表
for ( Map.Entry<String, Class<? extends IRouteGroup>> entry : Warehouse.groupsIndex.entrySet()){
Log.i(TAG, "loadInfo : " + entry.getKey() + " : " + entry.getValue().getName());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
五、博客资源
博客源码 :
相关文章
- 加密狗android,Android系统加密狗的设计与实现
- android 常用加密,分享一下Android各种类型的加密
- 代码加密 android,Android 开发怎样做代码加密或混淆「建议收藏」
- android移动点餐系统内容和要求,基于Android云计算的移动点餐系统
- android bindservice方法,Android bindservice方法返回false
- burpsuite小米手机抓包_Android 7.0+手机burpsuite抓包https
- Android 代码混淆配置总结[通俗易懂]
- 测试android sdk是否安装成功,配置Android SDK
- android sdk根目录,Android SDK位置
- android studio 导出的jar中没有主清单属性「建议收藏」
- android 复制控件,Android长按复制文本功能[通俗易懂]
- Android 数据库加密 android-database-sqlcipher 开源版本编译过程
- 软件分享 | 第三十六期 蓝光线路 点播直播一体TV盒子(Android、Pad、TV、BOX)
- 【Android FFMPEG 开发】音视频基础 和 FFMPEG 编译 ( 音视频基础 | MPEG-4 标准 | Android 开发环境 | FFMPEG 交叉编译 | 安卓项目导入配置 )
- 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
- 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )
- 【Android 组件化】路由组件 ( 注解处理器调试 )
- 【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )
- 【Android 逆向】Android 逆向通用工具开发 ( 静态库项目中的网络操作核心类 CNetwork 分析 )
- 【Android Gradle 插件】Module 目录下 build.gradle 配置文件 ( android 闭包块配置 | AppExtension 扩展类型参考文档 )
- 【Android Gradle 插件】build.gradle 中的 android 配置 ( 配置项 | compileSdkVersion 配置 | buildToolsVersion 配置 )
- 【ijkplayer】编译 Android 版本的 ijkplayer ⑥ ( 进入 ijkplayer-android/android 目录 | 执行 compile-ijk.sh 脚本完成编译 )
- 【Android Gradle 插件】组件化中的 Gradle 构建脚本实现 ③ ( 在 Gradle 构建脚本中实现 AndroidManifest.xml 清单文件切换设置 )
- 【错误记录】Unity 安卓打包报错( Platform Android with graphics API OpenGLES3 is not supported with HDRP )
- 【错误记录】Android Studio 编译报错 ( Could not resolve com.android.tools.build:gradle:7.4.2. )
- Android谷歌官方的自适应TextView字体大小的解决方案详解手机开发
- [android] 测试的相关概念详解手机开发
- android键盘事件和屏幕事件的运行原理及交互实现
- android自定义Android菜单背景的代码
- Android-屏幕适配需要注意的地方总结
- 在android中增加curl的解决方法