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工程

推荐阅读更多精彩内容