组件化

258 阅读19分钟

1、模块化

在Android Studio中,新建工程默认有一个App module,然后还可以通过File->New->New Module 新建module。那么这里的“module” 和实际说的“模块”基本是一个概念了。也就是说,原本一个 App模块承载了所有的功能,而模块化就是拆分成多个模块放在不同的Module里面,每个功能的代码都在自己所属的module中添加。

屏幕快照 2021-05-05 下午3.25.04.png

0760C321-5722-4214-8BB0-8B05B3808C51.png

1、模块划分

已京东为例,大致可以分为 “首页”、“分类”、“发现”、“购物车”、“我的”、“商品详情” 六个模块。

47E81B37-E633-4102-88EE-FFADEF06EE05.png

这是一般项目都会采用的结构。另外通常还会有一个通用基础模块module_common,提供BaseActivity/BaseFragment、图片加载、网络请求等基础能力,然后每个业务模块都会依赖这个基础模块。

然后业务模块之间很显然是有相互依赖的。例如  “首页”、“分类”、“发现”、“购物车”、“我的”,都是需要跳转到“商品详情” 的,必然是依赖“商品详情” ;而“商品详情”是需要能添加到“购物车”能力的;而“首页”点击搜索显然是“分类”中的搜索功能。所以这些模块之间存在复杂的依赖关系。

44896A33-C8D6-4861-8126-BE220E3F7284.png

模块化缺点:在各个业务功能比较独立的情况下是比较合理的,但多个模块中肯定会有页面跳转、数据传递、方法调用 等情况,所以必然存在以上这种依赖关系,即模块间有着高耦合度。高耦合度加上代码量大,就极易出现上面提到的那些问题了,严重影响了团队的开发效率及质量。

2、组件化介绍

业务组件:去除模块间的耦合,使得每个业务模块可以独立当做App存在,对于其他模块没有直接的依赖关系。

业务基础组件:是提供给业务组件使用,但不是独立的业务,例如分享组件、广告组件;

基础组件:即单独的基础功能,与业务无关,例如 图片加载、网络请求等。

1、组件化好处:

A、加快编译速度:每个业务组件都是一个单独的工程,可独立编译运行,拆分后代码量较少,编译自然变快。

B、提高协作效率:解耦使得组件之间彼此互不打扰,组件内部代码相关性极高。团队中每个人有自己的责任组件,不会影响其他组件;降低团队成员熟悉项目的成本,只需熟悉责任组件即可。

屏幕快照 2021-05-05 下午4.03.49.png

C、功能重用:组件类似引用的第三方库,只需维护好每个组件。业务组件可上可下,灵活多变;而基础组件,为新业务随时集成提供了基础,减少重复开发和维护工作量。

屏幕快照 2021-05-05 下午4.07.56.png

2、组件化架构

A、组件依赖关系是上层依赖下层,修改频率是上层高于下层;

B、基础组件是通用基础能力,修改频率极低,作为SDK可供公司所有项目集成使用。

C、common组件:作为支撑业务组件、业务基础组件的基础(BaseActivity/BaseFragment等基础能力),同时依赖所有的基础组件,提供多数业务组件需要的基本功能。所以业务组件、业务基础组件所需的基础能力只需要依赖common组件即可获得。

D、业务组件、业务基础组件:都依赖common组件。但业务组件之间不存在依赖关系,业务基础组件之间不存在依赖关系。而业务组件是依赖所需的业务基础组件的,例如几乎所有业务组件都会依赖广告组件来展示Banner广告、弹窗广告等。

E、最上层则是主工程,即所谓的“壳工程”,主要是集成所有的业务组件、提供Application唯一实现、gradle、manifest配置,整合成完备的App。

3、组件化开发的问题点

A、业务组件,如何实现单独运行调试? 

B、业务组件间没有依赖,如何实现页面的跳转? 

C、业务组件间没有依赖,如何实现组件间通信/方法调用? 

D、业务组件间没有依赖,如何获取fragment实例? 

E、业务组件不能反向依赖壳工程,如何获取Application实例、如何获取Application onCreate()回调(用于任务初始化)?

3、组件独立调试

