×
广告

Android路由框架Router分析

96
FullStack
2017.01.17 18:43* 字数 1375

Android路由框架Router

什么是路由?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能。

路由框架的意义

Android系统已经给我们提供了api来做页面跳转,比如startActivity,为什么还需要路由框架呢?我们来简单分析下路由框架存在的意义:

  1. 在一些复杂的业务场景下,灵活性比较强,很多功能都是动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,但如果事先做了约定,提前做好页面映射,便可以自由配置。
  2. 随着业务量的增长,客户端必然随之膨胀,开发人员的工作量越来越大,比如64K问题,比如协作开发问题。App一般都会走向组件化的道路,而组件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系。
  3. 简化代码。数行跳转代码精简成一行代码。
  4. 其他...

特性

Router有哪些特性或者优点呢?

  1. 基于注解,使用方便,源码简洁
  2. 链式调用,api友好
  3. 多路径支持
  4. 结果回调,每次跳转都会回调跳转结果
  5. 编译期处理注解,不影响运行时性能
  6. 除了可以使用注解定义路由,还支持手动分配路由
  7. 自定义拦截器,可以对路由进行拦截,比如登录判断和埋点处理
  8. 自定义路由匹配规则,相比较其他路由框架,该项目并没有限制路由的写法,除了内置的几个匹配器,用户完全可以定义自己的规则
  9. 支持隐式Intent跳转
  10. 支持多模块使用,支持组件化开发
  11. 不仅支持注解Activity,还支持注解Fragment
  12. 支持Kotlin

其他功能正在添加中...

集成

router-gradle-plugin router router-compiler
最新版本
[站外图片上传中……(3)]

集成过程也可参考项目主页README

  1. 在项目级的build.gradle中加入依赖:
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.x ↑'
        classpath 'com.chenenyu.router:gradle-plugin:latest.integration'
    }
}

注意,Router需要使用2.2.0及以上版本的Android gradle plugin来处理注解,截至写作时,最新版本为2.3.3

  1. 在module级的build.gradle中使用plugin:
apply plugin: 'com.android.application/library'
apply plugin: 'com.chenenyu.router'

至此,集成工作就完成了,简单的两步:添加插件路径和应用插件。这应该是类似框架中最简单的集成方式了。

使用

  1. Router需要初始化,用于初始化路由表,建议放到Application中做:
public class App extends MultiDexApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        // 开启log,要放到前面才能看到初始化过程的log
        if (BuildConfig.DEBUG) {
            Router.openLog();
        }
        // 初始化
        Router.initialize(this);
    }
}
  1. 添加路由注解
// 单路径注解
@Route("test")
public class TestActivity extends Activity {
  ...
}

// 多路径注解,这几个注解都能打开该Activity
@Route({"user", "example://user", "http://example.com/user"})
public class UserActivity extends Activity {
  ...
}

@Route("fragment")
public class TestFragment extends Fragment {
  ...
}
  1. 发起路由操作
// 最简单的路由跳转,打开TestActivity
Router.build("test").go(context);

// 其他部分api
Router.build("test")
   .requestCode(int) // 调用startActivityForResult
   .with(bundle)  // 携带跳转参数
   .addFlags(flag)  // 添加标记,比如intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
   .anim(enter, exit)  // 添加跳转动画
   .callback(calback)  // 跳转结果回调
   .go(this);

全部可用API可参考IRouter接口。

  1. startActivityForResult

Router.build(uri).requestCode(int).go(this); 其中requestCode一定得是一个非负数。


go()有如上几个重载方法,如果是在Fragment中发起startActivityForResult路由操作,切记传当前fragment实例哦!不然在fragment中是接受不到回调的。

  1. 添加跳转动画
// 1. 基础动画
Router.build(uri).anim(enter, exit).go(this);
// 2. 转场动画ActivityOptions
Router.build(uri).activityOptions(options).go(this);
  1. 获取目标页面
// 1. 获取intent,然后可以操作intent
Intent intent = Router.build(uri).getIntent(context);
// 2. 获取fragment,然后可以将该fragment添加到Activity中
Fragment fragment = (Fragment) Router.build(uri).getFragment(context);
  1. 全局拦截器
