Android路由框架-FSS开源框架之FssRouter

介绍

FssRouter让路由变得更简单,更规范。自定义路由API使得路由参数与路由信息一目了然,更便于管理。FssRouter框架还提供了自动创建路由API的方式,你只需要一个注解便能声明一种路由方式,更简单方便。
gitHub地址:https://github.com/gongbox/fss

背景

在Android开发中,如果要从当前Activity跳转到下一个Activity,你可能会直接使用Intent的方式跳转,如下:

Intent intent = new Intent(this,XXXActivity.class);
intent.putExtra("name","value");  //传递参数
startActivity(intent);

或者你也可能使用阿里的ARouter路由框架,如下:

ARouter.getInstance().build("/xxx/xxx")
                    .withString("name","value")   //传递参数
                    .navigation(XXXActivity.class);

不管使用哪种方式,你都会发现如下问题:
1,路由要携带的参数有哪些?各个参数的key值是什么?哪些参数是必传?这些参数是什么意思...
2,对应的Activity是显示启动还是隐式启动,如果是隐式启动对应的action是什么,如果有requestCode,category,flags又该设置为多少
3,无法统一管理路由,到处可见的Intent与startActivity等等,路由方式很随意,哪里想跳就在哪儿写,想传什么参数就传什么。
4,代码量多,传的参数越多,要跳转的地方越多,代码量也就越多。

在这种背景下,笔者便有了灵感,写出了FssRouter路由框架,我们看看FssRouter是怎么解决这些问题的。

接入

在根项目的build.gradle文件中添加仓库地址

  allprojects {
    repositories {
      ...
      maven { url 'https://www.jitpack.io' }
    }
  }

对应模块添加依赖

implementation 'com.github.gongbox.fss:router-api:1.0.33'
annotationProcessor 'com.github.gongbox.fss:router-compiler:1.0.33'

使用

我们先看一下使用自定义路由API的方式,使用这种方式需要声明一个接口,在接口中声明路由信息。
现在假设我们有两个Activity,分别是MainActivity,DetailActivity,
现在的代码是这样:

public class MainActivity extends AppCompatActivity {
    ...
}
public class DetailActivity extends AppCompatActivity {
    ...
}

然后我们声明一个java接口,接口名随意,这里为ITestRouteApi

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  @RouteActivity(DetailActivity.class) //定义路由的目的Activity
  void navigateToDetailActivity(Context context); //第一个参数必须为Context类型
}

声明上面的接口api后,然后再编译项目,然后我们就可以使用如下的方式进行路由了。

public class MainActivity extends AppCompatActivity {
   ...
   void xxx(){
     //使用下面的方式进行路由
     FssRouteApi.TEST.navigateToDetailActivity(MainActivity.this);
   }
}

上面是一个简单的例子,我们在看看其他情况。

1,隐式意图启动

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  @RouteActivity(action = "com.fss.route.detail")
  void navigateToDetailActivity(Context context);
}

2,携带路由参数

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  @RouteActivity(action = "com.fss.route.detail")
  void navigateToDetailActivity(Context context, @Extra("value") int value); 
}

3,携带默认路由参数

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  @RouteActivity(DetailActivity.class)
  @DefaultExtra(
      name = "defaultValue",
      defaultValue = "hello",
      type = String.class
  )
  void navigateToDetailActivity(Context context);
}

4,设置requestCode,category,flags,路由动画等

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  
  @RouteActivity(
            value = DetailActivity.class,             //设置路由的目标Activity
            requestCode = 1234,                       //设置requestCode
  )
  void navigateToDetailActivity1(Context context);

  @RouteActivity(
            value = DetailActivity.class,             //设置路由的目标Activity
            category = Intent.CATEGORY_DEFAULT,       //设置category
  )
  void navigateToDetailActivity2(Context context);

  @RouteActivity(
            value = DetailActivity.class,             //设置路由的目标Activity
            flags = Intent.FLAG_ACTIVITY_CLEAR_TASK,  //设置falgs
  )
  void navigateToDetailActivity3(Context context);

  @RouteActivity(
            value = DetailActivity.class,             //设置路由的目标Activity
            enterAnim = android.R.anim.fade_in,       //设置activity进入动画
            exitAnim = android.R.anim.fade_out        //设置activity退出动画
  )
  void navigateToDetailActivity4(Context context);

}

5,携带Uri,设置type等

@RouteApi("test") //定义为路由Api,“test”为这个路由api的分组
public interface ITestRouteApi {
  @RouteActivity(action = Intent.ACTION_VIEW)
  void routeToView(Context context, Uri data);

  @RouteActivity(
            action = Intent.ACTION_GET_CONTENT,
            requestCode = 2,
            category = Intent.CATEGORY_OPENABLE,
            type = "video/*")
  void navigateToGetContent(Context context);
}

自动创建Api

上面展示了如何自定义一个路由Api,并如何进行路由。接下来我们看一种更简单的方式实现路由。
我们在的DetailActivity上声明一个注解@Route,编译项目

