Router

Project Url: yjfnypeu/Router
Introduction: A simple router lib to android
More: Author   ReportBugs   
Tags:
Android Router-Android 路由规则-

一款简单的,支持在单品、组件化、插件化等环境下使用的路由框架。

什么是 router 框架

简单来说.即通过一行 url 去指定打开指定页面 Activity 的框架.充分做到页面间解耦.

我希望的 router 框架所能支持的功能

  • 自动化 可自动解析标准 url 参数
  • 安全: 路由启动过程中。全程 catch 住异常并通知用户。完全不用担心 crash 问题。
  • 强大的拦截器:与大部分的路由不同。提供三种路由拦截器机制,对应不同业务下使用。
  • 方便: 使用 apt 注解生成路由表,配置方便,易维护。
  • 灵活: 配置路由表方式多样,满足你在任意条件下进行使用。
  • 支持两种路由:页面路由与动作路由。
  • 支持重启路由:路由被拦截后。可通过一行代码无缝恢复重启路由。在登录检查中会很有用。
  • 高度可定制:单品、组件化完美支持,对于插件化环境。也可以针对性的定制使用。

版本依赖

  1. 添加 JitPack 仓库
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  1. 添加依赖

LatestVersion=

compile "com.github.yjfnypeu.Router:router-api:$LatestVersion"
annotationProcessor "com.github.yjfnypeu.Router:router-compiler:$LatestVersion"

用法

为 Activity 添加路由规则

  • 指定路由前缀与路由表生成包名
@RouteConfig(
    baseUrl = "haoge://page/", // 路由前缀:用于结合 RouterRule 合成完整的路由链接
    pack = "com.haoge.studio") // 路由表生成包名:配置后。路由表生成类将会放置于此包下
class App extends Application {...}
  • 为目标页指定路由规则链接
// 在目标 Activity 上添加 RouterRule 注解,添加对应的路由规则
// 同一目标页可以添加多个不同的路由链接。
@RouterRule({
    // 可只指定 path, 此种数据会与 RouteConfig 中的 baseUrl 进行结合:
    // 最终完整链接为:haoge://page/example
    "example", 
    // 也可直接指定完整链接
    "total://example"
    })
class ExampleActivity extends BaseActivity { ... }

注册路由表

经过上面的配置后,编译后即可生成对应的路由表类,生成的路由表类名统一为RouterRuleCreator:

然后即可通过以下代码进行路由表注册使用:

RouterConfiguration.get().addRouteCreator(new RouterRuleCreator())

启动路由

还是以上面的 example 为例。要使用 Router 启动 ExampleActivity, 使用以下链接进行跳转

Router.create("haoge://page/example").open(context)

启动浏览器打开网页

当路由链接为 http/https 时,且此时本地的也没有页面配置过此链接地址时,将触发使用跳转浏览器打开链接逻辑

比如浏览器打开百度页面

Router.create("https://www.baidu.com").open(context)

启用内部日志打印

Router.DEBUG = true

启用后,即可通过 RouterLog 进行过滤查看

添加额外数据启动

Bundle extra = getExtras();
Router.create(url)
    .addFlags(flag) // 添加启动标记位,与 Intent.addFlags(flag)相同
    .addExtras(extra) // 添加额外数据:Intent.addExtras(bundle)
    .requestCode(code) // 使用 startActivityForResult 进行启动
    .setAnim(in, out) // 设置转场动画。Activity.overridePendingTransition(inAnim, outAnim)
    .open(context)

使用路由回调

路由回调为RouteCallback接口,用于在进行路由启动后,对该次路由事件的状态做回调通知:

public interface RouteCallback {
    // 当用于启动的路由链接未匹配到对应的目标页时。回调到此
    void notFound(Uri uri, NotFoundException e);
    // 当启动路由成功后,回调到此。可在此通过 rule.getRuleClz()获取对应的目标页类名。
    void onOpenSuccess(Uri uri, RouteRule rule);
    // 当启动路由失败后,回调到此。
    void onOpenFailed(Uri uri, Throwable e);
}

路由回调的配置分为两种:

  1. 全局路由回调:所有的路由启动事件均会回调到此
RouterConfiguration.get().setCallback(callback)
  1. 临时路由回调:只对当次路由事件生效
Router.create(url).setCallback(callback).open(context)

路由回调机制在进行界面路由跳转埋点时,是个很好的特性。

有时候我们会需要在回调中使用启动时添加的额外数据,而回调的 api 中并没有提供此数据,所以此时我们需要使用以下方法进行额外数据获取:

此方法只能在回调方法内调用,运行完回调方法后会自动清空。
RouteBundleExtras extras = RouterConfiguration.get().restoreExtras(uri);

使用 ActivityResultCallback

ActivityResultCallback接口用于自动处理 onActivityResult 逻辑,可有效避免在 onActivityResult 中写一堆的判断 switch 逻辑。是个很棒的特性。

