Skip to content
This repository has been archived by the owner on Aug 21, 2021. It is now read-only.

Latest commit

 

History

History
366 lines (301 loc) · 12.7 KB

README.md

File metadata and controls

366 lines (301 loc) · 12.7 KB

ModuleBus

    一款简单易用的组件化解耦框架

版本

每次升级,将在此更新最新版本号

bus-gradle-plugin: 1.2.3
modulebus: 1.2.7
annotationProcessor: 1.2.0

一、功能介绍

  1. 支持注解添加URI路由进行跳转,并解析参数
  2. 支持跳转时设置各种参数,例如requestCode、Intent.FLAG、转场动画、Bundle参数等
  3. 支持manifest->data->schema配置的跳转
  4. 支持系统Action的隐式跳转
  5. 支持局部和全局降级
  6. 支持获取fragemnt以及传递参数
  7. 支持拦截器
  8. 自动处理组件运行方式
  9. 提供IDE插件便捷的关联路径和目标类

二、接入方法

  1. 工程build.gradle中添加GradlePlugin依赖

    repositories {
        jcenter()
    }
    
    dependencies {
        classpath 'com.sogou.module:bus-gradle-plugin:xx.xx.xx'
    }
  2. 在模块的build.gradle中配置组件化框架的版本

    implementation "com.sogou.module:modulebus:xx.xx.xx"
  3. 配置注解解释器

    在需要使用注解的模块中添加注解解释器依赖

    如果该模块是普通的android module,则只需向模块的build.gradle文件中dependencies部分添加

    implementation "com.sogou.module:annotationProcessor:xx.xx.xx"
    implementation "com.sogou.module:modulebus:xx.xx.xx"
    annotationProcessor "com.sogou.module:annotationProcessor:xx.xx.xx"

    如果该模块有kotlin支持,首先要在模块的build.gradle引入kapt插件

    apply plugin: 'kotlin-kapt'

    然后在dependencies中增加如下依赖

    implementation "com.sogou.module:annotationProcessor:xx.xx.xx"
    kapt "com.sogou.module:annotationProcessor:xx.xx.xx"
  4. 使用gradle插件-bus-gradle-plugin

    在每个module的build.gradle中首行添加

    如果是主module添加

    ext.mainApp = true  //设置为true,表示此module为主module,以application方式编译
    apply plugin: 'module-bus-extensions'

    非主module添加

    apply plugin: 'module-bus-extensions'

    删除apply plugin: 'com.android.application'或者apply plugin: 'com.android.library'

  5. 模块依赖

    主module依赖的业务组件使用bus-gradle-plugin提供的addComponent方法,可以屏蔽各业务组件对主module的可见性。
    也就是说使用`implementation project(path: ":login")`,主module仍然可以直接引用login模块的代码,但是使用`addComponent "login"`,
    login模块的实现对主module却是不可见的。
    

三、sdk初始化

  1. 在Application中做sdk的初始化工作

    RouterBus.getInstance().init(this);

    上面这种初始化不开启debug模式、不设置全局降级回调。

    RouterBus.Builder builder = new RouterBus.Builder()
                .enableDebug(BuildConfig.DEBUG)
                .addInterceptor(new GlobalInterceptor())
                .setGlobalDegrade(new GlobalDegrade() {
                        @Override
                        public void onMissed(RouterBuild build) {
                            Toast.makeText(MyApplication.this, "GlobalDegrade:"+build.getSchema(), Toast.LENGTH_SHORT).show();
                        }
                });
        RouterBus.getInstance().init(this, builder);

    参数说明:

    1. enableDebug参数为true(开发阶段)会开启log输出,并会将跳转过程中的Exception抛出;
        如果为false(线上环境)则会try-catch异常,并以RouterCallback-`ResultCode.FAILED`状态回调。
    2. addInterceptor为全局拦截器,会在每次路由跳转时执行
    3. GlobalDegrade为全局降级接口,在不设置局部回调的情况下,未命中路由会回调此接口。
    

