SogouModuleBus

目前,组件化开发基本已经成为每一个公司必选的开发方案。开源的组件化路由框架也有很多,例如ARouter,CC等,作为一个合格开发人员,熟练的掌握几款开发框架是必须的,网络上对其他的组件路由框架讲解有很多,但今天这篇文章带给大家的是一款新的组件间路由解耦框架--ModuleBus。

简单使用

当然对于一款组件化框架来说想一句代码接入那基本是不可能的,现在我们就来看看ModulesBus是如何接入的。

1.添加依赖和配置

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

repositories {
        jcenter()
    }
     
dependencies {
    classpath 'com.sogou.module:bus-gradle-plugin:1.2.3'
    }

2.模块build.gradle中配置组件化框架的版本

implementation "com.sogou.module:modulebus:1.2.7"

3.配置注解解释器
使用注解编译解释器肯定是为了将自定义注解在编译时期解释成Java代码,方便使用。
所以在需要使用注解的模块中添加注解解释器依赖如下:

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

apply plugin: 'kotlin-kapt'

向模块的build.gradle文件中dependencies部分添加

 implementation "com.sogou.module:annotationProcessor:1.2.0"
 //有kotlin支持时使用kapt依赖
 //如果不支持kotlin那么使用annotationProcessor依赖
 annotationProcessor "com.sogou.module:annotationProcessor:1.2.0" 
//kapt "com.sogou.module:annotationProcessor:1.2.0"

4.插件的接入,使用sogouplugin

在每个module的build.gradle中首行添加,并删除apply plugin: 'com.android.application' 或者 apply plugin: 'com.android.library'

//是主module添加,非主module则不添加
ext.mainApp = true  //设置为true,表示此module为主app module,以application方式编译
apply plugin: 'sogouplugin'

5.模块依赖

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

2.初始化SDK

当完成上述配置,ModuleBus的sdk其实已经导入。ModuleBus导入sdk的方式并不像其他的类库一样,通过implementation就可以导入,ModuleBus是在sogouplugin插件中自动读取版本号并添加相应的依赖。具体实现

在Application中完成sdk的初始化工作如下:

  • 不开启debug模式,不设置全局降级回调,拦截器等

    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);
    
  • 参数说明

类型 说明 备注
enableDebug Bolean true:开启log,跳转Exception抛出,false:以RouterCallback-ResultCode.FAILED状态回调
Interceptor IInterceptor实现类 全局拦截器,会在每次路由跳转时执行
GlobalDegrade GlobalDegrade实现类 全局降级接口,在不设置局部回调的情况下,未命中路由会回调此接口
RouterFactory RouterFactory实现类 手动设置路由列表

3.简单跳转

在完成上述基本配置和初始化后已经成功接入了ModuleBus,接下来看看如何使用它实现不同组件间的跳转吧。

  • 目标界面
    位于testmodule下的TestMainActivity,在支持路由的页面添加注解,注解没有特殊的格式要求,默认一般使用两级路劲,eg:”activity/test“
@RouterSchema("activity/test")
public class TestMainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_main);
    }
}
  • 跳转操作
    在跳转时也可以设置startActivityForResult的回调code,如下:
 public void funTestActivity(View view){
              RouterBus.getInstance()
              .build("activity/test")
              //.requestCode(1)
              .navigation(this);
    } 

4.带参跳转

一般的开启新的activity都是需要传递一些参数的,在传递对象行参数时,必须将此对象实现序列化接口。传参方式如下:

  • 目标界面
@RouterSchema("activity/param")
class ParamActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_param)
        val sb = StringBuilder("参数:\n")
        val bundle = intent.extras
        if (bundle != null) {
            val keySet = bundle.keySet()
            val iterator: Iterator<String> = keySet.iterator()
            while (iterator.hasNext()) {
                val key = iterator.next()
                var value: String?
                value = if (key  == "ObjTest") {
                    //获取可序列化数据
                    bundle.getSerializable(key).toString()
                } else {
                    bundle.getString(key)
                }
                sb.append(key).append(":").append(value).append(";\n")
            }
        }
        param.text = sb.toString();
    }
}
  • 跳转操作
public void funParamActivity(View view) {
        Bundle bundle = new Bundle();
        bundle.putString("ModuleBus", "paramTest");
        BeanInfo beanInfo = new BeanInfo();
        beanInfo.setName("objTest");
        RouterBus.getInstance()
                .build("activity/param")
                .with(bundle)
                .with("StringTest", "ParamString")
                .with("ObjTest", beanInfo)
                .navigation(this);
    }

5. 获取fragment

Fragment 传递参数的获取与activity基本相同,获取framgent和跳转的方法如下:

  • 目标界面
@RouterSchema("fragment/test")
public class TestFragment extends Fragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}
  • 跳转操作
 public void funFragment(View view){
        Fragment fragment = RouterBus.getInstance()
                .build("fragment/test")
                .getFragment();

        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment).commit();
    }

进阶使用

在组件化路由方案中并不单单涉及到avtivity/fragment的跳转,如何开启service也是比较常见的操作。还有比较常见的跳转拦截,启动失败后的降级处理,都是需要考虑的,那么接下来我们看看ModuleBus是如何处理的。

