Android中路由的实现

来源:互联网 发布:w3cschool java教程 编辑:程序博客网 时间:2024/04/29 13:48

本文和大家分享的主要是android开发中路由的实现相关方法,希望通过本文的分享,对大家学习android有所帮助。

前几个月有幸参加了CSDN组织的MDCC移动开发者大会,一天下来我最大的收获就是了解到了模块化开发,回来之后我就一直在思考模块化的一些优点,不说别的,提供一种可插拔的开发方式就足够我们兴奋一会了~接下来自己开始尝试了一些小demo,发现在模块化开发中最大的问题就是组件间通讯,例如: 在模块化架构中, 商城和个人中心分别是两个独立的模块,在开发阶段,个人中心如何想要跳转商城的某个页面咋办?这里就需要引入一个路由的概念了.做过web开发的都知道,在大部分web框架中url路由也是框架中很重要的组成部分

一张图来体会一下路由的作用, 因为我本地没有UML工具,新的还在下载中… 900M+,我这网速有点受不了.所以我选择KolourPaint手动绘制一张具有魔性的图片先来体会一下.


自己实现一个路由的动机

  那到了我们Android开发中呢?如果我们把项目模块化了,那两个组件间进行通讯或者跳转,我们一般构建Intent的方式就不再使用了,很简单,因为在模块A中根本找不到模块B中的C,这就需要我们自定义路由规则,绕一道弯去进行跳转,说白了就是给你的类起一个别名,我们用别用去引用.其实在我准备自己去实现一个路由的时候我是google了一些解决方案的,这些方案大致可分为两种.

1. 完全自己实现路由,完全封装跳转参数

2. 利用隐式意图跳转

  对于这两种方式我总结了一下, 个人认为第一种方式封装的太多, 甚至有些框架是RESTFul like,这样的封装一是学习成本太高,二是旧项目改动起来太麻烦.那第二种方式呢?利用隐式意图是一种不错的选择,而且Android原生支持,这也是大家在尝试模块化开发时的一个选择,不过这种方式仅支持Activity, Service, BroadcastReceiver,扩展性太差.综上因素,我还是决定自己实现一个路由,参考自上面的局限性,我们的路由具有一下2个特点.

1. 上手简单,目标是与原生方式一行代码之差就能实现Activity, Service, BroadcastReceiver调用.

2. 扩展性强,开发者可以任意添加自己的路由实现,不仅仅局限于Activity, Service, BroadcastReceiver.

体验一下

  在了解具体实现代码之前, 我们先来了解一下新的路由怎么使用, 使用起来是不是符合上面两点,首先我们先建立三个moduler,分别是壳app,商城模块shoplib, bbs模块bbslib. app模块就是我们的壳了,我们需要利用app模块去打包,而且app也是依赖shoplibbbslib,所以我们可以在appapplication里进行路由的注册.

public class App extends Application {

@Override

public void onCreate() {

super.onCreate();

setupRouter();

}

private void setupRouter() {

Router.router(ActivityRule.ACTIVITY_SCHEME + "shop.main", ShopActivity.class);

Router.router(ActivityRule.ACTIVITY_SCHEME + "bbs.main", BBSActivity.class);

}

}

  这里注册了两个路由, 分别是商城模块的的ShopActivitybbs模块的BBSActivity,它们都是通过 Router 类的静态方法 router 方法进行注册的,两个参数, 第一个参数是路由地址(也可以理解成别名),第二个参数对应的类.注册完了, 那接下来就是如何使用了,我们来看看在商城模块如何跳转BBS模块吧.

public class ShopActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv = new TextView(this);

tv.setTextSize(50);

tv.setText("SHOP!!!");

setContentView(tv);

tv.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent it = Router.invoke(ShopActivity.this, ActivityRule.ACTIVITY_SCHEME + "bbs.main");

startActivity(it);

}

});

}

}

  主要代码是在click事件里,我们调用了 Router.invoke 方法,第一个参数是当前Activity,第二个参数就是我们前面注册的路由了,这里都很好理解,关键是看它的返回值,这里直接返回了一个Intent,这一点是最棒的~返回Intent也就是说明下面的代码和我们使用原生方式没有任何区别!这一点符合上面我们说到的 上手简单 的目的.

 至于第二点目标, 高扩展性 ,大家可以实现 Rule 接口自定义路由Rule,然后调用 Router.addRule(String scheme, Rule rule) 方法进行路由规则的注册. Rule接口的定义如下,

