Android工程 模块/组件化

Android工程 模块/组件化

一、为什么要模块/组件化

设想一下,如果项目工程不做模块/组件化设计,经过不断地迭代,增加的业务、功能越来越多,代码全部混在一起,耦合性越来越高,越来越难维护,打包越来越慢。
牵一发而动全身,改一句代码1秒钟,等待构建验证半小时,这对多人开发的大型项目简直就是灾难。

二、什么是模块/组件化

再来说说模块化,就是将整个APP按照业务或功能拆分成不同的模块,比如登录模块,搜索模块,支付模块等等。但是在工程不断扩大的情况下,模块之间的耦合还是会越来越强,这时想对工程进一步解耦,组件化思想也就出现了。
可以说组件化是在模块化上的演进,将模块作为一个个的组件,并且组件可以独立使用/运行,这样在开发时,还需要等待半小时构建整包吗?是不是只要把当前修改的模块构建好就可以验证了。
其实模块化、组件化都是一种架构设计思想,其目的都是为了将工程大化小,实现解耦与重用,提高可维护性。

三、如何实现组件化

Talk is cheap, show me the code!

3.1 结构图

在这里插入图片描述
在这里插入图片描述

组件说明
App主项目应用壳子,用于构建最终的APK
movie_module业务模块,电影业务
music_module业务模块,音乐业务
base_module基础模块,内部可封装一些通用基础功能
route_module路由模块,解决各模块间的通信问题

3.1 敲代码

首先创建好各个module
在这里插入图片描述

3.1.1 模块怎么做到整体构建还是自有构建(library和application)

1.base和router模块在build.gradle中将其指定为library(因为这两个基础模块不需要当做一个application来运行使用)

apply plugin: 'com.android.library'

2.看到这里应该已经想到,肯定movie和music的build.gradle中要动态指定application或library。怎么做呢,首先是不是要有个开关配置,在gradle.properties中加个配置,true代表以模块形式运行,false代表以Application形式运行

# module switch
isModule=true

3.开关做好以后,在movie和music的build.gradle中根据开关做动态配置。

if (isModule.toBoolean()) {
    // 当做module时作为library使用
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}

defaultConfig {
        if (!isModule.toBoolean()) {
            applicationId "com.example.movie_module"
        }
        minSdkVersion 24
        targetSdkVersion 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

同時区分不同模式打包时manifest文件

sourceSets {
        main {
            if (isModule.toBoolean()) { // 作为module打包
                manifest.srcFile '/src/main/module-file/AndroidManifest.xml' // manifest 文件路径
            } else {
                manifest.srcFile '/src/main/AndroidManifest.xml' // manifest 文件路径
            }
        }
    }

3.1.2 处理依赖关系

1.route_module依赖base_module

// api依赖base_module,传递依赖,使得依赖route_module的组件也能使用base的能力
api project(':base_module')

2.其他模块都依赖route_module

implementation project(':router_module')

3.主工程判断业务以module形式构建时需对其依赖(要把各个module都打入App中)

// 组件化方式时,依赖其他模块
if (isModule.toBoolean()) {
    implementation project(':movie_module')
    implementation project(':music_module')
}

3.1.3 各模块具体实现

1.base_module:
简单封装了一个LogUtil

public class LogUtil {
    private static final String TAG = "LogUtil";
    public static void i(String method, String msg) {
        Log.i(TAG, "method " + method + " msg");
    }
}

2.movie/music_module:
也是简单写了两个界面,在音乐界面增加了一个跳转到电影界面的点击事件(Router下面会说到,也是关键点,music和movie两个组件相互独立,如何跳转),这里贴一下Activity代码

public class MusicActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music);
    }

    public void onClickToMovie(View view) {
        // 路由跳转到Movie模块界面
        Router.getInstance().startRouterActivity(this, RouterConstant.MODULE_MOVIE_ACTIVITY_MOVIE);
    }
}

3.route_module:
这里主要是一个Router类和定义了Path常量类

public class Router {

    private final HashMap<String, Class> routers = new HashMap<>();

    private static class LazyHolder {
        static final Router INSTANCE = new Router();
    }

    public static Router getInstance() {
        return LazyHolder.INSTANCE;
    }

    public void register(String path, Class cls) {
        routers.put(path, cls);
    }

    public void startRouterActivity(Context context, String targetPath) {
        Class aClass = routers.get(targetPath);
        if (aClass == null) {
            LogUtil.i("startRouterActivity", "class null");
            return;
        }
        Intent intent = new Intent(context, aClass);
        context.startActivity(intent);
    }
}

Router是个单例类,提供了一个注册和启动Activity的方法;通过注册将path(字符串)和对应的类保存到路由表中,这个表就记录了对应的path和类的关系。

4.App主工程:
主要是进行注册路由表,在Application的onCreate()中进行注册;同时还有一个MainActivity作为主入口。

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 注册路由表
        Router.getInstance().register(RouterConstant.MODULE_MOVIE_ACTIVITY_MOVIE, MovieActivity.class);
        Router.getInstance().register(RouterConstant.MODULE_MUSIC_ACTIVITY_MUSIC, MusicActivity.class);
    }
}

3.3 ShowCase

3.3.1 isModule为false时,单独运行movie/music

可以看见单独运行movie_module,桌面会有一个movie_module的app入口,这样就可以做到movie模块单独打包调试了,而不需要整个Demo应用来调试。
在这里插入图片描述

在这里插入图片描述

3.3.2 isModule为true,整个Demo运行

可以看出是整个demo的应用入口,打开后进入MainActivity,可以跳转到movie或music,同时music也可以跳转到movie
在这里插入图片描述

四、总结

了解模块/组件化的设计思想,以及具体在代码中如何配置,通过单独/整体打包,运行单个组件进行调试。同时这里也通过一个路由表注册保存path与类的信息,这样就解决了组件间通讯的问题。
关于ARouter或WMRouter的实现,肯定不可能这么简单的啦,他们有使用到注解,然后用到APT(Annotation Processing Tool)来扫描注解自动生成代码等,美团WMRouter还提供了类似ServiceLoader功能,多个模块之间通过接口调用代码,也是更好解决组件间通信。建议大家也多去研究。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值