Android模块化和组件化开发

目录

一.模块化介绍

1.1:模块化简介

1.2:模块化和组件化的区别

1.3:模块化的优点

1.4:模块化的层级介绍

二.如何实现组件化

2.1:实现模块化需要解决的问题

2.2:各个问题的解决方法

 

一:模块化介绍

(1)对于简单的小项目,大多都采用的是单一工程,独立开发。由于项目不大,编译速度及维护成本这些也在接受范围之内。而对于一个大型App产品,多人合作、单一工程的App架构势必会影响开发效率,增加项目的维护成本。每个开发者都要熟悉如此之多的代码,将很难进行多人协作开发,而且Android项目在编译代码的时候电脑会非常卡,又因为单一工程下代码耦合严重,每修改一处代码后都要重新编译打包测试,导致非常耗时。所以必须要有更灵活的架构代替过去单一的工程架构。

(2)模块和组件化的区别:

模块指的是独立的业务模块。

组件指的是单一的功能组件。如视频组件、支付组件。每个组件都可以以一个单独的module开发,并且可以单独抽取出来作为SDK对外发布使用。

模块和组件最明显的区别是模块相对组件来说粒度更大。一个模块中可能包含多个组件。在划分的时候,模块化是业务导向,组件化是功能导向。

模块化和组件化,我的拙见,并不需要严格意义上的区分。只要适合自己的就好。所以本文中,只是介绍思想,并不是严格意义上的模块化或组件化开发,抛砖引玉。

(3)模块化的优点:

使用模块化架构,业务模块分离,高内聚,低耦合,代码边界清晰,每一个模块业务都可以拆分出来独立运行。所有模块寄托于宿主App,加载分离的各个组件,各自编译自己的模块,有利于多人团队协作开发。我们只需要关注如何做好隔离,如何更好地计,以及提高开发效率与产品体验,还有代码质量。

(4)模块化层级介绍:

 

基础层BaseModule:基础层包含的是一些常用的基础库以及对基础库的封装和工具类的封装,比如常用的图片加载、网络请求、数据库、数据存储等操作。其他模块或组件都可以引用同一套基础库。

业务层ProviderModule:基础层往上封装了一层跟项目相关的业务功能层,一些与项目有关的常量类、以项目为基础的工具类。

应用层:业务功能层往上就是应用层,应用层可以是多个业务模块,最后将多个业务模块统筹输出为APP。

二:如何实现模块化:

2.1:模块化开发需要解决的问题

1.每一个组件或模块都是一个完整的整体,所以模块化开发过程中要满足单独运行及调试的要求。这样可以提升开发过程中项目的编译速度,以此来提高开发效率。

2.模块之间数据的传递和方法的相互调用。

3.模块之间的界面跳转:在模块化开发过程中如何在不相互依赖的情况下实现互相跳转。

4.模块化开发完成后相互之间的集成调试如何实现呢?

 

2.2:question one:组件的单独调试和集成调试

1.动态配置组件的工程类型

目的是:让我们的组件既可以单独调试也可以被其他模块依赖。

我们在 gradle.properties,添加一个Boolen变量,
isUserModule=false。

if (isUserModule.toBoolean()) {
apply plugin: 'com.android.library'//配置一个library工程,构建后输出aar包。

} else {
apply plugin: 'com.android.application'//配置一个Android App工程,项目构建后输出一个APK的安装包

}

2.动态配置组件的 ApplicationId 和 AndroidManifest 文件

一个APP应该只有一个ApplicationId 和AndroidManifest 文件。我们在build.gradle文件中添加代码进行动态配置。

//动态设置ApplicationId

if (!isUserModule.toBoolean()) {//如果是模块,则设置包名

applicationId "com.example.user"

}



//动态设置清单文件,需要建立两个清单文件。一个是单独运行的,另外一个是作为依赖时候使用的

sourceSets {

main {

if (isUserModule.toBoolean()) {

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

} else {

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

}

}

}

 

2.3:question two:组件间的数据传递和方法相互调用

因为组件和组件之间是没有相互依赖的,所以不可以直接使用类的方式进行相互调用来实现数据传递和方法调用。

数据传递我们可以使用:EventBus、广播、数据持久化、接口+实现等方式来解决。比如判断登录状态,获取登录的用户token等。

本文咱们说一下接口+实现的方式:我们以获取登录状态为例

(1):我们在业务模块ProviderModule创建service包,里面放置我们定义的接口和实现类。

(2):创建一个用户模块的接口,IAccountService。里面定义获取登录状态和用户id的方法。

(3):定义一个ServiceFactory,对外提供设置和获取IAccountService的具体实现类。

(4):在用户模块创建AccountService实现IAccountService接口,然后在Application中初始化ServiceFactory中的service。

(5):在其他模块就可以通过ServiceFactory获取实现类,调用实现类的方法了。

一:
/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   : 用户模块的对外提供的方法
 * version:
 */
interface IAccountService {
    /**
     *  是否登录
     */
    fun isLogin(): Boolean

