(二)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_girlsmodule_newslib_commoin 中都可能存在Activity,Fragment或者Service这些可以被ARouter支持的组件,其实ARouter的跳转是根据我们指定的路径去匹配的,只要路径是匹配的就可以跳转、获取或者是启动,最方便的是还支持参数携带,本文章只是讲一下在MVVMARouter最方便的使用场景,更多使用详情还请参考官方说明和官方Demo,官方说得很清楚了。


一、组件化中ARouter使用配置注意事项

本演示项目中AppModulelib一个有七个,如果是你每一个都用到了ARouter的功能,那么就需要在每一个模块的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<GirlsData>() {
            @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"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ActivityGirls">

    <data>

        <import type="android.view.View"/>

        <variable
            name="girlsViewModel"
            type="google.architecture.coremodel.viewmodel.GirlsViewModel"/>
        <variable
            name="recyclerAdapter"
            type="android.support.v7.widget.RecyclerView.Adapter"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/cardview_light_background"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/girls_list_wrapper">

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/girls_list"
                    android:contentDescription="girls list"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:adapter="@{recyclerAdapter}"
                    app:layoutManager="LinearLayoutManager" />
            </FrameLayout>
        </FrameLayout>
    </LinearLayout>

</layout>

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<GirlsData>() {
            @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优点总结:

  1. 个模块之间更加低耦合,各模块之间不需要关注彼此,要埋头开发自己的功能就行了。

  2. Activity的跳转、Fragment的获取、ARouter所谓的Service 的获取,都变得很隐式,执行之前不知道对方具体是谁,只要对方注册了对应的path路径就可以了。

  3. 支持Activity的跳转的拦截,我测试似乎不支持Fragment的获取拦截,有些遗憾,在不满足情况的条件下模块可以对外屏蔽。

  4. 支持URL跳转,方便H5和原生混合开发。

...

五、无耻的预告

下一篇讲解DataBinding在MVVM中的使用,关于更多的ARouter请参考官方说明和官方Demo:

ARouter Github地址: https://github.com/alibaba/ARouter

示例工程Demo地址:https://github.com/Dawish/GoogleArchitectureDemo

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