每个业务组件都是一个完整的整体,可以当做独立的App,需要满足单独运行及调试的要求,这样可以提升编译速度提高效率。  

做到组件独立调试,有两种方案:  

A、单工程方案,组件以module形式存在,动态配置组件的工程类型; 

B、多工程方案,业务组件以library module形式存在于独立的工程,且只有这一个library module(不讲)。 

1、动态配置组件工程类型

单工程模式,整个项目只有一个工程,它包含:App module加上各个业务组件module,就是所有的代码,这就是单工程模式。

在 AndroidStudio开发Android项目时,使用的是Gradle来构建。Android Gradle中提供了两种插件,在开发中可以通过配置不同的插件来配置不同的module类型。  

A、Application插件,id: com.android.application                B、Library插件,id: com.android.library

区别比较简单,Application插件来配置一个Android App工程,项目构建后输出一个 APK 安装包;Library插件来配置一个Android Library工程,构建后输出ARR包。

CFEB0E73-0755-413A-97A9-83F997AF4BC7.png

显然App module配置的就是Application插件,业务组件module配置的是Library插件。想要实现业务组件的独立调试,这就需要把配置改为Application插件;而独立开发调试完成后,又需要变回Library 插件进行集成调试。

AndroidStudio创建一个Android项目后,会在根目录中生成一个gradle.properties文件。在这个文件定义的常量,可以被任何一个build.gradle读取。所以可以在gradle.properties中定义一个常量值isModule,true为即独立调试;false为集成调试。然后在业务组件的build.gradle中读取isModule,设置成对应的插件即可。

97CCD2D5-32CE-4D9F-9BD0-B94191D1DD56.png

2、动态配置ApplicationId和AndroidManifest

A、一个App是需要一个ApplicationId,而组件在独立调试时也是一个App,所以也需要一个 ApplicationId,集成调试时组件是不需要ApplicationId;

B、一个 APP也只有一个启动页, 而组件在独立调试时也需要一个启动页,在集成调试时就不需要了。

//build.gradle(业务组件)

android {

    、、、、、、

    defaultConfig {

    、、、、、、   

        if (isModule.toBoolean()) {

            //独立调试时添加 applicationId ,集成调试时移除

            applicationId "com.hfy.componentlearning.cart"

        }

    }

    sourceSets {

        main {

            //独立调试与集成调试时使用不同的AndroidManifest.xml文件

            if (isModule.toBoolean()) {

                manifest.srcFile 'src/main/moduleManifest/AndroidManifest.xml'

            } else {

                manifest.srcFile 'src/main/AndroidManifest.xml'

            }

        }

    }

    、、、、、、

}

所以ApplicationId、AndroidManifest也是需要isModule来进行配置的。其中独立调试的AndroidManifest是新建于目录moduleManifest,使用manifest.srcFile即可指定两种调试模式的AndroidManifest文件路径。

(1) moduleManifest中新建的manifest文件指定了Application、启动activity(单独调试)

//moduleManifest/AndroidManifest.xml

<manifest xmlns:android="schemas.android.com/apk/res/and…"

    package="com.hfy.module_cart" >

    <application android:name=".CartApplication"

        android:allowBackup="true"

        android:label="Cart"

        android:theme="@style/Theme.AppCompat">

        

            

                

                

            

        

    

(2) 原本自动生成的manifest,未指定Application、启动activity(集成调试)

<manifest xmlns:android="schemas.android.com/apk/res/and…"

    package="com.hfy.module_cart">

    

        

    

4、页面跳转

所有的业务组件都依赖了Common组件,所以在Common组件中使用关键字**“api”**添加的依赖,业务组件都能访问。要使用 ARouter进行界面跳转,需要Common组件添加Arouter的依赖(另外其它组件共同依赖的库也要都放到 Common 中统一依赖)

因为ARouter比较特殊,“arouter-compiler ”的annotationProcessor依赖需要所有使用到ARouter的组件中都单独添加,不然无法在apt中生成索引文件,就无法跳转成功。

并且在每个使用到ARouter的组件的build.gradle中,其android{}中的 javaCompileOptions中也需要添加特定配置。然后壳工程需要依赖业务组件。

1、common组件的build.gradle

//common组件的build.gradle

