搭建Android客户端APP架构——《跨组件化路由实现——路由跳转》

it2024-01-07  61

搭建Android客户端APP架构——《跨组件化路由实现一路由跳转》

背景说明我们为什么要用优雅的方式进行路由跳转如何优雅的进行路由跳转实现创建路由模块添加路由核心注解添加Arouter核心单例ARouter ARouterProcessor 生成添加数据的类组件化开发中的使用代码链接 吐槽

背景

本人从事开发工作也有多年,目前坐标湖南长沙,以前在各种平台也发过一些文章但是都没有坚持下来; 我初步规划是写一个完整的项目系列文章期望能坚持下来。 为什么会想到要写呢? 其一、眨眼就到了而立之年,觉得自己记忆力也是下降久做过的东西总是记不起,果然是好记性不如烂笔头。 其二、这么多年白嫖了网上很多的文章,视频,一直觉得应该分享一些东西但总是沉不下心去做。 其三、可能写的不好至少也留下一些东西,也是希望能帮助到一些朋友。

说明

按照我偶尔写一点代码然后偶尔写一篇文章的速度可能,一步步的慢慢搭建一个可用的示例项目可能会比较慢,这篇是系列文章的第三篇开发。组件化开发必然就需要涉及到路由跳转。 当然业界有很多大名鼎鼎的组件化路由工具如:ARouter,虽然我们可能写得不如ARouter全,但是我们一定不能直接拿来主义。我们需要是自己弄清楚实现核心内容。

我们为什么要用优雅的方式进行路由跳转

有人可能会问反正都是会要引用直接Activity类跳转不就得了…那…我们干脆不用组件化开发吧… 在我的想法中组件化开发,插件化开发核心思想应该是解耦,只有解耦之后才能谈我们的组件化和插件化。 既然是解耦当然不能直接引用我们的Activity类进行跳转了。而且试想一个场景我们主APP模块里面已经去掉了一个活动的引用(下架活动了)我们Activity类是不是就找不到了这个时候是不是就得报错呢。我们每次都要去掉再添加吗?当然不应该这样。 我们应该优雅的进行路由跳转。

如何优雅的进行路由跳转实现

(取名凭啥我不能取一个ARouter哈哈…)

创建路由模块

首先三连创建arouter-annotations(JAVA LIB),arouter-compiler(JAVA LIB), arouter 创建步骤请参考: 搭建Android客户端APP架构——《编译时APT技术》.

添加路由核心注解