@Route
public class DetailActivity extends AppCompatActivity {
    ...
}

然后我们在MainActivity中便可以直接这样来跳转到DetailActivity了

public class MainActivity extends AppCompatActivity {
   ...
   void xxx(){
     //使用下面的方式进行路由
     FssRouteApi.DEFAULT.navigateToDetailActivity(MainActivity.this);
   }
}

这是因为在编译时,FssRouter会帮我们自动生成对应的路由Api,如下:


image.png

IDefaultRouteApi.java文件的具体内容如下:

public interface IDefaultRouteApi {
  @RouteActivity(DetailActivity.class)
  void navigateToDetailActivity(Context context);
}

因此,使用@Route注解便可以帮我们自动生成路由Api,这使得我们不必自定义路由API便可以实现路由,这种方式相对更简单,使用@Route注解,你几乎可以完成上面自定义Api的所有工作。笔者也更推荐使用这种方式,下面将介绍如何使用@Route注解。

1.携带参数

@Route(routeExtras = {
       @RouteExtra(name = "value", type = String.class)
})
public class DetailActivity extends AppCompatActivity {
    ...
}

其中name为对应参数的key,type为对应参数的类型,默认为Object.class,支持多个参数。
对应生成的API为:

public interface IDefaultRouteApi {
  @RouteActivity(DetailActivity.class)
  void navigateToDetailActivity(Context context, @Extra("value") String value);
}

2.携带默认参数

@Route(defaultExtras = {
       @DefaultExtra(name = "defaultValue", defaultValue = "hello" , type = String.class)
})
public class DetailActivity extends AppCompatActivity {
    ...
}

默认参数即是默认携带的参数,只支持基本数据类型,默认为String类型,支持多个默认参数。
对应生成的API为:

public interface IDefaultRouteApi {
  @RouteActivity(DetailActivity.class)
  @DefaultExtra(name = "defaultValue", defaultValue = "hello" , type = String.class)
  void navigateToDetailActivity(Context context);
}

3.隐式启动

@Route(action = "com.gongbo.fss.route.detail")
public class DetailActivity extends AppCompatActivity {
    ...
}

这里便会以隐式意图去启动这个Activity,但是注意你还是需要在AndroidManifest.xml中配置这个Activity支持隐式启动。
对应生成的API为:

public interface IDefaultRouteApi {
  @RouteActivity(action = "com.gongbo.fss.route.detail")
  void navigateToDetailActivity(Context context);
}

4. 设置分组

@Route(group = "test")
public class DetailActivity extends AppCompatActivity {
    ...
}

每一个分组都会对应FssRouteApi类中的一个静态常量,test分组即对应FssRouteApi.TEST,如果没有设置分组,默认为default分组(FssRouteApi.DEFAULT)。此外,相同分组的Api将定义在同一个文件内,如IDefaultRouteApi.class。
对应生成的API为:

public interface ITestRouteApi {
  @RouteActivity(DetailActivity.class)
  void navigateToDetailActivity(Context context);
}

5. 设置路由方法名

@Route(name = "navigateTest")
public class DetailActivity extends AppCompatActivity {
    ...
}

路由方法默认为 "navigateTo"+路由目的类的类名称,通过设置name你可以设置自己想要的名称。
对应生成的API为:

public interface ITestRouteApi {
  @RouteActivity(DetailActivity.class)
  void navigateTest(Context context);
}

6. 设置flags,category,requestCode等

@Route(
    category = Intent.CATEGORY_DEFAULT,
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK,
    requestCode = 1234
)
public class DetailActivity extends AppCompatActivity {
    ...
}

对应生成的API为:

public interface ITestRouteApi {
  int REQUEST_CODE_TO_DETAIL_ACTIVITY = 1234;
 
  @RouteActivity(
      value = DetailActivity.class,
      category = Intent.CATEGORY_DEFAULT,
      flags = Intent.FLAG_ACTIVITY_CLEAR_TASK,
      requestCode = REQUEST_CODE_TO_DETAIL_ACTIVITY)
  void navigateToDetailActivity(Context context);
}

7,其他,设置路由动画,为路由方法添加注释等等

下面是一个比较完整的例子

@Route(
        //设置路由名称
        name = "navigateTest",
        //设置分组
        group = "detail",
        //添加requestCode
        requestCode = 1234,
        //设置category
        category = Intent.CATEGORY_DEFAULT,
        //设置falgs
        flags = Intent.FLAG_ACTIVITY_CLEAR_TASK,
        //添加参数
        routeExtras = {
                @RouteExtra(name = "value", type = String.class, desc = "参数注释")
        },
        //添加默认参数
        defaultExtras = {
                @DefaultExtra(name = "defaultValue", defaultValue = "hello")
        },
        //activity进入动画
        enterAnim = android.R.anim.fade_in,
        exitAnim = android.R.anim.fade_out,
        //添加注释
        desc = "示例"
)
public class DetailActivity extends AppCompatActivity {
    ...
}