    /**
     * 获取用户的AccountId
     */
    fun getAccountId(): String

    /**
     * 获取用户中心Fragment
     */
    fun getUserFragment(activity: Activity): BaseFragment?
}

二:在用户模块,实现IAccountService。
/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   : IAccountService实现类。暴露用户模块对外提供的方法。解耦,外部只需要调用就可以了,不需要知道内部实现的细节。
 * version:
 */
class AccountService : IAccountService {


    override fun isLogin(): Boolean {
        return true
    }

    override fun getAccountId(): String {
        return "这是来自usercenter的召唤"
    }

    override fun getUserFragment(activity: Activity): BaseFragment? {
        return UserFragment()
    }

}
三:ServiceFactory
/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   : ServiceFactory,对外提供设置和获取IAccountService的具体实现类
 * version:
 */
class ServiceFactory private constructor() {

    companion object {
        val instance: ServiceFactory by lazy {
            ServiceFactory()
        }
    }

    private var accountService: IAccountService? = null

    public fun setAccountService(service: IAccountService) {
        this.accountService = service
    }

    public fun getAccountService(): IAccountService {
        if (accountService == null) {
            accountService = EmptyAccountService()
        }
        return this.accountService!!
    }


}
四:
/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   :
 * version:
 */
class UserCenterApplication : BaseModuleApplication() {
    private val TAG = "UserCenterApplication"
    override fun initModuleApplication() {
        ServiceFactory.instance.setAccountService(AccountService())
        Log.e(TAG, "UserCenterApplication-initModuleApplication")

    }

    override fun initModuleData() {
        Log.e(TAG, "UserCenterApplication-initModuleData")
    }

}
五:使用
 toast(ServiceFactory.instance.getAccountService().getAccountId())

这样引申出一个问题:Application的动态配置

当用户模块单独调试的时候,UserApplication需要单独初始化。当模块集中调试的时候,APP的的Application就需要对所有的模块application进行初始化。what should i do?

我们可以采用反射的方式来实现模块的Application初始化。

(1):在Base模块,定义BaseModuleApplication,里面定义了两个方法,initModeApp 是初始化当前模块时需要调用的方法,initModuleData 是所有模块的都初始化后再调用的方法。

/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   :
 * version:
 */
abstract class BaseModuleApplication : BaseApplication() {
    override fun onCreate() {
        super.onCreate()
        initModuleApplication()
        initModuleData()
    }

    /**
     *  初始化模块的Application
     */
    public abstract fun initModuleApplication()

    /**
     * 所有组件的都初始化后再调用的方法
     */
    public abstract fun initModuleData()
}

(2):所有的Application都继承BaseModuleApplication,然后实现方法。

/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   :
 * version:
 */
class UserCenterApplication : BaseModuleApplication() {
    private val TAG = "UserCenterApplication"
    override fun initModuleApplication() {
        ServiceFactory.instance.setAccountService(AccountService())
        Log.e(TAG, "UserCenterApplication-initModuleApplication")

    }

    override fun initModuleData() {
        Log.e(TAG, "UserCenterApplication-initModuleData")
    }

}

(3):在Base模块中,定义ApplicationConfig,定义一个数组,我们把需要初始化的模块application的完整类名放到数组中。
class ApplicationConfig {

    companion object {
        private const val UserCenterApplication: String = "com.zx.user.UserCenterApplication"
        private const val MsgApplication: String = "com.message.MsgApplication"
        val moduleApps = listOf(UserCenterApplication,MsgApplication)
    }

}


(4):主Application的initModuleApplication,遍历数组,通过反射的方式初始化各个application。
/**
 * author : Naruto
 * date   : 2019-09-07
 * desc   :
 * version:
 */
class MainApplication : BaseModuleApplication() {

  
    override fun initModuleApplication() {
        try {
            val moduleApps = ApplicationConfig.moduleApps
            for (moduleApp in moduleApps) {
                val clazz = Class.forName(moduleApp)
                val instance = clazz.newInstance() as BaseModuleApplication
                instance.initModuleApplication()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun initModuleData() {
        try {
            val moduleApps = ApplicationConfig.moduleApps
            for (moduleApp in moduleApps) {
                val clazz = Class.forName(moduleApp)
                val instance = clazz.newInstance() as BaseModuleApplication
                instance.initModuleData()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

}

2.4:question three:组件之间的页面跳转

这里采用阿里开源的ARouter。ARouter是阿里巴巴开源路由框架,主要解决组件间、模块间的界面跳转问题。

https://blog.csdn.net/weixin_37292229/article/details/93375669

 

2.5:集成调试

在主模块的build.gradle中配置简短的代码就ok了。

if (isUserModule.toBoolean()) {

implementation project(':UserCenter')

}

本文中的模块化开发,告一段落。

Finally, I think a meaningful life is to insist on doing something embarrassing.

                                                              Come on (ง • ̀ _ •) ง               Naruto

 

 

 

 

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值