前语

  在进行安装开发的时分咱们常常会对项目进行不同事务逻辑处理分包,例如专门处理网络、数据库、事务逻辑代码的,假如咱们不分包则一切东西写在一同,势必在开发过程中会浪费许多时刻在寻找文件上,而分包的思想源于模块化,例如一些utils类,专门放到一个包下面,这样在运用的时分就能很快找到。节约开发时刻,下降后期保护本钱,那么组件化是什么?

正文

  在上面咱们提到模块化,能够依据不同的功能进行进一步的封装,放到一个新建的模块下面,例如登录注册模块,个人中心模块之类的,可是模块化时就会涉及到一个分体,不同模块之间存在事务逻辑上的耦合,这种耦合开发过程中还不是最苦楚的,苦楚的是你需求复用其中某一个模块的时分,你需求一点一点的剥离出去,作业量乃至不亚于从头写,由于这时你的脑海里需求有本来项目的逻辑,还有的新项目的逻辑,还要想一下怎样迁移曩昔合理,为了解决模块化耦合严峻的问题,就呈现了组件化。

  模块化是一个App主模块依靠多个Android子模块,只能运转App主模块;组件化是多个App主模块,各个模块都能独立运转测试。那么是否要运用模块化呢?这取决于你的项目体量了,假如一开端你不确认项目体量。那么就只用模块化,确认的化就运用组件化,也能够模块化转组件化。这个看实际情况而定,而假如项目太小了,你乃至都不需求用什么模块化组件化,一个MainActivity就搞定了,何必搞那么复杂呢?

  总而言之,言而总之。这个组件化知识,你或许现在用不上,可是你学会了总有用武之地,正所谓,技多不压身嘛!下面开端实操环节,或许比你幻想的还要简略呢?

一、创立项目

  首要咱们创立一个名为StudyComponent的项目,项目创立好之后就能够看到app模块了。

Android 组件化(一)项目模式管理、模式切换

  这便是咱们的app主模块,也便是所谓的App的壳,它里边要有一个个组件构成,那么下面咱们需求创立组件,这儿做一个假设,我的App有登录注册和个人中心两个内容,怎样把这两个内容变成两个组件呢?首要要做的便是创立这两个组件,首要创立login组件,先将项目从Android形式切换到Project形式,然后鼠标右键点击StudyComponent → New → Module,呈现弹窗,这儿挑选的是Phone & Tablet的办法。

Android 组件化(一)项目模式管理、模式切换

点击Next,会需求你创立一个Activity,挑选Empty Activity。

Android 组件化(一)项目模式管理、模式切换

  点击Next,给你的Activity命名,这儿要将Activity的姓名改变一下,由于组件化,在打包的时分是不允许有重复资源的,MainActivity咱们在app组件中已经有了,所以其他组件中要防止重复。

Android 组件化(一)项目模式管理、模式切换

  点击Next,等待项目创立完结,创立完结之后你会看到login组件,一起能够自在的切换当时所运转的是app组件仍是login组件,现在能够试试看。

Android 组件化(一)项目模式管理、模式切换

参阅创立login组件的办法,咱们再创立一个personal组件,里边的Activity就改成PersonalActivity即可,其他的都差不多。

Android 组件化(一)项目模式管理、模式切换

  现在就有三个组件了,三个组件现在各自独立,那么咱们调试运转的时分能够经过切换不同的组件进行,打包要怎样办呢?

二、项目形式切换

  咱们希望在打包的时分app组件依靠login和personal组件,那么咱们就需求对这两个组件进行切换,切换之前咱们需求知道它们当时是什么形式,在哪里看呢?

Android 组件化(一)项目模式管理、模式切换

  这儿的application就表明你这是工程项目,而假如要被app组件依靠的话,则需求变成library,那么怎样变呢?在考虑怎样变之前,咱们首要应该知道library是什么姿态,所以咱们应该创立一个根底模块,一切的组件又依靠这个根底模块。

flowchart TD
    A(App)
    A--依靠-->B(login)
   A--依靠-->C(personal)
   B--依靠-->D(basic)
   C--依靠-->D(basic)

经过这个流程图就能很好的了解这个思想了,下面咱们创立basic模块。

Android 组件化(一)项目模式管理、模式切换

  创立模块的办法和之前相同,只不过这儿咱们就要挑选Android Library进行创立了,它里边是没有让你去创立Activity的,点击Finish即可。

① 比照build.gradle

  下面要做的便是比照一下组件和模块下的build.gradle中的差异,看看那些相同,那些不同,先看login组件的build.gradle。

