A Preliminary Exploration of Android Componentization - This article will take you all the way to the dark

Overview

The software development process is also the evolution process of the architecture. Take Android as an example, from the initial MVC, MVP, MVVP, to the later componentization and plug-in, but in the final analysis, everything is for better project maintenance. , iteration, reduce development costs.

In the development process of a project, we may put all the functional modules into a module in the early stage, so that we can develop quickly, but as the project grows and the number of developers and functions increase, the code will become more and more bloated. , the coupling between the various modules is getting heavier and heavier, and the whole body is affected. At this time, in order to ensure the quality of the project, we need to refactor the project.

We can check and score according to business modules, put different business modules into different modules, and realize the structure between each business. They also rely on the underlying public library. This is the concept of modularity , but when multiple modules involve When the same function is reached, the coupling of the code will increase. For example, there are two modules that need the function of video playback. If the video playback is placed in two components, the problem of code duplication will occur, and it does not feel very good to put it in the public library. At this time, componentization is used to solve this problem

Modular and componentized

modular

Specific business modules, such as product details module, product release module, search module

componentized

A single functional component, such as video playback component, sharing component, etc., each component can be developed as a separate module, and can be extracted separately as an SDK for external release.

The idea of ​​modularization and componentization is the same, both of which are code splitting, but modularization is based on functional modules (business-oriented), and componentization is based on functional modules (function-oriented), and modular particles The degree is larger, and the granularity of components is smaller. It is also common for modules and components to coexist in a project, and each is responsible for its own affairs.

image

image

As shown in the figure above is the basic structure of a componentized project

  • Basic library and public library : basic operation classes, tool classes, introduction and packaging of third-party libraries, app hosting functions, modules and components required by the project depend on this library
  • 组件层:项目用的功能模块或者业务模块,如:登录模块,视频播放组件,分享组件等
  • 应用层:宿主工程,APP的主项目,APP入口和主架子

组件化Demo

地址如下: github.com/syg13579/as… 我根据demo项目从以下几个方面来讲解

  • 1:项目分析
  • 2:组件application和library动态切换
  • 3:组件间的数据传递和方法调用
  • 4:组件类(例如:Fragment)的获取,以及夸组件页面跳转和通讯

1:项目分析

image

image

如上图所示,项目的主要结构

  • 应用层:app 项目的主入口
  • 组件层:goods login 商品详情页和登录组件
  • 基础库层:assemblebase用来各个组件数据和方法交互的 ,base是常用的工具类,各种类库的封装

2:组件application和library动态切换

在开发过程中,为了能够实现快速开发,组件能够独立运行就显的特别重要,moudle一般分为两种

  • App 插件,id: com.android.application
  • Library 插件,id: com.android.library

我们可以通过配置可动态进行application和library的切换,我们在各个组件的gradle.properties文件中配置一个控制切换的变量

image

image

然后在build.gradle中就可以通过isRunAlone变量来进行application和library的切换了,主要设计的点有三部分

  • plugin属性的配置
  • applicationId的配置
  • AndroidManifest的配置
if (isRunAlone.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion 26

    defaultConfig {
        if (isRunAlone.toBoolean()) {
            applicationId "ppzh.jd.com.goods"
        }
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            if (isRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

}
复制代码

如果以上配置就可以实现application和library的切换了

3:组件间的数据传递和方法调用

由于主项目、组件之间,组件和组件之间不能直接通过引用进行数据传递和方法调用,那么在开发的过程中怎么进行数据传递和方法调用呢,可以通过「接口」+「实现」的方式进行,

assemblebase基础库就是用来进行数据传递和方法调用的,它被所有组件所依赖,assemblebase提供各个组件对外提供数据和方法调用的抽象service ,同时还有serviceFactory对service进行操作,各个组件在初始化的时候对各自的service进行实现。同时中也会提供所有的 Service 的空实现,以避免引起的空指针异常

就以登录模块为例,对外提供两个数据

public interface ILoginService {

    /**
     * 是否已经登录
     *
     * @return
     */
    boolean isLogin();

    /**
     * 获取登录用户的 AccountId
     *
     * @return
     */
    String getAccountId();

}
复制代码

相关的serviceFactory类如下,可以通过serviceFactory拉取相关service的实例

public class ServiceFactory {

    private ILoginService loginService;
    private IGoodsService goodsService;

    /**
     * 禁止外部创建 ServiceFactory 对象
     */
    private ServiceFactory() {
    }

    /**
     * 通过静态内部类方式实现 ServiceFactory 的单例
     */
    public static ServiceFactory getInstance() {
        return Inner.serviceFactory;
    }

    private static class Inner {
        private static ServiceFactory serviceFactory = new ServiceFactory();
    }

//    ------------------------LoginService------------------------
    /**
     * 接收 Login 组件实现的 Service 实例
     */
    public void setLoginService(ILoginService loginService) {
        this.loginService = loginService;
    }

    /**
     * 返回 Login 组件的 Service 实例
     */
    public ILoginService getLoginService() {
        if (loginService == null) {
            loginService = new EmptyLoginService();
        }
        return loginService;
    }
复制代码

在login组件中只需要实现ILoginService,并通过serviceFactory进行设置

public class LoginService implements ILoginService {
    @Override
    public boolean isLogin() {
        return false;
    }

    @Override
    public String getAccountId() {
        return null;
    }
}
复制代码

在login的appliction中进行service的设置

public class LoginApp extends BaseApp {

    @Override
    public void onCreate() {
        super.onCreate();
        initModuleApp(this);
        initModuleData(this);
    }

    @Override
    public void initModuleApp(Application application) {
        ServiceFactory.getInstance().setLoginService(new LoginService());
    }

    @Override
    public void initModuleData(Application application) {

    }
}
复制代码

但是有这样一个问题:在集成到app中,LoginApp是没有被执行的,这个怎么解决呢,我们可以通过反射进行解决

public class AssembleApplication extends BaseApp {
    @Override
    public void onCreate() {
        super.onCreate();
        initModuleApp(this);
        initModuleData(this);
        initComponentList();
    }

    @Override
    public void initModuleApp(Application application) {

    }

    @Override
    public void initModuleData(Application application) {

    }

    //初始化组件
    //通过反射初始化
    private void initComponentList(){
        for (String moduleApp : AppConfig.moduleApps) {
            try {
                Class clazz = Class.forName(moduleApp);
                BaseApp baseApp = (BaseApp) clazz.newInstance();
                baseApp.initModuleApp(this);
                baseApp.initModuleData(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
复制代码

如上所示就完成了

4:组件类(例如:Fragment)的获取,以及夸组件页面跳转和通讯

fragment的获取也是通过service来完成的

public interface IGoodsService {

    /**
     * 创建 GoodsFragment
     * @param bundle
     * @return
     */
    Fragment newGoodsFragment(Bundle bundle);
}
复制代码

相关组件实现该接口就行

各个组件间页面的跳转可以通过阿里的ARouter实现,我是通过设置ComponentName来实现的,但这种方式好像并没有实现真正的代码隔离

 /**
     *
     * 去登陆
     *
     * 跨组件页面跳转
     */
    private void toLogin(){
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(mContext, "ppzh.jd.com.login.LoginActivity"));
        startActivityForResult(intent,LOGIN_REQUEST_CODE);
    }
复制代码

总结

通过上面就整体实现了项目组件化,在以后也可以更多的运用组件化来进行项目开发

本博主会持续更新Android学习知识,更多学习知识以及笔记请查看我的Gitee.

Guess you like

Origin juejin.im/post/6988015894119055367