Android组件化初识

614 阅读7分钟

组件化和模块化的区别

  • 组件化的重心主要是放在业务逻辑层,主要是为了拆分业务逻辑,只针对业务逻辑
  • 模块化的重心主要是为了功能的重用,那功能拆分一个个的插件,针对整个项目

组件化和插件化的区别

  • 组件化我们不管把我们的业务拆分为多少个模块,最终在打包上线的时候我们都会生成一个apk
  • 而插件化也是拆分为很多插件模块,但是在最终打包之后,成为了很多apk,最终我们把它上传到我们的服务器上面,用户使用的时候,只需要下载响应的apk即可,然后使用动态加载技术,加载里面相应的Activity

组件化开发的优势

  • 相当于我们每次运行不需要整个项目运行,而是运行单一的组件即可
  • 如果我们要将某一个模块用到新项目中去,就很简单了,因为我们每一个模块都是一个独立的Application
  • 因为Application是不能依赖其他的Application的
  • 我们就不需要解耦合了、资源等等
  • 大团队开发中,组件化开发是开发的基石

业务逻辑层

公共层

系统层

组件化开发要遇到的问题

从组件化实战来解决问题

  • 创建组件化项目
  • 创建两个Application组件,注意修改MainActivity的类名,以防止资源命名冲突问题

  • 我们增加了这么多模块,那么每个模块里面都有build.gradle,假设后面我们需要升级某一个模块中依赖的组件的版本,我们需要修改每一个模块中的版本号么?这样肯定是不行的,所以我们需要创建一个文件,统一管理我们的版本号,而在各个模块中使用相同的版本控制工具才行
  • 这里有三种方式
  • 第一种方式:直接定义到gradle.properties,简单粗暴
  • 第二种方式:定义到我们整个项目的build中
  • 第三种方式:我们自己创建一个Gradle文件,专门去装我们这些参数
  • 我们采用第三种方式
  • 文件创建好了,但是我们的项目是不知道的,怎么让整个项目都能使用呢?
  • 在项目的build.gradle中声明即可
  • 这样声明了就是告诉项目在我们程序加载的时候去加载我们的config
  • 然后我们就可以在主App和子Module下面使用我们的config文件了
  • 改完之后,Sync now一下即可,没有报错就是成功了
  • 新的问题来了,我们通常修改完SDK版本号之后,我们下面的各种依赖库的版本也要与之对应,怎么办呢?
  • 当然,我们也可以通过同样的方式,定义一个数组来解决问题
  • 同样的方式,在各个组件的依赖下面引入即可
  • 写到这里,会有疑问,gradle文件里面,为什么有的直接用大括号?有的用等于号,有的用冒号,这里解释下,直接用大括号表示数据集的意思,后面等于号表示一个对象的意思,冒号则是某个属性啦
  • 写到这里,我们就将各种依赖库的版本号给统一管理啦
  • 下一个问题

组件如何在Application和各种Module之间来回切换?

  • 其实就是修改我们的组件的打包方式即可
  • 把此处的Application改为library
  • 但是如果我们每次都需要手动修改,就比较麻烦了
  • 我们可以添加一个开关
  • 然后在module中根据开关配置一下即可
if (rootProject.ext.android.is_application) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

apply plugin: 'kotlin-android'
  • 这个时候,我们把开关设置为false,就会发现,组件都变为Library了

AndroidManifest.xml文件的区分

  • 因为application和library的AndroidManifest.xml文件是不同的,这个时候我们就需要区分了
  • 我们再module模块中,添加一个manifest文件夹,并拷贝一份manifest文件进去
  • 然后library的manifest文件是不需要intent的,这个时候就需要我们去掉intent以及Application里面的部分代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.login">

    <application>
        <activity android:name=".LoginActivity">
        </activity>
    </application>

</manifest>
  • 我们把每个Module里面的manifest修改了之后,就需要设置一下了,根据is_applitaion属性来动态设置manifest文件
  • 在每个module模块下面添加这样一段代码即可