/**

* 路由规则接口

* Created by qibin on 2016/10/8.

*/

public interface Rule<T, V> {

/**

* 添加路由

* @param pattern 路由uri

* @param klass 路由class

*/

void router(String pattern, Classklass);

/**

* 路由调用

* @param ctx Context

* @param pattern 路由uri

* @return {@code V} 返回对应的返回值

*/

V invoke(Context ctx, String pattern);

}

 解释一下, 首先是Rule接口的两个范型,第一个T是我们注册的路由类型,例如前面使用的Activity类型,第二个Vinvoke 方法的返回值类型, 例如前面使用的Intent类型.至于自定义的代码,这里我赖了~,没有提供demo~~~大家可以尝试自定义一下.

路由实现代码

  接下来我们开始进入实现代码环节~ 在来是代码之前, 还是先来一张图了解下这个Router 的结构.


带着上面的图片, 我们来看代码,首先我们来看看Router,毕竟我们在使用的时候都是在和Router打交道.

/**

* Usage:

*

 

* step 1. 调用Router.router方法添加路由

* step 2. 调用Router.invoke方法根据pattern调用路由

*

* Created by qibin on 2016/10/9.

*/

public class Router {

/**

* 添加自定义路由规则

* @param scheme 路由scheme

* @param rule 路由规则

* @return {@code RouterInternal} Router真实调用类

*/

public static RouterInternal addRule(String scheme, Rule rule) {

RouterInternal router = RouterInternal.get();

router.addRule(scheme, rule);

return router;

}

/**

* 添加路由

* @param pattern 路由uri

* @param klass 路由class

* @return {@code RouterInternal} Router真实调用类

*/

public static RouterInternal router(String pattern, Class<T>klass) {

return RouterInternal.get().router(pattern, klass);

}

/**

* 路由调用

* @param ctx Context

* @param pattern 路由uri

* @return {@code V} 返回对应的返回值

*/

public static V invoke(Context ctx, String pattern) {

return RouterInternal.get().invoke(ctx, pattern);

}

}

 哈, Router的代码很简单,主要就是起到一个类似静态代理的作用,主要的代码还是在RouterInternal ,那来看看 RouterInternal 的结构吧.

public class RouterInternal {

private static RouterInternal sInstance;

/** scheme->路由规则 */

private HashMap<STRING, rule="">mRules;

private RouterInternal() {

mRules = new HashMap<>();

initDefaultRouter();

}

/**

* 添加默认的ActivityServiceReceiver路由

*/

private void initDefaultRouter() {

addRule(ActivityRule.ACTIVITY_SCHEME, new ActivityRule());

addRule(ServiceRule.SERVICE_SCHEME, new ServiceRule());

addRule(ReceiverRule.RECEIVER_SCHEME, new ReceiverRule());

}

/*package */ static RouterInternal get() {

if (sInstance == null) {

synchronized (RouterInternal.class) {

if (sInstance == null) {

sInstance = new RouterInternal();

}

}

}

return sInstance;

}

}

  首先 RouterInternal 是一个单例,一个 mRules 变量用来保存我们的路由规则,在构造中我们注册了三个默认的路由规则,这三个路由规则想都不用想就知道是Activity, ServiceBroadcastReceiver.接下来看看其他的方法.

/**

* 添加自定义路由规则

* @param scheme 路由scheme

* @param rule 路由规则

* @return {@code RouterInternal} Router真实调用类

*/public final RouterInternal addRule(String scheme, Rule rule) {

mRules.put(scheme, rule);

return this;

}

addRule 方法是添加路由规则的实现, 这里我们是直接向 mRules 这个 HashMap 中添加的.

private<T, v="">Rule<T, v="">getRule(String pattern) {

HashMap<STRING, rule="">rules = mRules;

SetkeySet = rules.keySet();

Rule<T, v="">rule = null;

for (String scheme : keySet) {

if (pattern.startsWith(scheme)) {

rule = rules.get(scheme);

break;

}

}

return rule;

}

