MRouter(Android路由)

路由的意义:

  1. 模块间解耦,不能在代码中写死Activity类名。
  2. 动态配置业务需求,现在都是业务模块化开发了。

1. 注解

我们这次编写的MRoute主要使用了编译时注解技术,注解在我们日常使用的框架中都有体现。

运行时注解,主要集合反射来完成功能。

编译时注解,则主要是在编译阶段生成类,来辅助我们后面实现功能。

关于注解,不做详细描述。

2. Activity跳转

正常情况下,我们进行Activity跳转是这样的:

Intent intent=new Intent(this,AnotherActivity.class);
startActivity(intent);

使用路由跳转是这样的:


MRouter.startActivity(context,url,bundle);

MRouter会根据传入的参数url来查找对应的类,然后完成Activity的跳转。

3. 具体实现

3.1 项目结构

MRoute
|--RouteAnnotation
|
|--RouteProcessor
|
|--RouteApi

3.2 RouteAnnotation

注意该Moudule为Java Library.

RouteAnnotation中定义了MRouter使用的注解Route,具体实现如下:

/**
 * Android路由注解。
 *
 * @author zhangshuai.
 */
@Documented
@Inherited
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Route {
    String url() default "";
}

要点解释:

  1. 因为使用编译时注解,因此Route的只存在于class中,不是Runtime。
  2. 注解的只能使用在类上。

3.3 RouteCompiler

该Module同样为Java Library,用于处理注解并生成辅助类。

该项目中定义了注解处理器RouteProcessor,使用了auto-service、javapoet来辅助完成。

下面来看核心类RouteProcessor实现:


import core.zs.routeannotation.Route;

@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {
    
    private Filer mFiler;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFiler = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
        TypeSpec spec = processElements(elements);
        try {
            if (spec != null) {
                // 生成Java文件
                // RouteMap.java
                JavaFile.builder("core.zs.route", spec).build().writeTo(mFiler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    private TypeSpec processElements(Set<? extends Element> elements) {
        if (elements == null || elements.size() == 0) {
            return null;
        }

        // 1. 构造参数,参数为activityMap
        // 参数类型为:HashMap<String,Class<?>>
        ParameterizedTypeName mapTypeName =
                ParameterizedTypeName.get(ClassName.get(HashMap.class), ClassName.get(String
                        .class), ClassName.get(Class.class));
        ParameterSpec mapSpec = ParameterSpec.builder(mapTypeName, "activityMap").build();
        
        // 2. 构造方法,方法名为initActivityMap
        // 参数为activityMap
        MethodSpec.Builder initMethodBuilder =
                MethodSpec.methodBuilder("initActivityMap").addModifiers(Modifier.PUBLIC,
                        Modifier.STATIC).addParameter(mapSpec);
        // 3. 处理使用Route注解的类信息
        for (Element element : elements) {
            Route route = element.getAnnotation(Route.class);
            String url = route.url();
            if (null != url && !"".equals(url)) {
                // 在initActivityMap中添加语句
                // activityMap.put(url,xxx.class);
                initMethodBuilder.addStatement("activityMap.put($S,$T.class)", url, ClassName.get
                        ((TypeElement) element));
            }
        }
        // 返回我们构造的类元素
        return TypeSpec.classBuilder("RouteMap").addMethod(initMethodBuilder.build())
                .addModifiers(Modifier.PUBLIC).build();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new LinkedHashSet<>();
        set.add(Route.class.getCanonicalName());
        return set;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}


使用处理器后,会在project的app/build/generated/source/apt/debug/项目名 下生产一个RouteMap.java文件。

其内容如下:

package core.zs.router;

import java.lang.Class;
import java.lang.String;
import java.util.HashMap;

public class RouteMap {
  public static void initActivityMap(HashMap<String, Class> activityMap) {
    activityMap.put("/core/zs/TestActivity",TestActivity.class);
  }
}

3.4 RouteApi

注意该Module为Android Library,该Module主要是封装一些常用的api,供其他项使用。

只有一个类MRouter。


package core.zs.routeapi;

/**
 * Created by ZhangShuai on 2018/6/26.
 */

public class MRouter {

    public static HashMap<String, Class<?>> activityMap = new HashMap<>();

    public static void init(Context context) {
        try {
            // 通过反射来调用RouteMap的initActivityMap方法,将数据保存到activityMap中
            Class clz = Class.forName("core.zs.route.RouteMap");
            if (clz != null) {
                Method initMapMethod = clz.getMethod("initActivityMap", HashMap.class);
                initMapMethod.invoke(null, activityMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 替换常规的startActivity方法
    public static void startActivity(Context context, String url, Bundle bundle) {
        if (context == null || url == null) {
            return;
        }
        Class atClazz = activityMap.get(url);
        if (atClazz != null) {
            System.out.println(atClazz.getCanonicalName());
            Intent intent = new Intent(context, atClazz);
            if (bundle != null) {
                intent.putExtras(bundle);
            }
            context.startActivity(intent);
        }else{
            System.out.println("=====Activity is null.");
        }


    }
}

3.5 如何使用

  1. 在Application中初始化
public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        MRouter.init(this);
    }
}
  1. 在Activity上使用Route注解。
@Route(url = "/core/zs/route/RouteActivity")
public class RouteActivity extends AppCompatActivity{
    //....
}

  1. 进行跳转
MRouter.startActivity(MainActivity.this, "/core/zs/route/RouteActivity", null);

4. 总结

随着项目的不断变化,模块化开发、组件化开发逐步被引入,使用MRouter能很好的帮你实现业务之间的解耦,实现业务之间的简单配置、顺利跳转。

奉上源码地址MRouter

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • Android组件化项目地址:Android组件化项目AndroidModulePattern Android组件...
    半灬边灬天阅读 2,862评论 4 37
  • 夜幕降临,阴沉沉的天空开始飘落起入秋以来第n场秋雨。 吃过晚饭,我们照例在客厅玩耍嬉戏。儿子现在特黏电视,吃过饭自...
    八七在路上阅读 195评论 1 5
  • 本文主要介绍怎么在Thinkphp中集成后台前端脚手架框架Vue-Cli。 一.安装Vue-Cli到Thinkph...
    风殇嶄往阅读 7,639评论 1 14
  • 2009-08-18 00:42:33 盛夏时光,阳光明媚。 遇见你。 也许是我多情 暗生情愫。 爱,要怎么说出口...
    国王的心事阅读 316评论 0 1