四、使用路由跳转

  1. 首先需要在目标Activity上使用注解标识路由

    @RouterSchema({"login://login"})
    该注解参数为数组,因此支持同一个Activity配置多个路由
    
  2. 调用方示例如下:

    // 1. 应用内简单的跳转
    RouterBus.getInstance()
                        .build("login://login")
                        .requestCode(66)
                        .navigation(this);
    
    // 2. 跳转并携带参数
    Bundle bundle = new Bundle();
                bundle.putString("name", "seasonfif");
    RouterBus.getInstance()
                .build("login://login?age=29&num=215603")
                .with(bundle)
                .with("pwd", "123456")
                .with("user", new User("Sam", 33))
                .navigation();
                
    // 由该示例可以看出支持的参数有URI get参数、Bundle参数、基础类型以及序列化对象。
    
    // 3. 处理跳转结果
    // 使用两个参数的navigation方法,可以获取单次跳转的结果
    RouterBus.getInstance()
                .build("login://login")
                .navigation(this, new RouterCallback() {
                    @Override
                    public void result(int code, RouterBuild build) {
    
                        switch (code){
                            case ResultCode.SUCCEED:
                                Toast.makeText(MainActivity.this, "Router SUCCEED", Toast.LENGTH_SHORT).show();
                                break;
                            case ResultCode.MISSED:
                                Toast.makeText(MainActivity.this, "Router MISSED", Toast.LENGTH_SHORT).show();
                                break;
                            case ResultCode.FAILED:
                                Toast.makeText(MainActivity.this, "Router FAILED", Toast.LENGTH_SHORT).show();
                                break;
                        }
                    }
                });

五、服务调用

  1. 使用接口类型调用

    // 声明接口,其他组件通过接口来调用服务
    public interface ILoginService {
        void login(Context ctx);
    }
    
    // 实现接口
    public class LoginServiceImpl implements ILoginService, IExported {
        @Override
        public void login(Context ctx) {
            Toast.makeText(ctx, "from module login", Toast.LENGTH_SHORT).show();
        }
    }

    实现接口时必须额外实现一个空接口-IExported,标识此实现类为导出调用服务。

    下面为接口的调用方法

    RouterBus.getInstance().navigation(ILoginService.class).login(this);

    如果上步中没有实现IExported标记接口,则服务不会导出,此处调用login会crash

  2. 使用注解标识调用

    // 声明接口,其他组件通过接口来调用服务
    public interface ILoginService {
        void login(Context ctx);
    }
    
    // 实现接口
    @RouterSchema("service/login")
    public class LoginServiceImpl implements ILoginService {
        @Override
        public void login(Context ctx) {
            Toast.makeText(ctx, "from module login", Toast.LENGTH_SHORT).show();
        }
    }

    下面为接口的调用方法

    ILoginService loginService = (ILoginService) RouterBus.getInstance().build("service/login").navigation();
    loginService.login(this);

六、获取fragment

  1. 注册fragment
    // 与路由的注册一样,都是通过注解
    @RouterSchema("f/tempfragment")
    public class TempFragment extends Fragment {
    
    }
  2. 获取fragment
    //支持的参数有URI get参数、Bundle参数、基础类型以及序列化对象。
    Bundle b1 = new Bundle();
            b1.putString("name", "seasonfif");
    Fragment fragment = RouterBus.getInstance()
            .build("f/tempfragment?a=1&b=2")
            .with(b1)
            .with("age", 29)
            .with("user", new User("Sam", 33))
            .getFragment();
    在fragment中通过getArguments()来获取设置的参数。