dependencies {

    、、、、、、、、

    //ARouter的依赖

    api 'com.alibaba:arouter-api:1.4.0’

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    //业务组件、业务基础组件 共同依赖的库(网络库、图片库等)都写在这里~

}

2、业务组件的build.gradle

//业务组件的build.gradle

android {

    、、、、、、、、

    defaultConfig {

        、、、、、、、、

        javaCompileOptions{

            annotationProcessorOptions {

                arguments = [AROUTER_MODULE_NAME: project.getName()]

            }

        }

    }

}

dependencies {

    、、、、、、 

    //ARouter注解处理器的依赖

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1’

    //业务组件依赖common组件

    implementation 'com.github.hufeiyang:Common:1.0.0'

}

3、壳工程app module的build.gradle

//壳工程app module的build.gradle

dependencies {

    、、、、、、、、

    //这里没有使用私有maven仓,而是发到JitPack仓,一样的意思~

    implementation 'com.github.hufeiyang:Cart:1.0.1' //依赖购物车组件

    implementation 'com.github.hufeiyang:HomePage:1.0.2' //依赖首页组件

    //壳工程内 也需要依赖Common组件,因为需要初始化ARouter

    implementation 'com.github.hufeiyang:Common:1.0.0'

}

4、ARouter初始化

ARouter依赖完了,先要对ARouter初始化,需要在Application内完成:

public class MyApplication extends Application {

    @Override

    public void onCreate() {

        super.onCreate();

        //这两行必须写在init之前,否则这些配置在init过程中将无效

        if (BuildConfig.DEBUG) {

            //打印日志

            ARouter.openLog();

            //开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)

            ARouter.openDebug();

        }

        //尽可能早,推荐在Application中初始化

        ARouter.init(this);

    }

}

5、组件间通信

例如,首页需要展示购物车中商品的数量,而查询购物车中商品数量,这个能力是购物车组件内部的。可以通过组件拆分出可暴露服务来实现。

A、暴露组件:只存放服务接口、服务接口相关的实体类、路由信息、便于服务调用的util类等;

B、业务组件:需要依赖自己的暴露组件,并实现服务接口,如module_cart依赖export_cart 并实现其中的服务接口;

C、服务调用方:只依赖服务提供方的暴露组件,如module_home依赖export_cart,而不依赖module_cart;

D、接口的实现注入:依然是由ARouter完成,和页面跳转一样使用路由信息。即暴露组件通过路由调用业务组件实现的真正的服务。

1、新建export_cart

在购物车工程中新建module即export_cart,在其中新建接口ICartService并定义获取购物车商品数量方法,注意接口必须继承IProvider,是为了使用ARouter的实现注入:

A、ICartService服务接口

//购物车组件对外暴露的服务,必须继承IProvider

public interface ICartService extends IProvider {

    //获取购物车中商品数量的方法

    CartInfo getProductCountInCart();

}

B、CartInfo是购物车信息(包含商品数量)

//购物车信息

public class CartInfo {

    //商品数量

    public int productCount;

}

C、创建路由表信息,存放购物车组件对外提供跳转的页面、服务的路由地址

//购物车组件路由表

public interface CartRouterTable {

    //购物车页面

    String PATH_PAGE_CART = "/cart/cartActivity";

    //购物车服务

    String PATH_SERVICE_CART = "/cart/service";

}

D、外部组件调用CartServiceUtil

//购物车组件服务工具类,其他组件直接使用此类即可:页面跳转、获取服务。

public class CartServiceUtil {

    //跳转到购物车页面

    public static void navigateCartPage(String param1, String param2){

        ARouter.getInstance()

                .build(CartRouterTable.PATH_PAGE_CART)

                .withString("key1",param1)

                .withString("key2",param2)

                .navigation();

    }

   //获取服务实现类

    public static ICartService getService(){

        return (ICartService) ARouter.getInstance().build(CartRouterTable.PATH_SERVICE_CART).navigation();

    }

    //获取购物车中商品数量

    public static CartInfo getCartProductCount(){

        return getService().getProductCountInCart();

    }

}

这里使用静态方法分别提供了页面跳转、服务获取、服务具体方法获取。 其中服务获取和页面跳转同样是使用路由,并且服务接口实现类也是需要添加@Route注解指定路径的。