getRule 的作用是根据 pattern 来获取规则,这是一个私有的方法, 所以在使用的时候不需要关心,它的原理很简单,就是根据你的 pattern 来匹配scheme 来获取对应的Rule .

/**

* 添加路由

* @param pattern 路由uri

* @param klass 路由class

* @return {@code RouterInternal} Router真实调用类

*/public final RouterInternal router(String pattern, Class<T> klass) {

Rule<T,>rule = getRule(pattern);

if (rule == null) {

throw new NotRouteException("unknown", pattern);

}

rule.router(pattern, klass);

return this;

}

  这个 router 方法就是我们添加路由的实现了,首先我们根据路由的uri来获取对应的Rule ,然后调用该 Rule router 方法, 至于 Rule.router 方法如何实现的,我们稍后看~

/**

* 路由调用

* @param ctx Context

* @param pattern 路由uri

* @return {@code V} 返回对应的返回值

*//*package*/ final V invoke(Context ctx, String pattern) {

Rule rule = getRule(pattern);</?,>

if (rule == null) {

throw new NotRouteException("unknown", pattern);

}

return rule.invoke(ctx, pattern);

}

invoke 方法就是我们调用的时候执行的代码的, 返回值 是返回的 Rule 范型中指定的类型,例如前面的 Intent .

  综上代码, 我们发现 RouterInternal 其实就是一个管理 Rule 的类,具体的调用还是在各个 Rule 中实现,上面提到过, Rule 是一个接口, 它具有两个范型,分别对应的调用 invoke 的返回值类型和我们要路由的类的类型.解析来我们就来看看默认的几个路由规则是如何实现的.

  对于Activity, Service, BroadcastReceiver的调用,总结了一下,它们其实都是返回的 Intent 类型,所以我们可以先构建一个指定返回值是 Intent Base类型.

/**

* 返回Intent的路由规则的基类

* Created by qibin on 2016/10/9.

*/

public abstract class BaseIntentRule<T> implements Rule<T, Intent> {

private HashMap<String, Class> mIntentRules;

public BaseIntentRule() {

mIntentRules = new HashMap<>();

}

/**

* {@inheritDoc}

*/

@Override

public void router(String pattern, Classklass) {

mIntentRules.put(pattern, klass);

}

/**

* {@inheritDoc}

*/

@Override

public Intent invoke(Context ctx, String pattern) {

Classklass = mIntentRules.get(pattern);

if (klass == null) { throwException(pattern);}

return new Intent(ctx, klass);

}

/**

* 当找不到路由规则时抛出异常

* @param pattern 路由pattern

*/

public abstract void throwException(String pattern);

}

router 方法不多说, 还是向 Map 中添加键值对, invoke 方法,我们通过参数中的 pattern  mIntentRules 目标类,然后构建一个 Intent 返回,最后一个 throwException 是一个抽象方法,用来在调用没有 router 的类时抛出异常用~,可以发现, 其实大部分的实现在这里都实现了,对于Activity继承这个BaseIntentRule ,并且指定要路由类的类型是Activity,并且实现 throwException 方法就可以了.

/**

* activity路由规则

* Created by qibin on 2016/10/8.

*/

public class ActivityRule extends BaseIntentRule {

/** activity路由scheme*/

public static final String ACTIVITY_SCHEME = "activity://";

/**

* {@inheritDoc}

*/

@Override

public void throwException(String pattern) {

throw new ActivityNotRouteException(pattern);

}

}

ActivityRule 首先继承了 BaseIntentRule 并指定了范型是 Activity ,实现的 throwException 方法也很简单,就是抛出了一个 ActivityNotRouteException 异常,对于这个异常, 大家可以在文章最后的源码下载部分找到~看完 ActivityRule 的实现,其实其他两个默认 Rule 的实现都一样了~大家也是自己去看代码吧.

  其实实现一个路由很简单, 原理就是给我们要路由的类定义一个别名, 然后在调用的地方通过别名去调用.而且在封装的时候尽量要符合现在用户的使用习惯,不要过多的封装而忽略了使用者的感受.

 

来源:Loader's Blog


0 1
原创粉丝点击