对应生成的API为:

public interface IDetailRouteApi {
  int REQUEST_CODE_TO_DETAIL_ACTIVITY = 1234;

  /**
   * 示例
   * @value 参数注释
   */
  @RouteActivity(
      value = RouteDetailActivity.class,
      requestCode = REQUEST_CODE_TO_ROUTE_DETAIL_ACTIVITY,
      category = {"android.intent.category.DEFAULT"},
      flags = 32768,
      enterAnim = 17432576,
      exitAnim = 17432577
  )
  @DefaultExtra(
      name = "defaultValue",
      defaultValue = "hello",
      type = String.class
  )
  void navigateTest(Context context, @Extra("value") String value);
}

这里提一下,@Route注解不仅可以声明在Activity上,也可以声明在Service或Fragment上,FssRouter同样支持路由到Service或Fragment,但部分属性不支持Service或Fragment。
而且你也可以在同一个Activity(或Service,Fragment)上添加多个@Route注解,以提供多种路由方式

其他

1,自定义配置
通过在build.gradle中defaultConfig中添加如下配置进行自定义修改

javaCompileOptions {
    annotationProcessorOptions {
          arguments = [
                    MODULE_NAME        : "fss",           //模块名(重要,多模块时一定要填写)
                    NAVIGATE_PREFIX    : "navigateTo",    //路由方法前缀
                    NAVIGATE_SUFFIX    : "",              //路由方法后缀
                    BUILD_INTENT_PREFIX: "buildIntentFor",//构建Intent方法前缀
                    BUILD_INTENT_SUFFIX: "",              //构建Intent方法后缀
                    API_PREFIX         : "I",             //路由接口名常量前缀
                    API_SUFFIX         : "RouteApi",      //路由接口名常量后缀
                    GROUP_PREFIX       : "",              //路由分组常量前缀
                    GROUP_SUFFIX       : "",              //路由分组常量后缀
                    DEFAULT_GROUP      : "default",       //默认分组名
                    ROUTE_API_PREFIX   : "",              //接口前缀
                    ROUTE_API_SUFFIX   : "RouteApi",      //接口后缀
          ]
      }
  }

以上展示的是默认值,大多数参数通常情况下你都不需要自定义,但要注意,MODULE_NAME一定要配置,尤其是在项目中存在多个使用FssRouter的模块时。在下节的多组件支持中会讲解。

2,多组件支持
通过在各组件/模块中配置不同的MODULE_NAME参数实现,这样相应模块生成的api将放在不同的包名下,默认是com.fss.router,修改后将为com.MODULE_NAME的值.router。同时相应的Api的接口名也将会修改,默认为FssRouteApi,修改后将为
MODULE_NAME的值+ROUTE_API_SUFFIX

3,同一个Activity生成多个跳转方式
如果你使用的是Java8

@Route(name = "navigateTest")
@Route(group = "test")
@Route(action = "com.fss.hello")
public class DetailActivity extends AppCompatActivity {
  ...
}

如果不是使用的Java8

@Routes(value = {
    @Route(name = "routeTest"),
    @Route(group = "test"),
    @Route(action = "com.fss.hello")
}) 
public class DetailActivity extends AppCompatActivity {
    ...
}

4,我想查看Activity的返回值,有没有更好的方式?
我们只需要将withResultCallBack设为true即可

@Route(requestCode = 1234, withResultCallBack = true)

对应生成的DefaultRouteApi

int REQUEST_CODE_TO_DETAIL_ACTIVITY = 1234;

@RouteActivity(
    value = DetailActivity.class,
    requestCode = REQUEST_CODE_TO_DETAIL_ACTIVITY
)
void navigateToDetailActivity(Context context, OnActivityResult onActivityResult);

可以看到上面的方法多了一个OnActivityResult类型的参数
然后我们就可以这样使用:

FssRouteApi.DEFAULT.navigateToDetailActivity(this, new OnActivityResult() {
            @Override
            public void onActivityResult(int requestCode, int resultCode, Intent data) {
                //在这个方法会在该Activity返回后调用
            }
        });

不过,要实现这样的功能,我们还需要在MainActivity里的onActivityResult方法里添加一行代码:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onActivityResult(int requestCode, int resultCode,@Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //这里必须添加,否则不会起作用
        FssRouter.onActivityResult(requestCode, resultCode, data);
    }
}

最后

总结一下:
一,FssRouter使用了类似retrofit的方式管理路由,路由信息清晰明了。
二,相同分组的路由声明在同一接口文件中,便于管理
三,使用时只需一行代码便可以实现路由,路由更方便。

如果你还有其他疑问或建议,欢迎与作者反馈,如果觉得不错,请点个赞,给颗星,谢谢。

更多FSS框架:

如果想要了解fss框架的具体使用,我为大家提供了demo工程

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

推荐阅读更多精彩内容