(二)Android官方MVVM框架实现组件化之ARouter串联各模块
作者: Dawish_大D
: http://www.jianshu.com/u/40e1f22c2c53
(一)Android官方MVVM框架实现组件化之整体结构
(二)Android官方MVVM框架实现组件化之ARouter串联各模块
目前的项目结构图置顶:Demo的Github地址: https://github.com/Dawish/GoogleArchitectureDemo
0-演示项目MVVM组件化架构图ARouter
路由器简直是MVVM组件化的一个天赐之物,非常适合在组件化中使用,可以很方便滴获取Fragment、跳转Activity、拦截跳转、启动服务等等。
ARouter
官方地址:https://github.com/alibaba/ARouter
上面结构图中,module_girls
、module_news
和lib_commoin
中都可能存在Activity
,Fragment
或者Service
这些可以被ARouter
支持的组件,其实ARouter
的跳转是根据我们指定的路径去匹配的,只要路径是匹配的就可以跳转、获取或者是启动,最方便的是还支持参数携带,本文章只是讲一下在MVVM
中ARouter
最方便的使用场景,更多使用详情还请参考官方说明和官方Demo
,官方说得很清楚了。
一、组件化中ARouter使用配置注意事项
本演示项目中App
、Module
和lib
一个有七个,如果是你每一个都用到了ARoute
r的功能,那么就需要在每一个模块的build.gradle
中添加配置,记住是每一个。
android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : project.getName() ] } } }}dependencies { // 替换成最新版本, 需要注意的是api // 要与compiler匹配使用,均使用最新版可以保证兼容 compile 'com.alibaba:arouter-api:x.x.x' annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' ...}
如果你的模块需要打包成apk
或者是aar
之类的,并且你开取了代码混淆,那么需要在每一个模块的proguard-rules.pro
文件中添加代码keep
:
-keep public class com.alibaba.android.arouter.routes.**{*;}-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现-keep class * implements com.alibaba.android.arouter.facade.template.IProvider
二、Activity的跳转和拦截
首先我们需要在公共库 lib_common
中添加一个类来存放ARouter
的跳转获取使用的path
:
package google.architecture.common.base;/** * Created by danxx on 2017/11/27. * 路由path * * Aty : Activity * Fgt : Fragment * *//** * Created by danxx on 2017/11/27. * 路由path * * Aty : Activity * Fgt : Fragment * */public class ARouterPath { /**妹子列表Activity*/ public static final String GirlsListAty = "/girls/aty/list"; /**妹子列表动态Activity*/ public static final String DynaGirlsListAty = "/girls/dynaty/list"; /**新闻列表Activity*/ public static final String NewsListAty = "/news/aty/list"; /**妹子列表Fragment*/ public static final String GirlsListFgt = "/girls/aty/fgt/list"; /**新闻列表Fragment*/ public static final String NewsListFgt = "/news/fgt/list"; /**关于Fragment*/ public static final String AboutFgt = "/about/fgt/fragment";}
需要被跳转的Activity增加Router注解,就是声明已下自己的path路径:
@Route(path = ARouterPath.GirlsListAty)public class ActivityGirls extends BaseActivity { GirlsAdapter girlsAdapter; ActivityGirlsBinding activityGirlsBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle("Module_ActivityGirls"); getSupportActionBar().setDisplayHomeAsUpEnabled(true); activityGirlsBinding = DataBindingUtil.setContentView(ActivityGirls.this,R.layout.activity_girls); GirlsViewModel girlsViewModel = new GirlsViewModel(ActivityGirls.this.getApplication()); girlsAdapter = new GirlsAdapter(girlItemClickCallback); activityGirlsBinding.setRecyclerAdapter(girlsAdapter); subscribeToModel(girlsViewModel); } GirlItemClickCallback girlItemClickCallback = new GirlItemClickCallback() { @Override public void onClick(GirlsData.ResultsBean fuliItem) { Toast.makeText(ActivityGirls.this, fuliItem.getDesc(), Toast.LENGTH_SHORT).show(); } }; /** * 订阅数据变化来刷新UI * @param model */ private void subscribeToModel(final GirlsViewModel model){ //观察数据变化来刷新UI model.getLiveObservableData().observe(this, new Observer() { @Override public void onChanged(@Nullable GirlsData girlsData) { Log.i("danxx", "subscribeToModel onChanged onChanged"); model.setUiObservableData(girlsData); girlsAdapter.setGirlsList(girlsData.getResults()); } }); } @Override protected void onResume() { super.onResume(); }}
有兴趣的可以看一下ActivityGirls 的布局文件:
<?xml version="1.0" encoding="utf-8"?>
ARouter
的拦截很简单的,写一个类实现IInterceptor
接口,用Interceptor
注解写上拦截器的等级和名字就可以了,不需要额外的去注册拦截器,在ARouter
执行跳转时会先执行拦截器。关于java
AOP
注解的使用请看我之前的文章《大话AOP与Android的爱恨情仇》
/** * Created by danxx on 2017/11/27. * * 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查 * 拦截器会在跳转之前执行,多个拦截器会按优先级顺序依次执行 * * priority就是优先级 可以设置多个级别的拦截器都活一次执行 * 创建一个实现IInterceptor接口的类就是一个拦截器,不用做额外的配置了 */@Interceptor(priority = 8, name = "测试用拦截器")public class RouterInterceptor implements IInterceptor { @Override public void process(Postcard postcard, InterceptorCallback callback) { if(postcard.getPath().contains(ARouterPath.GirlsListAty)){ Log.d("danxx", "拦截到向ActivityGirls跳转"); //自定义处理 }else { Log.d("danxx", "非拦截跳转执行path: "+postcard.getPath()); } callback.onContinue(postcard); // 处理完成,交还控制权 // callback.onInterrupt(new RuntimeException("我觉得有点异常")); // 觉得有问题,中断路由流程 // 以上两种至少需要调用其中一种,否则不会继续路由 } @Override public void init(Context context) { // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次 Log.d("danxx", "RouterInterceptor init"); }}
跳转的时候就厉害了哦,可以支持很多的参数携带,Activity
的跳转还可以自动以转场动画:
private ItemClick itemClick = new ItemClick() { @Override public void onClick(int id) { switch (id){ case R.id.toGirls: Log.i("danxx", "onClick toGirls"); //跳转到GirlsActivity ARouter.getInstance() .build(ARouterPath.GirlsListAty) /**可以针对性跳转跳转动画*/ .withTransition(R.anim.activity_up_in, R.anim.activity_up_out) .navigation(ActivityMain.this); break; case R.id.toNews: Log.i("danxx", "onClick toNews"); //跳转到NewsActivity ARouter.getInstance() .build(ARouterPath.NewsListAty) /**可以针对性跳转跳转动画*/ .withTransition(R.anim.activity_up_in, R.anim.activity_up_out) /**设置跳转回到*/ .navigation(ActivityMain.this, 2, new NavigationCallback() { @Override public void onFound(Postcard postcard) { Log.i("danxx", "ARouter onFound 找到跳转匹配路径"); } @Override public void onLost(Postcard postcard) { Log.i("danxx", "ARouter onLost 没有匹配到跳转路径"); } @Override public void onArrival(Postcard postcard) { Log.i("danxx", "ARouter onArrival 成功跳转"); } @Override public void onInterrupt(Postcard postcard) { Log.i("danxx", "ARouter onInterrupt 跳转被中断"); } }); break; case R.id.toDynamic: Log.i("danxx", "onClick toNews"); //跳转到ActivityDynamicGirls (模拟动态url) ARouter.getInstance() .build(ARouterPath.DynaGirlsListAty) .withString("fullUrl", "http://gank.io/api/data/%E7%A6%8F%E5%88%A9/20/1") .withTransition(R.anim.activity_up_in, R.anim.activity_up_out) /**支持携带requestCode参数,跳转回调onActivityResult方法*/ .navigation(ActivityMain.this, 3); break; } } };
三、Fragment的获取
看一下需要被获取Fragment:
/** * @Desc FragmentGirls */@Route(path = ARouterPath.GirlsListFgt)public class FragmentGirls extends BaseFragment { FragmentGirlsBinding girlsBinding; GirlsAdapter girlsAdapter; private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private String mParam1; private String mParam2; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ARouter.getInstance().inject(FragmentGirls.this); girlsBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_girls,container,false); girlsAdapter = new GirlsAdapter(girlItemClickCallback); girlsBinding.setRecyclerAdapter(girlsAdapter); final GirlsViewModel girlsViewModel = new GirlsViewModel(getActivity().getApplication()); subscribeToModel(girlsViewModel); return girlsBinding.getRoot(); } GirlItemClickCallback girlItemClickCallback = new GirlItemClickCallback() { @Override public void onClick(GirlsData.ResultsBean fuliItem) { Toast.makeText(getContext(), fuliItem.getDesc(), Toast.LENGTH_SHORT).show(); } }; /** * 订阅数据变化来刷新UI * @param model */ private void subscribeToModel(final GirlsViewModel model){ //观察数据变化来刷新UI model.getLiveObservableData().observe(FragmentGirls.this, new Observer() { @Override public void onChanged(@Nullable GirlsData girlsData) { Log.i("danxx", "subscribeToModel onChanged onChanged"); model.setUiObservableData(girlsData); girlsAdapter.setGirlsList(girlsData.getResults()); } }); }}
获取Fragment
的时候也很单纯:
BaseFragment fragmentNews = (BaseFragment) ARouter.getInstance().build(ARouterPath.NewsListFgt).navigation();BaseFragment fragmentGirls = (BaseFragment) ARouter.getInstance().build( ARouterPath.GirlsListFgt).navigation();BaseFragment fragmentAbout = (BaseFragment) ARouter.getInstance().build( ARouterPath.AboutFgt ).navigation();
四、ARouter所谓的服务获取
一开始看到这个,我还以为是获取Android中
的Service
,其实不是的,ARouter
所谓的服务就是直接或者是间接实现ARouter
提供的IProvider
接口的类。
首先我们可以先写一个继承IProvider
接口的接口,我们可以在自己的接口中增加自己想要的方法:
/** * Created by danxx on 2017/11/28. * * ARouter所谓的服务就是直接或者是间接实现ARouter提供的IProvider接口的类 * */public interface TestService extends IProvider { /**增加自己想要的方法*/ String sayHello(String name);}
实现这个接口:
/** * Created by danxx on 2017/11/28. * 实现了一个测试用的服务接口 */@Route(path = "/service/test", name = "测试服务")public class TestServiceImpl implements TestService { @Override public String sayHello(String name) { Log.d("danxx", "TestServiceImpl sayHello : "+name); return "TestServiceImpl TestServiceImpl TestServiceImpl"; } @Override public void init(Context context) { Log.d("danxx", "TestServiceImpl TestServiceImpl init"); }}
使用的时候可以有多种方式了:
服务声明:
@Autowired(name = "/service/test") TestService testService1; TestService testService2; TestService testService3;
服务使用:
//注入才可以自动初始化Autowired注解声明的变量 ARouter.getInstance().inject(ActivityMain.this); //注解的方法 testService1.sayHello("Autowired invoke 233"); //类寻找的方法 testService2 = ARouter.getInstance().navigation(TestService.class); testService2.sayHello("navigation invoke 233"); //路径匹配的方式 testService3 = (TestService) ARouter.getInstance().build("/service/test").navigation(); testService3.sayHello("build invoke 233");
官方推荐注解的方式,比较靠谱一些。
MVVM中使用ARouter优点总结:
个模块之间更加低耦合,各模块之间不需要关注彼此,要埋头开发自己的功能就行了。
Activity
的跳转、Fragment
的获取、ARouter所谓的Service
的获取,都变得很隐式,执行之前不知道对方具体是谁,只要对方注册了对应的path
路径就可以了。支持
Activity
的跳转的拦截,我测试似乎不支持Fragment
的获取拦截,有些遗憾,在不满足情况的条件下模块可以对外屏蔽。支持
URL
跳转,方便H5
和原生混合开发。
...
五、无耻的预告
下一篇讲解DataBinding在MVVM中的使用,关于更多的ARouter请参考官方说明和官方Demo:
ARouter Github地址: https://github.com/alibaba/ARouter
示例工程Demo地址:https://github.com/Dawish/GoogleArchitectureDemo
更多相关文章
- Android非常好用的组件或者框架
- Android的四大组件之三--Activity(1)----->生命周期
- Android 6.0 新特性(官方文档翻译)
- Android Studio自动化快速实现Parcelable接口序列化
- Android官方使低版本系统(2.1)支持ActionBar的方法
- Android 接口定义语言AIdl
- Google SwipeRefreshLayout(Goolge官方下拉刷新控件)尝鲜