1.Manifest配置跳转

  • 跳转操作

    新建一个没有界面的UriActivituy用于监听<a href="test://uriactivity?uri=test/uriactivity">路由分发 test://uriactivity?uri=test/uriactivity</a>(rul的跳转)事件,之后直接把uri传递给RouterBus作为跳转的Schame完成跳转。

    <activity android:name=".UriActivituy">
                <intent-filter>
                    <!--action.VIEW和category.DEFAULT必须设置-->
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <!--如果需要浏览器支持打开,则category.BROWSABLE-->
                    <category android:name="android.intent.category.BROWSABLE"/>
                    <data
                        android:scheme="test"
                        android:host="uriactivity"/>
                </intent-filter>
            </activity>
    
    public class UriActivituy extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Uri uri = getIntent().getData();
            if (uri != null){
                String schema = uri.getQueryParameter("uri");
                              RouterBus.getInstance().build(schema).navigation(this);
            }
            finish();
        }
    }
    
  • 目标界面

    @RouterSchema("test/uriactivity")
    public class TestUriActivituy extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test_uri_activituy);
        }
    }
    

2.自定义全局降级策略

全局的降级策略出现是因为原生的路由方案存在无法灵活降级的问题StartActivity()一旦失败将会抛出运营级异常。当我们并不想应为启动失败而抛出异常,而是去选择执行另一个策略,那么就出现了全局的降级策略。

public class MyGlobalDegrade implements GlobalDegrade {
    @Override
    public void onMissed(RouterBuild build) {
        //dosomething
        //eg:
        RouterBus.getInstance()
                //打开传参测试的activity
                .build("activity/param")
                //参数设置为原本要打开的Schema
                .with("GlobalDegrade", build.getSchema())
                .navigation(MyApp.getApp());
    }
}

自定义全局降级策略类MyGlobalDegrade,在初始化ModuleBus的时候设置。

3.声明拦截器(拦截跳转过程)

原生的路由方案中存在的问题就是其无法在页面跳转的过程中插入一些自定义逻辑,而拦截器其实就是一种AOP的思想。在跳转过程中处理一些逻辑,例如:一个应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查。拦截器会在跳转之间执行,多个拦截器会按顺序依次执行。这就是拦截器的作用,下面看看ModuleBus是如何使用的:

  • 声明拦截器

    声明拦截器当当传递条件复合时进行处理,如果不符合那么不处理。

    @RouterSchema("/interceptor/test")
    class TestInterceptor : IInterceptor{
        override fun interceptor(p0: IInterceptor.Chain?) {
            val build: RouterBuild = p0!!.routerBuild
            build.with("localinterceptor", "Interceptor")
            val bundle = build.bundle
            val count = bundle.getInt("count")
            if (count % 2 == 0) {//符合拦截的条件
                val routerCallback = build.callback
                routerCallback?.result(ResultCode.INTERUPT, build)
            } else {
                p0.process()
            }
        }
    }
    
  • 注册拦截器
    在设置的callBack中处理具体的拦截逻辑。

    public void funInterceptor(View view) {
            IInterceptor interceptor = (IInterceptor) RouterBus.getInstance().build("/interceptor/test").navigation();
            RouterBus.getInstance()
                    .build("activity/param")
                    .with("count", 100)
                    .addInterceptor(interceptor)
                    .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;
    
                                case ResultCode.FAILED:
                                    Toast.makeText(MainActivity.this, "Router FAILED", Toast.LENGTH_SHORT).show();
                                    break;
                            }
                        }
                    });
        }
    

4.通过依赖注入解耦:服务管理

由于组件间可能相互调用,但是各个组件又都是项目独立的,所以需要将对外开放使用的对象抽象到base组件,抽象接口:

public interface IService {
    public void onNext(Context ctx);
}
  • 暴露服务
  1. 接口暴露法

    声明接口,其他组件通过接口来调用服务,在接口暴露时需要额外实现一个标识接口IExported

    • 接口声明
    public interface IService {
        public void onNext(Context ctx);
    }
    
    • 服务实现和暴露
    public class IServiceImpl implements IService, IExported {
        @Override
        public void onNext(Context ctx) {
            Toast.makeText(ctx, "执行onNext", Toast.LENGTH_SHORT).show();
        }
    }
    
    
    • 服务发现及调用
     public void funServiceInterface(View view){
            RouterBus.getInstance().navigation(IService.class).onNext(this);
        }
    

2.注解暴露法

  • 接口声明

    public interface IService {
        public void onNext(Context ctx);
    }
    
  • 服务实现和暴露

    @RouterSchema("service/test")
    class IServiceImpl2:IService {
        override fun onNext(ctx: Context?) {
            Toast.makeText(ctx, "执行onNext2", Toast.LENGTH_SHORT).show()
        }
    }
    
  • 服务发现及使用

```
 public void funServiceAnnotation(View view){
        IService service = (IService) RouterBus.getInstance().build("service/test").navigation();
        service.onNext(this);
    }
```

5.组件单独运行

上文中我们在插件的接入,使用sogouplugin中讲到,当一个mudule为主组件时我们需要设置ext.mainApp = true,因此,当我们使用一个mudule单独运行时,需要设置这个参数。具体操作如下:

  • 在module的build.gradle文件首行添加 ext.mainApp = true
  • 项目local.properties添加主module不需要编译进去的module,例如:

moduletest = true

- 在此module的builde.gradle通过addComponent添加对其他module的依赖,例如:
    ```groovy
addComponent "moduletest_kt", project(path: ':moduletest_kt')

总结

通过对ModuleBus的学习使用,相信我们对组件化框架的认识会进一步深入。当然ModuleBus的源码结构是非常清晰的,值得我们去学习。github开源地址:组件化解耦框架-ModuleBus

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352