2、module_cart的实现

A、module_cart需要依赖export_cart(module_cart需要依赖export_cart)

//module_cart的Build.gradle

dependencies {

    、、、、、、、

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    implementation 'com.github.hufeiyang:Common:1.0.0'

    //依赖export_cart

    implementation 'com.github.hufeiyang.Cart:export_cart:1.0.5'

}

B、CartActivity的path改为路由表提供(为CartActivity添加页面路由注解)

@Route(path = CartRouterTable.PATH_PAGE_CART)

public class CartActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_cart);

    }

}

C、实现ICartService(添加@Route注解指定CartRouterTable中定义的服务路由)

@Route(path = CartRouterTable.PATH_SERVICE_CART)

public class CartServiceImpl implements ICartService {

    @Override

    public CartInfo getProductCountInCart() {

        //这里实际项目中应该是请求接口或查询数据库

        CartInfo cartInfo = new CartInfo();

        cartInfo.productCount = 666;

        return cartInfo;

    }

    @Override

    public void init(Context context) {

        //初始化工作,服务注入时会调用,可忽略

    }

}

3、module_home中的使用和调试(module_home需要依赖export_cart)

//module_home的Build.gradle

dependencies {

    、、、、、、

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    implementation 'com.github.hufeiyang:Common:1.0.0'

    //注意这里只依赖export_cart(module_cart由壳工程引入)

    implementation 'com.github.hufeiyang.Cart:export_cart:1.0.5'

}

1、HomeActivity调用CartServiceUtil打开购物车页面或者或者购物车商品数量

@Route(path = "/homepage/homeActivity")

public class HomeActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home);

        //通过路由跳转到 购物车组件的购物车页面(但没有依赖购物车组件)

        findViewById(R.id.btn_go_cart).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                CartServiceUtil.navigateCartPage("param1", "param1");

            }

        });

        //调用购物车组件服务:获取购物车商品数量

        TextView tvCartProductCount = findViewById(R.id.tv_cart_product_count);

        tvCartProductCount.setText("购物车商品数量:"+ CartServiceUtil.getCartProductCount().productCount);

    }

}

看到使用CartServiceUtil.getCartProductCount()获取购物车信息并展示,跳转页面也改为了CartServiceUtil.navigateCartPage()方法。

6、fragment实例获取

通常直接访问具体Fragment类来new一个Fragment实例。但这里组件间没有直接依赖,依然是ARouter来实现不同组件间获取Fragment。

1、先在module_cart中创建CartFragment(并添加注解@Route,指定路径)

//添加注解@Route,指定路径

@Route(path = CartRouterTable.PATH_FRAGMENT_CART)

public class CartFragment extends Fragment {

    、、、、、、、、

    public CartFragment() {}

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        、、、、、、、、

    }

    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_cart, container, false);

    }

}

2、获取Fragment

路由还是定义在export_cart的CartRouterTable中,然后再module_home中依赖export_cart,使用ARouter获取Fragment实例。

@Route(path = "/homepage/homeActivity")

public class HomeActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home);

        、、、、、、、、、

        FragmentManager manager = getSupportFragmentManager();

        FragmentTransaction transaction= manager.beginTransaction();

        //使用ARouter获取Fragment实例,并添加

        Fragment userFragment = (Fragment) ARouter.getInstance().build(CartRouterTable.PATH_FRAGMENT_CART).navigation();

        transaction.add(R.id.fl_test_fragment, userFragment, "tag");

        transaction.commit();

    }

}

7、Application生命周期分发

通常会在Application的onCreate中做一些初始化任务,例如前面提到的ARouter初始化。而业务组件有时也需要获取应用的Application,也要在应用启动时进行一些初始化任务。

直接在壳工程Application的onCreate操作,这样做会带来问题:因为希望壳工程和业务组件代码隔离(虽然有依赖),并且希望内部的任务要在业务组件内部完成。  那么如何做到各业务组件无侵入地获取Application生命周期呢?

使用AppLifeCycle插件,它专门用于在Android组件化开发中,Application生命周期主动分发到组件。具体使用如下:

1、common组件依赖applifecycle-api

//common组件build.gradle