plugins {
    id 'com.android.application'
}
android {
    namespace 'com.llw.login'
    compileSdk 32
    defaultConfig {
        applicationId "com.llw.login"
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

再看base下的build.gralde

plugins {
    id 'com.android.library'
}
android {
    namespace 'com.llw.base'
    compileSdk 32
    defaultConfig {
        minSdk 23
        targetSdk 32
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

  那么现在就能得出结论了,从上到下都有三个闭包,别离是plugins{}、android{}、dependencies{},先从plugins{}开端分析。

(一)plugins{}

组件中:

id 'com.android.application'

模块中:

id 'com.android.library'

那么咱们需求依据一个变量来更改是application仍是library。

(二)android{}

再分析android{}:

组件中:

android {
    namespace 'com.llw.login'
    compileSdk 32
    defaultConfig {
        applicationId "com.llw.login"
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    ...
}

模块中:

android {
    namespace 'com.llw.base'
    compileSdk 32
    defaultConfig {
        minSdk 23
        targetSdk 32
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }
    ...
}

  这个闭包中,相同的内容就不看了,看那些不同的,首要namespace不必改动,compileSdk 、minSdk 、targetSdk 能够集顶用一个变量办理,这样改的时分也方便一步到位。然后便是不同的当地,applicationId、versionCode、versionName在模块中没有,组件中有,这个需求留意。dependencies{}就没有啥好说的,能够说彻底相同,集中处理就能够了。

② 比照AndroidManifest.xml

还有一处不同便是组件下的AndroidManifest.xml中是有内容的:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.StudyComponent">
        <activity
            android:name=".LoginActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>
</manifest>

而在模块下的AndroidManifest.xml中的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

  这意味中咱们需求在组件中再准备一个模块的AndroidManifest.xml文件,依据当时是组件仍是模块进行不同的引证。到现在为止咱们都没有写过代码,下面开端写代码,写代码之前。你能够切换app、login、personal别离运转一下,你就会发现有三个使用,每个使用你都能够单独运转。

③ 办理项目参数

  先梳理一下,咱们现在有三个组件:app、login、personal,一个模块:basic。模块和组件里边有许多内容是相同的,例如版别号、编译SDK版别什么的,还有一些依靠库版别,jdk版别,假如我没有每一个的去改无疑很费事,所以咱们将这些信息界说到一个文件中,假如有依靠库的版别改变了只要改这个文件就能够了,那就很方便了,右键点击你的StudyComponent → New → File ,输入config.gradle。

Android 组件化(一)项目模式管理、模式切换

  回车,在咱们的工程目录下创立了一个config.gradle文件,这也是一个gradle文件,里边的代码需求咱们自己去写,留意看这个文件的所在位置和你的工程build.gradle是同一级其他,假如你不是这样的,阐明你创立文件的位置错了,需求从头创立。

Android 组件化(一)项目模式管理、模式切换

gradle里边是有一种编程语言的,是Groovy,感兴趣的话能够去了解一下。先把里边的代码写进去再来阐明一下,config.gradle的代码如下:

//项目工程装备
ext {
    //基本信息装备
    android = [
            compileSdk   : 32,    //编译SDK版别
            minSdk       : 23,    //最低运转SDK版别
            targetSdk    : 32,    //目标SDK版别
            versionCode  : 1,     //项目版别号
            versionName  : "1.0", //项目版别名
            isApplication: true   //是否为Application
    ]
    //编译JDK装备
    compileOptions = [
            sourceCompatibility: JavaVersion.VERSION_11,
            targetCompatibility: JavaVersion.VERSION_11,
    ]
    //依靠装备
    dependencies = [
            publicImplementation : [
                    'androidx.appcompat:appcompat:1.4.1',
                    'com.google.android.material:material:1.6.0',
                    'androidx.constraintlayout:constraintlayout:2.1.4'
            ],
            publicTestImplementation : [
                    'junit:junit:4.13.2'
            ],
            publicAndroidTestImplementation: [
                    'androidx.test.ext:junit:1.1.3',
                    'androidx.test.espresso:espresso-core:3.4.0'
            ],
            other : [
                    ':basic'
            ]
    ]
}

  这儿的代码是Groovy的语法,ext你能够看做一个类名,android、compileOptions 、dependencies能够看做ext的内部类,中括号里边的便是类的特点值,特点名不能重复。特点值这儿咱们就界说了一些项目中需求用的特点,经过注释你应该明白这些特点是什么意思了,要运用它,首要需求让咱们的工程知道有这样一个文件,在工程的build.gradle中新增如下所示代码:

apply from: "config.gradle"

添加位置如下图所示:

Android 组件化(一)项目模式管理、模式切换

然后Sync Now,下面运用这个文件,

(一)修正app的build.gradle

修正一下app下的build.gradle中代码,如下所示:

plugins {
    id 'com.android.application'
}
android {
    namespace 'com.llw.component'
    compileSdk rootProject.ext.android.compileSdk
    defaultConfig {
        applicationId "com.llw.component"
        minSdk rootProject.ext.android.minSdk
        targetSdk rootProject.ext.android.targetSdk
        versionCode rootProject.ext.android.versionCode
        versionName rootProject.ext.android.versionName
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility
        targetCompatibility rootProject.ext.compileOptions.targetCompatibility
    }
}
dependencies {
    implementation rootProject.ext.dependencies.publicImplementation
    implementation rootProject.ext.dependencies.publicTestImplementation
    implementation rootProject.ext.dependencies.publicAndroidTestImplementation
    rootProject.ext.dependencies.other.each {
        implementation project(it)
    }
    //不是组件时才依靠
    if (!rootProject.ext.android.isApplication) {
        implementation project(path: ':login')
        implementation project(path: ':personal')
    }
}

  app模块就一直是application,不需求切换到library,然后修正compileSdk 的值,rootProject表明这个工程,这儿引证ext,再引证android,最后找到compileSdk,实际的值便是32,其他的特点设置也是这个道理,这儿面的dependencies 中需求引证basic模块,这样写的优点便是,有一天我的basic模块改成了network模块,我只需求修正config.gradle中的值就能够了,道理和其他大局特点相同。而最后这儿的依靠其他的组件,需求在他们是library的时分才依靠,不是则不依靠。

(二)修正basic模块

basic作为其他组件依靠的根底模块,任何时分都是library形式,这一点和app模块恰恰相反,修正一下basic的build.gradle中代码,如下所示:

plugins {
    id 'com.android.library'
}
android {
    namespace 'com.llw.basic'
    compileSdk rootProject.ext.android.compileSdk
    defaultConfig {
        minSdk rootProject.ext.android.minSdk
        targetSdk rootProject.ext.android.targetSdk
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            buildConfigField 'boolean', 'isApplication',
                    rootProject.ext.android.isApplication.toString()
        }
        advanced {
            buildConfigField 'boolean', 'isApplication',
                    rootProject.ext.android.isApplication.toString()
        }
        debug {
            buildConfigField 'boolean', 'isApplication',
                    rootProject.ext.android.isApplication.toString()
        }
    }
    compileOptions {
        sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility
        targetCompatibility rootProject.ext.compileOptions.targetCompatibility
    }
}
dependencies {
    implementation rootProject.ext.dependencies.publicImplementation
    implementation rootProject.ext.dependencies.publicTestImplementation
    implementation rootProject.ext.dependencies.publicAndroidTestImplementation
}

  许多内容是相似的,不过这儿有一点不相同,那便是界说了一个变量isApplication,这个变量的作用便是用来告知当时这个模块是application形式仍是library形式,这很重要,由于app、login、personal在application形式下是各自独立的,而他们都需求承继basic,所以这个区分模块形式的作业就在basic完结,而basic需求做的任务还有其他,在com.llw.basic包下新建一个BaseActivity类,代码如下:

public class BaseActivity extends AppCompatActivity {
    protected boolean isApplication;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        isApplication = BuildConfig.isApplication;
    }
    protected void showMsg(CharSequence msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

还有Application的处理,在com.llw.basic包下新建一个BaseApplication类,代码如下:

public class BaseApplication extends Application {
    public static boolean isApplication;
    @Override
    public void onCreate() {
        super.onCreate();
        isApplication = BuildConfig.isApplication;
    }
}

  其他组件依靠了basic之后能够经过承继这两个类得知当时属于什么形式。假如呈现BuildConfig报红,编译一下,假如还爆红,就从头导包,basic模块中的改动就完结了。

(三)修正login组件

  修正这个login组件就有一些不相同了,由于它既有或许是application也有或许是library。当时咱们的login仍是application,那么对应的AndroidManifest.xml便是惯例的,而假如变成了library形式时,这个AndroidManifest.xml也需求有改变,所以这儿咱们就需求两个AndroidManifest.xml,一个在application时用,一个在library时用。所以在login组件的main文件夹下新建一个manifest文件夹,然后再创立一个AndroidManifest.xml

Android 组件化(一)项目模式管理、模式切换

这个AndroidManifest.xml的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
    </application>
</manifest>

  你再把application标签去掉就和basic中的AndroidManifest.xml彻底相同了,这儿改好之后,咱们再去修正login组件的build.gradle,代码如下:

if (rootProject.ext.android.isApplication) {//修正插件的类型
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {
    namespace 'com.llw.login'
    compileSdk rootProject.ext.android.compileSdk
    defaultConfig {
        if (rootProject.ext.android.isApplication) {//application时需求id,版别号、版别名
            applicationId "com.llw.login"
            versionCode rootProject.ext.android.versionCode
            versionName rootProject.ext.android.versionName
        }
        minSdk rootProject.ext.android.minSdk
        targetSdk rootProject.ext.android.targetSdk
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            if (rootProject.ext.android.isApplication) {//修正运用的AndroidManifest
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            }
        }
    }
    compileOptions {
        sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility
        targetCompatibility rootProject.ext.compileOptions.targetCompatibility
    }
}
dependencies {
    implementation rootProject.ext.dependencies.publicImplementation
    implementation rootProject.ext.dependencies.publicTestImplementation
    implementation rootProject.ext.dependencies.publicAndroidTestImplementation
    rootProject.ext.dependencies.other.each {
        implementation project(it)
    }
}

  上面的修正代码首要便是组件是application时怎样做,library时怎样做,相信你能够看懂,最后咱们修正一下activity_login.xml中TextView显现的文本内容,由Hello World!改成Login。

Android 组件化(一)项目模式管理、模式切换

  一起咱们修正一下LoginActivity中的代码,让它承继自basic模块中的BaseActivity,运用父类的办法showMsg,弹出Toast。

Android 组件化(一)项目模式管理、模式切换

(四)修正personal组件

  这儿的修正办法大致是相同的,我仍是重复描绘一遍吧,由于我怕你跟着操作到这儿就不会了,首要相同需求一个AndroidManifest.xml,你能够直接把login组件中所写的仿制过来。

Android 组件化(一)项目模式管理、模式切换

然后修正personal的build.gradle,代码如下:

if (rootProject.ext.android.isApplication) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {
    namespace 'com.llw.personal'
    compileSdk rootProject.ext.android.compileSdk
    defaultConfig {
        if (rootProject.ext.android.isApplication) {
            applicationId "com.llw.personal"
            versionCode rootProject.ext.android.versionCode
            versionName rootProject.ext.android.versionName
        }
        minSdk rootProject.ext.android.minSdk
        targetSdk rootProject.ext.android.targetSdk
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            if (rootProject.ext.android.isApplication) {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            }
        }
    }
    compileOptions {
        sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility
        targetCompatibility rootProject.ext.compileOptions.targetCompatibility
    }
}
dependencies {
    implementation rootProject.ext.dependencies.publicImplementation
    implementation rootProject.ext.dependencies.publicTestImplementation
    implementation rootProject.ext.dependencies.publicAndroidTestImplementation
    rootProject.ext.dependencies.other.each {
        implementation project(it)
    }
}

  除了有包名不相同以外,其他都和login组件的build.gralde相同,下面相同修正一下activity_personal.xml。

Android 组件化(一)项目模式管理、模式切换

然后修正PersonalActivity的代码。

Android 组件化(一)项目模式管理、模式切换

现在基本上就改完了。

④ 组件运转

现在app、login、personal都是组件,咱们别离运转一下试试看。

Android 组件化(一)项目模式管理、模式切换

  经过这儿切换需求运转的项目,下面咱们依次运转一下app、login、personal,运转作用如下图所示。

Android 组件化(一)项目模式管理、模式切换
Android 组件化(一)项目模式管理、模式切换
Android 组件化(一)项目模式管理、模式切换

  从这几个图来看,咱们的组件依靠basic模块没有问题,其次便是组件各自为一个项目,你现在手机上应该有三个使用才对。

⑤ 切换形式

  现在login和personal在application下能够正常运转,那假如在library时,app就依靠了login和personal,此时就只有app这一个组件了,那么这个时分app能不能正常运转呢?

Android 组件化(一)项目模式管理、模式切换

下面咱们把config.gradle中的isApplication的值从ture改成false,然后Sync Now。

Android 组件化(一)项目模式管理、模式切换

  就能够看到login和personal,现在不能够正常独立运转了,有一个 ,然后留意看图标也变了,不是和app组件相同了,而是和basic相同了,这阐明咱们切换组件的项目形式是可行了,那么下面咱们运转一下app组件,看能否正常运转。我这边是能够正常运转,不知道你那边怎样样。

三、源码

  后面每次文章更新对应新的分支,源码地址指向分支地址,这样就不会让你看代码的时分和文章中所写的不一致了。

源码地址:StudyComponent

欢迎 Star 和 Fork