七、拦截器使用

  1. 全局拦截器与局部拦截器

    顾名思义,全局拦截器对每一个跳转都起作用,而局部拦截器只对设置的地方起作用。如果同时设置了全局拦截器与局部拦截器,则会优先执行局部拦截器。

    全局拦截器在初始化SDK时添加,见三、sdk初始化

    局部拦截器添加如下:

    // 1. 使用注解名添加
    RouterBus.getInstance()
            .build(Constants.LOGIN_SCHEMA)
            .addInterceptor("/interceptor/LoginInterceptor")
            .navigation(this);
    
    // 2. 使用拦截器对象添加
    IInterceptor interceptor = (IInterceptor) RouterBus.getInstance().build("/interceptor/LoginInterceptor").navigation();
    RouterBus.getInstance()
            .build(Constants.LOGIN_SCHEMA)
            .addInterceptor(interceptor)
            .navigation(this);
  2. 拦截器回调

    如果跳转时设置了路由回调RouterCallback,则可在拦截器中获取此对象,将拦截器处理的情况回调出来。

    跳转代码

    RouterBus.getInstance()
            .build(Constants.LOGIN_SCHEMA)
            .addInterceptor("/interceptor/LoginInterceptor")
            .navigation(this, new RouterCallback() {
                @Override
                public void result(int resultCode, RouterBuild build) {
                    switch (resultCode){
                        case ResultCode.INTERUPT:
                            Toast.makeText(MainActivity.this, "Router INTERUPTed", Toast.LENGTH_SHORT).show();
                            break;
                    }
                }
            });

    拦截器代码

    @RouterSchema("/interceptor/LoginInterceptor")
    public class LoginInterceptor implements IInterceptor {
        @Override
        public void interceptor(Chain chain) {
            ILoginService loginService = (ILoginService) RouterBus.getInstance().build("service/login").navigation();
            loginService.login(chain.getContext());
            RouterBuild build = chain.getRouterBuild();
            // 拦截跳转 额外添加一些参数
            build.with("localinterceptor", "Interceptor");
            build.with("user", new User("Sam", 18));
            
            // 获取传递的原始参数
            Bundle bundle = build.getBundle();
            int count = bundle.getInt("count");
            if (count % 2 == 0){
                //拦截本次跳转行为,并将结果以ResultCode.INTERUPT状态回调
                RouterCallback routerCallback = build.getCallback();
                if (routerCallback != null){
                    routerCallback.result(ResultCode.INTERUPT, build);
                }
            }else{
                //不拦截跳转
                //若要继续执行请调用chain.process()
                chain.process();
            }
        }
    }

八、动态路由

  1. 动态路由工厂在初始化sdk时添加

    RouterBus.Builder builder = new RouterBus.Builder()
                .enableDebug(BuildConfig.DEBUG)
                .addInterceptor(new GlobalInterceptor())
                .setRouterFactory(new RouterFactory() {
                    @Override
                    public void putDynamicRouter(Map<String, String> routerTable) {
                        // 如果key与已有的路由相同,则会覆盖已有的路由
                        routerTable.put(Constants.LOGIN_SCHEMA, "com.sogou.profile.ProfileActivity");
                    }
                });
        RouterBus.getInstance().init(this, builder);

    注意:为了确保动态路由的时效性,工厂方法putDynamicRouter会在每次跳转时调用,因此不要在此做耗时操作。

九、开发工具

  1. 插件导航到路由对应的目标类

    使用 AndroidStudio 手动安装插件方式安装bus-idea-plugin.zip ,插件位于attachment文件夹中,安装后插件无任何设置,可以在跳转代码的行首找到一个图标 (navigation) 点击该图标,即可跳转到标识了代码中路径的目标类

十、混淆

  1. 发布release版本需要添加以下混淆配置

    -dontwarn com.sogou.annotationprocessor.**
    
    -keep class com.sogou.modulebus.** { *; }
    
    -keep class * implements com.sogou.modulebus.routerbus.IInterceptor
    
    -keep class * implements com.sogou.modulebus.routerbus.IRouterRegistry
    
    -keep public class * extends com.sogou.modulebus.functionbus.IExported
    
    -keep class com.sogou.annotation.RouterSchema {
        *;
    }
    
    -keep @com.sogou.annotation.RouterSchema class * {
        *;
    }