dependencies {

    、、、、、、、、

    //AppLifecycle的依赖

    api 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-api:1.0.4'

}

2、业务组件依赖applifecycle-compiler和common组件

//业务组件build.gradle

dependencies {

    、、、、、、、

    //common组件的依赖

    implementation 'com.github.hufeiyang:Common:1.0.2’

    //applifecycle-compiler的依赖,注解处理相关 

    annotationProcessor 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-compiler:1.0.4'

}

3、实现接口IApplicationLifecycleCallbacks用于接收Application生命周期,且添加@AppLifecycle注解

//业务组件的AppLifecycle

@AppLifecycle

public class CartApplication implements IApplicationLifecycleCallbacks {

    public  Context context;

    //用于设置优先级,即多个组件onCreate方法调用的优先顺序

    @Override

    public int getPriority() {

        return NORM_PRIORITY;

    }

    @Override

    public void onCreate(Context context) {

        //可在此处做初始化任务,相当于Application的onCreate方法

        this.context = context;

    }

    @Override

    public void onTerminate() {}

    @Override

    public void onLowMemory() {}

    @Override

    public void onTrimMemory(int level) {}

}

实现的方法有onCreate、onTerminate、onLowMemory、onTrimMemory。最重要的就是onCreate方法了,相当于Application的onCreate方法,可在此处做初始化任务。并且还可以通过getPriority()方法设置回调,多个组件onCreate方法调用的优先顺序,无特殊要求设置NORM_PRIORITY即可。

4、壳工程引入AppLifecycle插件、触发回调

A、壳工程根目录的build.gradle

//壳工程根目录的build.gradle

buildscript {

   

    repositories {

        google()

        jcenter()

        //applifecycle插件仓也是jitpack

        maven { url 'jitpack.io' }

    }

    dependencies {

        classpath 'com.android.tools.build:gradle:3.6.1'

        //加载插件applifecycle

        classpath 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-plugin:1.0.3'

    }

}

B、app module的build.gradle

//app module的build.gradle

apply plugin: 'com.android.application'

//使用插件applifecycle

apply plugin: 'com.hm.plugin.lifecycle'

、、、、、、、

dependencies {

    、、、、、、、、

    //依赖所有的业务模块

    implementation 'com.github.hufeiyang.Cart:module_cart:1.0.11'

    implementation 'com.github.hufeiyang:HomePage:1.0.5’

    //壳工程内也需要依赖Common组件,因为要触发生命周期分发

    implementation 'com.github.hufeiyang:Common:1.0.2'

}

C、在Application中触发生命周期的分发

//壳工程 MyApplication

public class MyApplication extends Application {

    @Override

    public void onCreate() {

        super.onCreate();

        、、、、、、

        ApplicationLifecycleManager.init();

        ApplicationLifecycleManager.onCreate(this);

    }

    @Override

    public void onTerminate() {

        super.onTerminate();

        ApplicationLifecycleManager.onTerminate();

    }

    @Override

    public void onLowMemory() {

        super.onLowMemory();

        ApplicationLifecycleManager.onLowMemory();

    }

    @Override

    public void onTrimMemory(int level) {

        super.onTrimMemory(level);

        ApplicationLifecycleManager.onTrimMemory(level);

    }

}


android {

    、、、、、、

    defaultConfig {

    、、、、、、   

        if (isModule.toBoolean()) {

            //独立调试时添加 applicationId ,集成调试时移除

            applicationId "com.hfy.componentlearning.cart"

        }

    }

  


    sourceSets {

        main {

            //独立调试与集成调试时使用不同的AndroidManifest.xml文件

            if (isModule.toBoolean()) {

                manifest.srcFile 'src/main/moduleManifest/AndroidManifest.xml'

            } else {

                manifest.srcFile 'src/main/AndroidManifest.xml'

            }

        }

    }

    、、、、、、

}

//moduleManifest/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.hfy.module_cart" >

    <application android:name=".CartApplication"

        android:allowBackup="true"

        android:label="Cart"

        android:theme="@style/Theme.AppCompat">

        <activity android:name=".CartActivity">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

</manifest>

  


(2) 原本自动生成的manifest,未指定Application、启动activity(集成调试)

  


