目标:降低不同module进行交互时的沟通成本,让开发人员更容易做正确的事情。
- 不同module都能接收Application的onCreate等生命周期回调进行本module的初始化
- 不同module之间的页面跳转,自动打包传递参数
- 不同module之间的数据交互,接口参数对开发友好可见
- 页面跳转支持所有Intent可配置的参数
- 支持页面跳转拦截
- 自动绑定并注入到框架
- 支持Multdex
- 支持单接口多module多实现方案
在项目根目录的build.gradle文件中添加classpath依赖
buildscript { dependencies { classpath 'bamboo.components.stitch:stitch-gradle-plugin:1.2' } } 在需要使用stitch的module的build.gradle文件中加入:
//apply plugin: 'com.android.application' apply plugin: 'com.android.library' //需要放在android的plugin后面,目前只支持与application及library的plugin共同使用 apply plugin: 'stitch.plugin'ComponentLife类是组件生命周期代理基类,我们通过继承ComponentLife类并使用@Component进行标记,即可将我们的组件注入到stitch中。
每一个Module只允许有1个@Component标注类,如果标注了多个,只会有一个生效。一个显然已经满足需求了,^_^
具体使用方法:
如果Module不需要在Application的onCreate、attachBaseContext等生命周期时进行初始化操作,不需要自定义ComponentLife类。
@Component public class TestComponetLife extends ComponentLife { /* * 代理Application的OnCreate方法 */ public void onCreate(){ //TODO 进行需要的初始化操作 } public void attachBaseContext(Context baseContext){ } } //获取TestComponetLife对象 TestComponetLife testComponent = Stitch.searchComponentApplication(TestComponetLife.class); //获取Application对象 Application application = testComponent.getApplication(); //或者 Application application = Stitch.getApplication();在Application里面我们需要主动传递Application的生命周期给stitch,stitch提供了两种实现方式:
1. 直接继承StitchApplication
StitchApplication继承自Application,只是添加了stitch的生命周期调用
2. 通过Stitch调用组件的生命周期。
如果不能直接继承StitchApplication,在你自己的Application中主动调用Stitch.onCreate()、Stitch.attachBaseContext()等方法,具体可参考StitchApplication的实现。
@Exported注解用于将Module中的Activity开放给其他Module调用,
ActivityPage 用于与开放的Activity进行关联,同时它实现了Serializable接口,所以也作为Activity的数据传递的封装类使用。
在实际使用之前,我们需要新建1个公用Module作为路由Module,这里我们假设我们所有的Module都依赖于名为Router的Module,而我们开发的Module名为ModuleA,其他Module名为ModuleB
每一个需要共享给其他Module使用的Activity都需要与一个ActivityPage进行关联,其他Module通过ActivityPage即可与Activity进行交互。
//1.在Router中创建TestPage.java public class TestPage extends ActivityPage{ public final String text; public TestPage(Context context,String text) { super(context); this.text = text; } } //2.在ModuleB中启动TestPage,开发ModuleB的人不需要知道TestPage对应到具体哪个页面 Stitch.start(new TestPage(context,"text test")); //或者使用 new TestPage(context,"text test").start(); //3.在ModuleA中创建TestActivity并关联TestPage @Exported(TestPage.class) public class TestActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { ... //接收页面启动时传递的参数,TestPage的simpleClassName为参数key TestPage infoPage = (TestPage) getIntent().getSerializableExtra("TestPage"); mTestTextView.setText(infoPage.text); } }public void onActionTest(View view) { new ActionTestPage(context) .setTargetIntent(new Intent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) .start(); //如果需要在Activity接收返回值 new ActionTestPage(context) .setTargetIntent(new Intent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) .setRequestCode(100) .startForResult(); }在页面交互时,有时候我们需要往下一个页面传递参数,stitch默认支持Serializable,同时也支持Parcelable方式
如果你定义的TestPage实现了Parcelable接口,stitch会自动切换为Parcelable方式进行参数传递。
// 默认的Serializable方式 public class TestPage extends ActivityPage { .... // 不想传递的参数用transient描述 public transient final int noParamer = 100; } // Parcelable方式,需要自己写序列化方法 public class TestPage extends ActivityPage implements Parcelable { .... }@Intercept用来作为@Exported的补充,在某些情况下,Module对外公开的是一个页面入口,但是在不同的条件下可能该入口打开的页面并不是同一个。
public class InterceptTest{ @Intercept public static void receive(TestPage page){ if (AppUtils.isHuaweiChannel()) { Intent intent = Stitch.pack(page); intent.setClass(page.context, TestActivityA.class); page.context.startActivity(intent); } else { Intent intent = Stitch.pack(page); intent.setClass(page.context, TestActivity.class); page.context.startActivity(intent); } } }@Service注解用于向stitch中注入Module对外公开的实现接口。
// 1.在 Router 中创建 ITestService.java public interface ITestService { String getTestText(); } // 2. 在 ModuleA 中创建 TestServiceImp.java 并实现 ITestService @Service public class TestServiceImp implements ITestService { public String getTestText() { return "hello world!"; } } // 3. 在 ModuleB 中使用该接口 public class TestServiceTest { public void test() { ITestService testService = Stitch.searchService(ITestService.class); //如果组件没有引用(未打包到apk中)时,testService为null String testText = testService == null ? "" : testService.getTestText(); } }在不同module想要调用其他module对外公开的页面,就需要知道有哪些页面被公开或公开的页面叫什么名称。同样的,想要调用其他module对外公开的接口,也需要知道接口是什么以及在哪个service中。为解决这个问题,stitch提供了整合ActivityPage及Service的功能。
implementation 'bamboo.components.stitch:stitch-router-anno:1.2' annotationProcessor 'bamboo.components.stitch:stitch-router-compiler:1.2'将所有的ActivityPage统合到ActivityPageManager中,我们只需要通过ActivityPageManager就可以知道有哪些页面是全局可见的。
@Wrapper public class TestPageA extends ActivityPage { @Parameter public Strint text; private String param; public TestPageA(Context context) { super(context); } @Parameter("param") public void setParam(String param){ this.param = param; } } @Wrapper public class TestPageB extends ActivityPage { public TestPageB(Context context,String text) { super(context); this.text = text; } } //使用时通过ActivityPageManager访问 ActivityPageManager.newTestPageA(context).setText("test text").setParam("param test").setRequestCode(1000).startForResult(); ActivityPageManager.newTestPageB(context,"test text").start();将所有Service实现的接口方法统合ServiceManager中,可跳过Class层直接调用到方法。
@Wrapper public interface ITestService { String getTestText(); //容易混淆或重名的方法,用@Alias指定别名 @Alias("getTestServiceName") String getName(); } @Wrapper public interface ITestService2 { //容易混淆或重名的方法,用@Alias指定别名 @Alias("getTestService2Name") String getName(); } //调用时通过ServiceManager直接访问方法 String text = ServiceManager.getTestText(); String testName = ServiceManager.getTestServiceName(); String testName2 = ServiceManager.getTestService2Name(); //设定默认值,如果接口没有实现或者实现的module没有被打包到Apk,接口会返回默认值 //原始数据类型默认值是其数据值的最小值,boolean默认值为false,其他引用类型为null,如果要覆盖这个设定可以通过以下方式 String defaultText = "defaultText"; //如果getTestText没有实现,会返回defaultText String text = ServiceManager.getTestText(defaultText);