public interface ActivityResultCallback {
    void onResult(int resultCode, Intent data);
}

使用此特性前,需要在 BaseActivity 中的 onActivityResult 方法处,添加上派发方法:

RouterConfiguration.get()
    .dispatchActivityResult(this, requestCode, resultCode, data)

然后即可直接使用

// 添加了 resultCallback 属性后,即可不指定 requestCode 了。免去了取值的烦恼
Router.create(url).resultCallback(resultCallback).open(context)

使用路由拦截器拦截器

拦截器,顾名思义,就是在路由启动过程中,进行中间状态判断,是否需要拦截掉此次路由事件。使其启动失败。

拦截器的接口名为RouteInterceptor

public interface RouteInterceptor{
    // 在此进行状态判断。判断是否需要拦截此次路由事件,当返回 true 时,代表此次启动事件被拦截
    boolean intercept (Uri uri, RouteBundleExtras extras, Context context);
    // 当 intercept 方法返回 true 时,此方法被调用。
    void onIntercepted(Uri uri, RouteBundleExtras extras, Context context);
}

Router 经过长期的迭代,对拦截器进行了详细的分类,提供了三种拦截器提供使用:

1. 全局拦截器:对所有的路由事件生效。

RouterConfiguration.get().setInterceptor(interceptor);

2. 单次拦截器:对当次路由事件生效。

Router.create(url)
    // 是的你没有看错,可以配置多个不同的拦截器实例
    .addInterceptor(interceptor1)
    .addInterceptor(interceptor2)
    .open(context);

3. 指定目标的拦截器:对指定的目标页面生效

// 在配置的 RouterRule 的目标页,添加此 RouteInterceptors 注解即可。
// 在此配置的拦截器,当使用路由启动此页面时,即可被触发。
@RouteInterceptors({CustomRouteInterceptor.class})
@RouterRule("user")
public class UserActivity extends BaseActivity {...}

恢复路由的方式

既然路由可以被拦截,那么也可以直接被恢复。

Router.resume(uri, extras).open(context);

光这样看有点不太直观。我们举个最经典的登录检查拦截案例作为说明:

当不使用路由进行跳转时,这种情况就会导致你本地写上了大量的登录判断逻辑代码。这在维护起来是很费劲的。而且也非常不灵活,而使用拦截器的方式来做登录检查,就会很方便了:

下面是一个简单的登录拦截实现:

// 实现 RouteInterceptor 接口
public class LoginInterceptor implements RouteInterceptor{
    @Override
    public boolean intercept(Uri uri, RouteBundleExtras extras, Context context){
        // 未登录时进行拦截
        return !LoginChecker.isLogin();
    }

    @Override
    public void onIntercepted(Uri uri, RouteBundleExtras extras, Context context) {
        // 拦截后跳转登录页并路由信息传递过去,便于登录后进行恢复
        Intent loginIntent = new Intent(context,LoginActivity.class);
        // uri 为路由链接
        loginIntent.putExtra("uri",uri);
        // extras 中装载了所有的额外配置数据
        loginIntent.putExtra("extras",extras);
        context.startActivity(loginIntent);
    }
}
public class LoginActivity extends BaseActivity {

    @Arg
    Uri uri;
    @Arg
    RouteBundleExtras extras;

    void onLoginSuccess() {
        if(uri != null) {
            // 登录成功。使用此方法直接无缝恢复路由启动
            Router.resume(uri, extras).open(context);
        }
        finish();
    }
}

自动解析传递 url 参数

Router 支持自动从 url 中解析参数进行传递:

Router.create("haoge://page/user?username=haoge&uid=123456")
    .open(context);

上面的链接即代表:跳转到haoge://page/user页面,并传递 username 和 uid 数据过去。

结合 Parceler 框架使用

Parceler 框架是我另一款超轻量型的 Bundle 数据处理框架,具备良好的兼容性。

将 Router 与 Parceler 结合进行使用。可做到 自动匹配目标页的数据类型进行传递的效果。

以上方的带参数链接为例:解析后传递到目标页的数据全部是 String 类型的

key value type
username haoge String
uid 123456 String

这样在数据传递的时候就比较麻烦,因为你如果要传递其他类型的数据,还得需要到目标页自己去手动转换一下。

使用 Arg 注解

解决方法就是使用 Arg 注解, 比如如下页面:

@RouterRule("parceler-args")
class ArgsActivity:BaseActivity() {

    // 为对应参数的成员变量添加 Arg 注解
    // 基本数据类型
    @Arg
    var mBoolean:Boolean? = null
    @Arg
    var mInt:Int? = null
    @Arg
    var mByte:Byte? = null
    @Arg
    var mShort:Short? = null
    @Arg
    var mChar:Char? = null
    @Arg
    var mFloat:Float? = null
    @Arg
    var mLong:Long? = null
    @Arg
    var mDouble:Double? = null
    // 默认类型 String
    @Arg
    var mString:String? = null
    // 普通实体 bean
    @Arg
    var mUser: User? = null
    // 子链接
    @Arg
    var mUrl:String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 从 intent 中读取数据,注入到 Arg 注解的成员变量中去。
        Parceler.toEntity(this, intent)
    }
}

