Android 路由实践(一)

本文介绍了在复杂的安卓应用中实现模块间路由的三种方法:隐式Intent、路由表初始化及注解方式。每种方法都有其优缺点,适用于不同的场景。

前言

         随着app的业务越来越复杂,如果不分模块的话,维护性和扩展性简直惨不忍睹,举个栗子,项目有登录模块和用户模块,业务需求是登录之后展示用户信息,这个时候登录模块是拿不到用户模块任何类的引用的,可能你会说那就引入进来不就好了,对于小的项目还好说,对于大的项目,如果各个模块互相引用,那么模块化就没有意义了,同级的模块互相引用导致,有时候需求只需要更改一个模块的东西,但是因为耦合,却要编译好多模块,亦可赛艇。所以这就出现了路由。来看一张图:


现在的需求是从loginLibrary中的登录界面跳转到userInfoLibrary的用户信息界面,用于loginLibrary和userInfoLibrary互相不可见因此不能直接跳转,这个时候我们注意到loginLibrary和userInfoLibrary都引用了commmLib模块,那我就拿这个做文章,把commmLib当做桥梁,在loginLibrary和userInfoLibrary之间建立联系,这是典型的桥接模式。这种模式在现实生活中很常见,比如房产中介。这就安卓的路由,下面我们分三种方式来实现安卓的路由:

第一种:隐式的Intent

我们知道安卓中activity之间的跳转,除了startActivity(intent)这种显式的跳转之外,还可以再AndroidManifest.xml中通过intentFilter的配置,通过匹配规则来实现跳转:

看下代码:

/**
 * 通过action实现的路由
 */
private void jumpByAction() {
    Intent intent = new Intent();
    intent.setAction(ActionConstant.ACTION_USER_INFO);
    intent.putExtra(ActionConstant.KEY_USER_NAME, mEtUserName.getText().toString());
    intent.putExtra(ActionConstant.KEY_PASS_WORD, mEtPwd.getText().toString());
    if (ActionConstant.isIntentAvailable(this, intent)) {
        startActivity(intent);
    }
}
/**
 * 通过Schema实现的路由
 */
private void jumpByScheme() {
    Intent intent = new Intent();
    intent.setAction(ActionConstant.ACTION_USER_INFO);
    intent.putExtra(ActionConstant.KEY_USER_NAME, mEtUserName.getText().toString());
    intent.putExtra(ActionConstant.KEY_PASS_WORD, mEtPwd.getText().toString());
    intent.setData(Uri.parse("http://www.baidu.com:80/login"));
    intent.getCategories();
    if (ActionConstant.isIntentAvailable(this, intent)) {
        startActivity(intent);
    }
}复制代码

这里主要实现了通过Action和Scheme的隐式跳转,注意的是通过隐式跳转的Activity注册的时候一定要加上<category android:name="android.intent.category.DEFAULT" />否则奔溃如下:

Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.USERINFO (has extras) }复制代码

第二种:通过初始化路由表的方式实现

public class RouterManagerByMap {
    private static RouterManagerByMap instance = new RouterManagerByMap();
    private ArrayMap<String, Class> routers = new ArrayMap<>();

    private RouterManagerByMap() {
    }

    public static RouterManagerByMap getInstance() {
        return instance;
    }

    public void addRouter(String key, Class activity) {
        routers.put(key, activity);
    }

    public Class getRouter(String key) {
        return routers.get(key);
    }
}复制代码

RouterManagerByMap维护了路由表,在RouterApp初始化的时候将需要跳转的Activity以KeyValue的形式,存起来,如下:

RouterManagerByMap.getInstance().addRouter(ActionConstant.ACTION_USER_INFO, UserInfoActivity.class);复制代码

这要的话使用的时候

RouterManagerByMap.getInstance().getRouter(ActionConstant.ACTION_USER_INFO)

就可以获取到对应的要跳转的类了。

第三种:通过注解

其实第三种和第二种差不多,只不过初始化理由表的实现方式不一样,是通过注解实现的,来看下初始化代码:

public class RouterByAnnotationManager {
    private static RouterByAnnotationManager instance = new RouterByAnnotationManager();
    private ArrayMap<String, Class> routers = new ArrayMap<>();

    private RouterByAnnotationManager() {
    }

    public static RouterByAnnotationManager getInstance() {
        return instance;
    }

    private void addRouter(String key, Class activity) {
        routers.put(key, activity);
    }

    public Class getRouter(String key) {
        return routers.get(key);
    }
    
    public  void init(Application application) {
        try {
            //通过资源路径获得DexFile,注意5.0以上版本要求关掉instant run 方法否则会自动拆包遍历不到所有的类
            DexFile e = new DexFile(application.getPackageCodePath());
            Enumeration entries = e.entries();
            //遍历所有元素
            while (entries.hasMoreElements()) {
                String entryName = (String) entries.nextElement();
                //匹配Activity包名
                if (entryName.contains("activity")) {
                    //通过反射获得Activity类
                    Class entryClass = Class.forName(entryName);
                    if (entryClass.isAnnotationPresent(RouterTarget.class)) {
                        RouterTarget action = (RouterTarget) entryClass.getAnnotation(RouterTarget.class);
                        RouterByAnnotationManager.getInstance().addRouter(action.value(), entryClass);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}复制代码

注意的是:通过资源路径获得DexFile,注意5.0以上版本要求关掉instant run 方法否则会自动拆包遍历不到所需要activity类

这里定义一个运行时注解:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface RouterTarget {
    String value();
}复制代码

对注解不是很熟悉的朋友可以先去网上看看,用法如下图:



怎么样是不是很方便。

总结

以上三种方式都可以实现路由的跳转,但是第一种由于安卓不支持在代码里面设置Activity的intentFilter,所以造成一个问题:比如药跳转的UserInfoActivity的action是“android.intent.action.USERINFO”为了能够匹配setAction()和UserInfoActivity的AndroidManifest.xml中的要严格匹配,而安卓不支持在代码里面设置Activity的intentFilter,所以不能通过ActionConstant来统一维护,小项目还好,一旦项目大起来,那么就很难保证匹配。第三种看起来使用起来很方便,但是有两个问题:

1.通过遍历反射射包下所有的类,效率低下;

2.注意到DexFile已经打上了废弃标志,不出意外接下来的安卓版本可能会不支持,有风险

所以建议使用第二种。代码地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值