package com.lyl.arouter.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.CLASS; @Retention(CLASS) @Target(ElementType.TYPE) public @interface IsActivity { //使用这个String可以跳转到本Activity String value(); //要跳转到本Activity需要先验证的事情(拦截)默认是可以没有拦截 Class<? extends IInterceptor> interceptor() default DefaultInterceptor.class; }

拦截器接口

package com.lyl.arouter.annotations; public interface IInterceptor { boolean interceptor(Object context); }

默认拦截器实现

package com.lyl.arouter.annotations; /** * * @Description 默认拦截器 * * @CreateDate 2020/10/7 * * @Version 1.0 * * @Remark TODO **/ public class DefaultInterceptor implements IInterceptor { @Override public boolean interceptor(Object context) { return false; } }

添加Arouter核心

单例ARouter

private static ARouter instance = new ARouter(); //保存路由跳转数据 private Map<String, Class<? extends Activity>> mRouterData = new HashMap<>(); //保存拦截器数据 private Map<String, Class<? extends IInterceptor>> mInterceptorData = new HashMap<>(); //是否初始化 private boolean hasInit = false; public static ARouter getInstance() { return instance; }

初始化

public boolean init(Application context) { long time = System.currentTimeMillis(); try { //根据指定需要扫描的包名获取所有类 List<String> classArr = ClassUtils.getFileNameByPackageName(context, AConstant.SCAN_ROUTER_PACKAGE); time = (System.currentTimeMillis() - time); Log.e(TAG, "time:" + time); time = System.currentTimeMillis(); for (String clazz : classArr) { if (clazz.contains(AConstant.SCAN_ROUTER_CLASS)) { // mRouterClassName.add(clazz); Log.e(TAG, "clazz:" + clazz); //反射生成扫描到的类 ReflexUtils.genObjByClassName(clazz); } } hasInit = true; return true; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } time = (System.currentTimeMillis() - time); Log.e(TAG, "time:" + time); return false; }

我们路由数据到Map的方法

//添加路由数据 public void addRouterData(String key, Class<? extends Activity> clazz) { Log.e(TAG, "addRouterData:" + key); mRouterData.put(key, clazz); } //添加拦截器 public void addInterceptor(String key, Class<? extends IInterceptor> clazz) { Log.e(TAG, "addInterceptor:" + key); mInterceptorData.put(key, clazz); }

跳转的方法

//跳转Activity 这样设置是未在Application中初始化时跳转时会初始化一次,但初始化会耗费时间会用demo测试反馈在200ms以内具体看具体项目 public void jumpActivity(Context context, String jumpKey) { if (hasInit) { boolean isCanJump = isCanJump(context, mInterceptorData, jumpKey); if (!isCanJump && !TextUtils.isEmpty(jumpKey) && mRouterData.containsKey(jumpKey)) { Class<? extends Activity> clazz = mRouterData.get(jumpKey); if (clazz != null) { startActivity(context, clazz); } } } else { //这里没有添加穿透问题处理 一般跳转场景很少会涉及 if (init((Application) context.getApplicationContext())) { jumpActivity(context, jumpKey); } } } private void startActivity(Context context, Class clazz) { Intent intent = new Intent(); intent.setClass(context, clazz); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } private boolean isCanJump(Context context, Map<String, Class<? extends IInterceptor>> interceptorDatas, String key) { if (!interceptorDatas.containsKey(key)) { return false; } else { Class c = interceptorDatas.get(key); try { IInterceptor o = (IInterceptor) c.newInstance(); return o.interceptor(context); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } return false; } }

ARouterProcessor 生成添加数据的类

生成思想比较简单就是收集所有的注解,再根据每个模块添加的注解生成相应的添加到Map的类文件。

将数据生成到如下两个Map中

//保存路由跳转数据 private Map<String, Class<? extends Activity>> mRouterData = new HashMap<>(); //保存拦截器数据 private Map<String, Class<? extends IInterceptor>> mInterceptorData = new HashMap<>(); //核心部分 @AutoService(Processor.class) public class ARouterProcessor extends BaseProcessor { @Override protected Set<Class<? extends Annotation>> getSupportedAnnotations() { Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>(); annotations.add(IsActivity.class); return annotations; } @Override public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment env) { Set<? extends Element> clazzElement = env.getElementsAnnotatedWith(IsActivity.class); if (clazzElement != null && clazzElement.size() > 0) { //构建ARouter需要的文件 buildARouterUtilFile(clazzElement); } return false; } } //构建ARouterUtil private void buildARouterUtilFile(Set<? extends Element> clazzElement) { try { String activityClassNameStr = "ARouterUtil_" + System.currentTimeMillis(); TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr).addModifiers(Modifier.PUBLIC) .addModifiers(Modifier.FINAL, Modifier.PUBLIC); System.out.println(activityClassNameStr + clazzElement.size()); MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder(); constructorMethodBuilder.addModifiers(Modifier.PUBLIC); for (Element object : clazzElement) { String key = object.getAnnotation(IsActivity.class).value(); String packageName = mElementUtils.getPackageOf(object).getQualifiedName().toString(); String className = object.getSimpleName().toString(); //object.getAnnotation(IsActivity.class).interceptor().getCanonicalName();不能直接这么获取会报错 String interceptorName = getClassNameByAnnotation(object, IsActivity.class, "interceptor"); System.out.println(interceptorName); constructorMethodBuilder.addStatement("ARouter.getInstance().addRouterData($S,$L.$L.class)", key, packageName, className); if (interceptorName != null && interceptorName.length() > 0 && !interceptorName.equals("com.lyl.arouter.annotations.DefaultInterceptor")) { constructorMethodBuilder.addStatement("ARouter.getInstance().addInterceptor($S,$L.class)", key, interceptorName); } } classBuilder.addMethod(constructorMethodBuilder.build()); JavaFile javaFile = JavaFile.builder("com.lyl.arouter", classBuilder.build()).build(); javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } } //通过Annotation类获取类名 private String getClassNameByAnnotation(Element object, Class<? extends Annotation> clazz, String valueName) { try { AnnotationMirror annotationMirror = MoreElements.getAnnotationMirror(object, clazz).get(); Set<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> entrySet = annotationMirror.getElementValues().entrySet(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> value : entrySet) { if (value.getKey().getSimpleName().contentEquals(valueName)) { DeclaredType declaredType = (DeclaredType) value.getValue().getValue(); TypeElement typeElement = (TypeElement) declaredType.asElement(); return typeElement.getQualifiedName().toString(); } } } catch (Exception e) { System.out.println(e.getMessage()); } return null; }

组件化开发中的使用

//添加注解 //定义的value不重复即可可以自己要求按照某种协议规定好。

//跳转到这个Activity使用com.lyl.practice/MainActivity,跳转会需要先经过 LoginInterceptor拦截 @IsActivity(value = "com.lyl.practice/MainActivity", interceptor = LoginInterceptor.class) public class MainActivity extends AppCompatActivity { }

跳转

ARouter.getInstance().jumpActivity(this,"com.lyl.practice/MainActivity");

//这里可以加一个判断如果要到MainActivity就可以先跳转到LoginActivity

public class LoginInterceptor implements IInterceptor { @Override public boolean interceptor(Object context) { ARouter.getInstance().jumpActivity((Context) context, "com.lyl.login/LoginActivity"); return true; } }

代码链接

demo链接: 代码链接.

吐槽

其实这个Demo写了一段时间了。本来写了一些关于组件化的内容。想着放这里不合适添加到组件化文章里了。

最新回复(0)