然后此时使用 Router 往此页面跳转,传值。将会自动进行转换,比如我们使用如下方式进行传递:

val url = Uri.parse("haoge://page/parceler-args")
    .buildUpon()
    // 添加基本数据类型
    .appendQueryParameter("mBoolean", "true")
    .appendQueryParameter("mByte", "0")
    .appendQueryParameter("mShort", "1")
    .appendQueryParameter("mChar", "c")
    .appendQueryParameter("mInt", "3")
    .appendQueryParameter("mFloat", "3.14")
    .appendQueryParameter("mDouble", "3.14")
    .appendQueryParameter("mLong", "5")
    .appendQueryParameter("mString", "HaogeStudio")
    // 非可序列化对象可通过 json 格式传递
    .appendQueryParameter("mUser", JSON.toJSONString(User("HaogeStudio")))
    // 转义字符串。比如参数中需要传递网址时
    // appendQueryParameter 本身会将数据先进行转义后再拼接上。所以此处是转义的链接
    .appendQueryParameter("mUrl", "https://www.baidu.com")
    .build()

Router.create(url).open(this)

这样就很简单的达到了自动匹配目标页的数据类型进行传递目的。

使用动作路由

上面主要介绍的页面跳转的路由,也叫页面路由,但实际上。有的时候我们使用路由启动的,并不是需要启动某个页面。而是需要执行一些特殊的操作:比如添加购物车强制登出等。此时就需要使用动作路由了。

创建动作路由

动作路由通过继承ActionSupport类进行创建:

// 与页面路由一样。添加 RouterRule 注解配置路由链接即可。
@RouterRule("action/hello")
public class SayHelloAction extends ActionSupport {
    @Override
    public void onRouteTrigger(Context context, Bundle data) {
        //  启动动作路由成功会触发调用此方法。
        Toast.makeText(context, "Hello! this is an action route!", Toast.LENGTH_SHORT).show();
    }
}

动作路由的启动方式与页面路由一致:

Router.create("haoge://page/action/hello").open(context)

指定动作路由的执行线程

动作路由是用于执行一些特殊的操作的路由,而有时候部分操作是需要在指定线程进行处理的:

动作路由提供两种指定线程的操作:

  1. 启动前进行配置(优先级高):
Router.create(url).setExecutor(executor).open(context);
  1. 在定制动作路由时,直接指定线程:
@RouteExecutor(CustomExecutor.class)
@RouterRule("action/hello")
public class SayHelloAction extends ActionSupport {...}

在没有配置过线程切换器时。默认使用 MainThreadExecutor。指定线程为主线程

在目标页获取启动链接

// 先从目标页读取 bundle 数据
Bundle bundle = getBundle();
// 然后从 bundle 中读取即可
Uri lauchUri = bundle.getParcelable(Router.RAW_URI);

使用对象路由

对象路由(InstanceRouter)是在 Router 2.8.+版本以上添加的新型路由,主要作用为通过指定路由链接,创建出具体的对象实例提供使用:

对象路由的配置方式是与页面路由,动作路由类似。也是直接在指定类上添加 RouterRule 注解,如此处将 UserFragment 作为实例创建目标:

@RouterRule("haoge://page/fragment/user")
class UserFragment extends Fragment 
        // 实现 ICreatorInjector 接口。复写方法以接收传参
        implements ICreatorInjector{

    @Override
    public void inject(Bundle bundle) {
        // 接收传参
    }
}

然后即可通过路由链接启动并获取 UserFragment 实例:

UserFragment user = Router.createInstanceRouter("haoge://page/fragment/user")
        .addExtras(bundle)// 也可以添加额外参数
        .createInstance<UserFragment>()// 获取具体实例进行使用

当然,不限于 Fragment,你也可以为其他的任意类(除ActivityActionSupport)添加上对象路由的配置,比如一个简单的普通 bean:

// 对任意类添加路由配置注解
@RouterRule("haoge://pojo/user")
class Uesr implements ICreatorInjector {
    @Override
    public void inject(Bundle bundle) {
        // 接收传参
    }
}

// 然后通过指定的链接,直接获取实例
User user = Router.createInstanceRouter("haoge://pojo/user")
            .createInstance<User>()

更多介绍

基本使用方式说明,请参考Router:一款单品、组件化、插件化全支持的路由框架

插件化中的使用方式,请参考Router: 教你如何进行任意插件化环境下的路由适配

更新日志

查看 releases

联系作者

email: 470368500@qq.com

安卓交流会所

或者手动加入 QQ 群: 108895031

License

Copyright 2015 Haoge

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools