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

背景

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

说明

按照我偶尔写一点代码然后偶尔写一篇文章的速度可能,一步步的慢慢搭建一个可用的示例项目可能会比较慢,这篇是系列文章的第三篇开发。组件化开发必然就需要涉及到路由跳转。
当然业界有很多大名鼎鼎的组件化路由工具如: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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值