sourceSets{
        main{
            if (rootProject.ext.android.is_application) {
               manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            }
        }
    }

library模块中不能有applicationId

  • 这个异常是因为什么呢? 是因为我们的defaultConfig中设置了applicationId,但是library中是不需要的
  • 怎么解决这个异常呢?
  • 我们在设置applicationId的时候添加判断即可
  • 如上即可

模块设置完毕了,那么最终我们需要发布的时候,就需要让我们的主App去依赖我们的Module了

  • 如上图所示,我们让App依赖下面的两个模块

依赖之后,我们已经发布过了,但是现在我们又需要将我们的模块修改为application怎么办呢?

  • 我们改为true,发现抛出了异常
  • 异常的意思就是我们的App模块不能依赖我么的login模块和member模块,因为都是application,application不能依赖application模块
  • 那么我们就需要修改了,修改成子模块为library的时候,我们才去依赖那两个模块,否则不依赖
  • 修改app下面的build.gradle
dependencies {

    implementation rootProject.ext.dependencies.publicImplementation
    
    if (!rootProject.ext.android.is_application) {
        implementation project(path: ':login')
        implementation project(path: ':member')   
    }

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

以上就是一些基本问题,下面还有一些问题

比如我们在项目中需要知道当前项目是application还是library

  • 这个时候就需要我们创建给基础库了
  • 为什么需要基础库呢?因为我们每个模块都需要用到这些东西
  • 接下来问题来了,基础库创建好了,我们要怎么写才能让每个Module都去依赖它呢?
  • 我们先在config中定义一个other库,然后再build.gradle中通过each语法,遍历,依赖即可
  • 这样,我们以后就可以只改动一个地方,就好了
  • 接下来我们要处理参数传递的问题,我们需要将is_application参数传递给每个模块的buildConfig文件中去
  • 我们需要在我们basic模块下的build.config文件中,添加下面的代码,注意各种环境都要添加该属性
buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
        }
        advanced {
            buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
        }
        debug {
            buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
        }
    }
  • 接着我们Make一下工程
  • 查找basic的buildConfig文件,发现属性已经添加好了
  • 接下来我们就可以在basic模块下面使用了
  • 注意这里我们需要在basic模块下的application下面声明一个is_application的静态变量
  • 这样才能让我们的每一个模块都能使用上这个属性
  • 如下
package com.example.basic

import android.app.Application

/**
 * <pre>
 *     author: Jafar
 *     date  : 2020/11/11
 *     desc  :
 * </pre>
 */
class BaseApplication : Application() {

    companion object {
        const val is_application = BuildConfig.is_application
    }
    
    override fun onCreate() {
        super.onCreate()
    }
}
  • 当然,我们每一个模块不一定都能用上application,那么我们的baseActivity无疑是最合适的
package com.example.arouterproject

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

/**
 * <pre>
 *     author: Jafar
 *     date  : 2020/11/11
 *     desc  :
 * </pre>
 */
class BaseActivity : AppCompatActivity() {

    companion object {
        const val is_application = BuildConfig.is_application
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
}
  • 我们有时候,需要在模块里面编译的时候加载一些用到的类,但是打包的时候不需要,怎么设置呢?
  • 如上图所示,我们只需要创建一个文件夹,把java类拷贝进去,然后在sourceSets中添加这样一行代码即可
  • 其实这样一行代码的意思就是声明我这个目录就是一个java的文件夹
  • 这样做的好处就是,开发的时候,我们使用Application编译,使用临时的代码进行编译,但是呢?打包的时候,我们就不需要用到了,也就是当其变为library的时候,我们就不需要了。减少了无所谓的编译
  • 这种做法,就会防止我们因为不断地切换而产生的BUG
  • 解决了资源浪费的问题
  • 到此为止。我们就把细节都处理的差不多了

模块之间的跳转怎么做呢?手写路由即可!