// 1. 添加全局拦截器
Router.addGlobalInterceptor(routeInterceptor);
// 2. 跳过全局拦截器
Router.build(uri).skipInterceptors().go(this);
  1. 添加拦截器

拦截器是Router的功能之一,作用就是在执行路由之前判断是否需要拦截该次路由请求。比如可以在拦截器中做登录状态判断。

// 定义拦截器(通过注解)
@Interceptor("SampleInterceptor")
public class SampleInterceptor implements RouteInterceptor {
    @Override
    public boolean intercept(Context context, @NonNull Uri uri, @Nullable Bundle extras) {
      // 返回true表示拦截当前路由  
      return true;
    }
}
// 定义拦截器(通过代码)
Router.handleInterceptorTable(new InterceptorTable() {
    @Override
    public void handle(Map<String, Class<? extends RouteInterceptor>> map) {
        map.put("SampleInterceptor", SampleInterceptor.class);
    }
});

// 应用拦截器(通过注解)
@Route(value = "test", interceptors = "SampleInterceptor")
public class TestActivity extends AppCompatActivity {
    ...
}
// 应用拦截器(通过代码)
Router.handleTargetInterceptors(new TargetInterceptors() {
    @Override
    public void handle(Map<Class<?>, String[]> map) {
        map.put(TestActivity.class, new String[]{"SampleInterceptor"});
    }
});
  1. 自定义路由

除了可以使用注解来添加路由外(上面步骤2介绍的方式),还可以通过代码手动控制路由表。

// 动态添加路由表
Router.handleRouteTable(new RouteTable() {
    @Override
    public void handle(Map<String, Class<?>> map) {
        map.put("dynamic1", TestActivity.class);
        map.put("dynamic2", TestFragment.class);
        ...
    }
});

该方式与注解没有冲突,可以同时使用。

  1. 路由匹配规则

该功能是Router最大的特色功能,不同于其他框架,Router并没有规定路由的写法规则,而是抽象出Matcher的概念,交给用户去控制。但是Router仍然内置了4个常用的Matcher

MatcherRegistry

匹配优先级从高到低依次是DirectMatcherSchemeMatcherImplicitMatcherBrowserMatcher,关于原理将在后序的原理篇中进行讲解。
假设有如下路由页面:

@Route({"user", "http://example.com/user"})
public class UserActivity extends Activity {
    ...
}

除了可以通过Router.build("test").go(this)Router.build("http://example.com/user").go(this)打开UserActivity之外(DirectMatcher命中),还可以通过Router.build("http://example.com/user?id=9527&status=0").go(this)打开(通过SchemeMatcher命中),并且自动帮你配置了Bundle参数,即:

@Route({"user", "http://example.com/user"})
public class UserActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Bundle bundle = getIntent().getExtras();
        String id = bundle.getString("id");
        String status = bundle.getString("status");
    }
}

Matcher支持配置多个,会根据优先级依次进行匹配。

  1. 参数注入

上面讲了可以通过路由传递参数,然后在目标页面通过Bundle获取,其实这个过程也被Router简化了。通过@InjectParam注解可以为Activity或者Fragment的成员变量添加参数注入

@Route({"test", "http://example.com/test", "router://test"})
public class TestActivity extends AppCompatActivity {
    @InjectParam
    int id = 123;
    @InjectParam(key = "status")
    private String sts = "default"; // 不建议使用private修饰符

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Router.injectParams(this); // 实现参数注入
    }
}
  • @InjectParam会在Bundle中取出对应key的值传给成员变量,默认key为变量名,也可以通过key=""属性指定
  • 参数注入支持变量的默认值,目前支持默认值的变量类型有基本数据类型,String,CharSequence,其他类型的默认值都是null
  • 变量不建议使用private修饰符,因为私有的变量会采用反射的方式注入参数
  • 需要使用Router.injectParams(this)来实现最终的参数注入

Router还有其他一些人性化的小功能,在这里就不一一介绍了,有问题可以在项目主页提issue。后续会给大家讲解一下背后的实现原理。

总结

Router是一个十分小巧灵活的路由框架,代码设计也很优雅简洁,且完美支持组件化开发,目前仍在不断地迭代中,源码地址为https://github.com/chenenyu/Router,欢迎各位试用点评。

Android
Web note ad 1