<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.hfy.module_cart">

    <application>

        <activity android:name=".CartActivity"></activity>

    </application>

</manifest>

//common组件的build.gradle

dependencies {

    、、、、、、、、

    //ARouter的依赖

    api 'com.alibaba:arouter-api:1.4.0’

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    //业务组件、业务基础组件 共同依赖的库(网络库、图片库等)都写在这里~

}

  


2、业务组件的build.gradle

  


//业务组件的build.gradle

android {

    、、、、、、、、

    defaultConfig {

        、、、、、、、、

        javaCompileOptions{

            annotationProcessorOptions {

                arguments = [AROUTER_MODULE_NAME: project.getName()]

            }

        }

    }

}

  


dependencies {

    、、、、、、 

    //ARouter注解处理器的依赖

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1’

    //业务组件依赖common组件

    implementation 'com.github.hufeiyang:Common:1.0.0'

}

  


3、壳工程app module的build.gradle

  


//壳工程app module的build.gradle

dependencies {

    、、、、、、、、

    //这里没有使用私有maven仓,而是发到JitPack仓,一样的意思~

    implementation 'com.github.hufeiyang:Cart:1.0.1' //依赖购物车组件

    implementation 'com.github.hufeiyang:HomePage:1.0.2' //依赖首页组件

  


    //壳工程内 也需要依赖Common组件,因为需要初始化ARouter

    implementation 'com.github.hufeiyang:Common:1.0.0'

}

  


4、ARouter初始化

  


ARouter依赖完了,先要对ARouter初始化,需要在Application内完成:

  


public class MyApplication extends Application {

    @Override

    public void onCreate() {

        super.onCreate();

        //这两行必须写在init之前,否则这些配置在init过程中将无效

        if (BuildConfig.DEBUG) {

            //打印日志

            ARouter.openLog();

            //开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)

            ARouter.openDebug();

        }

        //尽可能早,推荐在Application中初始化

        ARouter.init(this);

    }

}

A、暴露组件:只存放服务接口、服务接口相关的实体类、路由信息、便于服务调用的util类等;

  


B、业务组件:需要依赖自己的暴露组件,并实现服务接口,如module_cart依赖export_cart 并实现其中的服务接口;

  


C、服务调用方:只依赖服务提供方的暴露组件,如module_home依赖export_cart,而不依赖module_cart;

  


D、接口的实现注入:依然是由ARouter完成,和页面跳转一样使用路由信息。即暴露组件通过路由调用业务组件实现的真正的服务。

  


1、新建export_cart

  


在购物车工程中新建module即export_cart,在其中新建接口ICartService并定义获取购物车商品数量方法,注意接口必须继承IProvider,是为了使用ARouter的实现注入:

  


A、ICartService服务接口

  


//购物车组件对外暴露的服务,必须继承IProvider

public interface ICartService extends IProvider {

    //获取购物车中商品数量的方法

    CartInfo getProductCountInCart();

}

  


B、CartInfo是购物车信息(包含商品数量)

  


//购物车信息

public class CartInfo {

    //商品数量

    public int productCount;

}

  


C、创建路由表信息,存放购物车组件对外提供跳转的页面、服务的路由地址

  


//购物车组件路由表

public interface CartRouterTable {

    //购物车页面

    String PATH_PAGE_CART = "/cart/cartActivity";

    //购物车服务

    String PATH_SERVICE_CART = "/cart/service";

}

  


D、外部组件调用CartServiceUtil

  


//购物车组件服务工具类,其他组件直接使用此类即可:页面跳转、获取服务。

public class CartServiceUtil {

  


    //跳转到购物车页面

    public static void navigateCartPage(String param1, String param2){

        ARouter.getInstance()

                .build(CartRouterTable.PATH_PAGE_CART)

                .withString("key1",param1)

                .withString("key2",param2)

                .navigation();

    }

  


   //获取服务实现类

    public static ICartService getService(){

        return (ICartService) ARouter.getInstance().build(CartRouterTable.PATH_SERVICE_CART).navigation();

    }

  


    //获取购物车中商品数量

    public static CartInfo getCartProductCount(){

        return getService().getProductCountInCart();

    }

}

  


这里使用静态方法分别提供了页面跳转、服务获取、服务具体方法获取。 其中服务获取和页面跳转同样是使用路由,并且服务接口实现类也是需要添加@Route注解指定路径的。

  


2、module_cart的实现

  


A、module_cart需要依赖export_cart(module_cart需要依赖export_cart)

  


//module_cart的Build.gradle

dependencies {

    、、、、、、、

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    implementation 'com.github.hufeiyang:Common:1.0.0'

    //依赖export_cart

    implementation 'com.github.hufeiyang.Cart:export_cart:1.0.5'

}

  


B、CartActivity的path改为路由表提供(为CartActivity添加页面路由注解)

  


@Route(path = CartRouterTable.PATH_PAGE_CART)

public class CartActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_cart);

    }

}

  


C、实现ICartService(添加@Route注解指定CartRouterTable中定义的服务路由)

  


@Route(path = CartRouterTable.PATH_SERVICE_CART)

public class CartServiceImpl implements ICartService {

    @Override

    public CartInfo getProductCountInCart() {

        //这里实际项目中应该是请求接口或查询数据库

        CartInfo cartInfo = new CartInfo();

        cartInfo.productCount = 666;

        return cartInfo;

    }

  


    @Override

    public void init(Context context) {

        //初始化工作,服务注入时会调用,可忽略

    }

}

  


3、module_home中的使用和调试(module_home需要依赖export_cart)

  


//module_home的Build.gradle

dependencies {

    、、、、、、

    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

    implementation 'com.github.hufeiyang:Common:1.0.0'

    //注意这里只依赖export_cart(module_cart由壳工程引入)

    implementation 'com.github.hufeiyang.Cart:export_cart:1.0.5'

}

  


1、HomeActivity调用CartServiceUtil打开购物车页面或者或者购物车商品数量

  


@Route(path = "/homepage/homeActivity")

public class HomeActivity extends AppCompatActivity {

  


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home);

  


        //通过路由跳转到 购物车组件的购物车页面(但没有依赖购物车组件)

        findViewById(R.id.btn_go_cart).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                CartServiceUtil.navigateCartPage("param1", "param1");

            }

        });

  


        //调用购物车组件服务:获取购物车商品数量

        TextView tvCartProductCount = findViewById(R.id.tv_cart_product_count);

        tvCartProductCount.setText("购物车商品数量:"+ CartServiceUtil.getCartProductCount().productCount);

    }

}

  


看到使用CartServiceUtil.getCartProductCount()获取购物车信息并展示,跳转页面也改为了CartServiceUtil.navigateCartPage()方法。

  


6、fragment实例获取

  


通常直接访问具体Fragment类来new一个Fragment实例。但这里组件间没有直接依赖,依然是ARouter来实现不同组件间获取Fragment。

  


1、先在module_cart中创建CartFragment(并添加注解@Route,指定路径)

  


//添加注解@Route,指定路径

@Route(path = CartRouterTable.PATH_FRAGMENT_CART)

public class CartFragment extends Fragment {

    、、、、、、、、

    public CartFragment() {}

  


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        、、、、、、、、

    }

  


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_cart, container, false);

    }

}

  


2、获取Fragment

  


路由还是定义在export_cart的CartRouterTable中,然后再module_home中依赖export_cart,使用ARouter获取Fragment实例。

  


@Route(path = "/homepage/homeActivity")

public class HomeActivity extends AppCompatActivity {

  


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home);

        、、、、、、、、、

        FragmentManager manager = getSupportFragmentManager();

        FragmentTransaction transaction= manager.beginTransaction();

  


        //使用ARouter获取Fragment实例,并添加

        Fragment userFragment = (Fragment) ARouter.getInstance().build(CartRouterTable.PATH_FRAGMENT_CART).navigation();

        transaction.add(R.id.fl_test_fragment, userFragment, "tag");

        transaction.commit();

    }

}

  


7、Application生命周期分发

  


通常会在Application的onCreate中做一些初始化任务,例如前面提到的ARouter初始化。而业务组件有时也需要获取应用的Application,也要在应用启动时进行一些初始化任务。

  


直接在壳工程Application的onCreate操作,这样做会带来问题:因为希望壳工程和业务组件代码隔离(虽然有依赖),并且希望内部的任务要在业务组件内部完成。  那么如何做到各业务组件无侵入地获取Application生命周期呢?

  


使用AppLifeCycle插件,它专门用于在Android组件化开发中,Application生命周期主动分发到组件。具体使用如下:

  


1、common组件依赖applifecycle-api

  


//common组件build.gradle

dependencies {

    、、、、、、、、

    //AppLifecycle的依赖

    api 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-api:1.0.4'

}

  


2、业务组件依赖applifecycle-compiler和common组件

  


//业务组件build.gradle

dependencies {

    、、、、、、、

    //common组件的依赖

    implementation 'com.github.hufeiyang:Common:1.0.2’

    //applifecycle-compiler的依赖,注解处理相关 

    annotationProcessor 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-compiler:1.0.4'

}

  


3、实现接口IApplicationLifecycleCallbacks用于接收Application生命周期,且添加@AppLifecycle注解

  


//业务组件的AppLifecycle

@AppLifecycle

public class CartApplication implements IApplicationLifecycleCallbacks {

  


    public  Context context;

  


    //用于设置优先级,即多个组件onCreate方法调用的优先顺序

    @Override

    public int getPriority() {

        return NORM_PRIORITY;

    }

  


    @Override

    public void onCreate(Context context) {

        //可在此处做初始化任务,相当于Application的onCreate方法

        this.context = context;

    }

  


    @Override

    public void onTerminate() {}

  


    @Override

    public void onLowMemory() {}

  


    @Override

    public void onTrimMemory(int level) {}

}

  


实现的方法有onCreate、onTerminate、onLowMemory、onTrimMemory。最重要的就是onCreate方法了,相当于Application的onCreate方法,可在此处做初始化任务。并且还可以通过getPriority()方法设置回调,多个组件onCreate方法调用的优先顺序,无特殊要求设置NORM_PRIORITY即可。

  


4、壳工程引入AppLifecycle插件、触发回调

  


A、壳工程根目录的build.gradle

  


//壳工程根目录的build.gradle

buildscript {

   

    repositories {

        google()

        jcenter()

  


        //applifecycle插件仓也是jitpack

        maven { url 'https://jitpack.io' }

    }

    dependencies {

        classpath 'com.android.tools.build:gradle:3.6.1'

        //加载插件applifecycle

        classpath 'com.github.hufeiyang.Android-AppLifecycleMgr:applifecycle-plugin:1.0.3'

    }

}

  


B、app module的build.gradle

  


//app module的build.gradle

apply plugin: 'com.android.application'

//使用插件applifecycle

apply plugin: 'com.hm.plugin.lifecycle'

、、、、、、、

dependencies {

    、、、、、、、、

    //依赖所有的业务模块

    implementation 'com.github.hufeiyang.Cart:module_cart:1.0.11'

    implementation 'com.github.hufeiyang:HomePage:1.0.5’

  


    //壳工程内也需要依赖Common组件,因为要触发生命周期分发

    implementation 'com.github.hufeiyang:Common:1.0.2'

}

  


C、在Application中触发生命周期的分发

  


//壳工程 MyApplication

public class MyApplication extends Application {

  


    @Override

    public void onCreate() {

        super.onCreate();

        、、、、、、

        ApplicationLifecycleManager.init();

        ApplicationLifecycleManager.onCreate(this);

    }

  


    @Override

    public void onTerminate() {

        super.onTerminate();

        ApplicationLifecycleManager.onTerminate();

    }

  


    @Override

    public void onLowMemory() {

        super.onLowMemory();

        ApplicationLifecycleManager.onLowMemory();

    }

  


    @Override

    public void onTrimMemory(int level) {

        super.onTrimMemory(level);

        ApplicationLifecycleManager.onTrimMemory(level);

    }

}

首先在onCreate方法中调用ApplicationLifecycleManager的init()方法,用于收集组件内实现了IApplicationLifecycleCallbacks且添加了@AppLifecycle注解的类。然后在各生命周期方法内调用对应的ApplicationLifecycleManager的方法,来分发到所有组件。

97CCD2D5-32CE-4D9F-9BD0-B94191D1DD56.png