前语

音视频模块算是更完了,当然还有部分较为详细的仍是没有更进去,随后抽空加进文档里吧。前段时间看过一本书,把组件化这块讲的很透彻,拿来和咱们分享下。

重视公众号:Android苦做舟
解锁 《Android十二大板块PDF》
音视频大合集,从初中高到面试包罗万象;让学习更靠近未来实战。已构成PDF版

十二个模块PDF内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开始一同学
4.功用优化大合集,离别优化烦恼
5.Framework大合集,从里到外剖析的明明白白
6.Flutter大合集,进阶Flutter高档工程师
7.compose大合集,拥抱新技能
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基稳固楼房平地起
11.Flutter番外篇:Flutter面试+项目实战+电子书
12.大厂高档Android组件化强化实战

整理不易,重视一下吧。开始进入正题,ღ( ・ᴗ・` )

第一章:初始Android组件化

1.1.组件化和模块化的差异

组件化便是把能够复用的、独立的、根底的、功用专一的代码封装到一个办法或许代码片段里,在未来需求的当地引进运用。用极少的代码完结之前相同的功用,避免了相同功用代码的复写,提高了开发的功率。在未来对改组件功用进行修正的时分只需求修正组件代码就可修正项目里一切的相同功用。组件化归于纵向分块,每个组件就像一个竖直的线永不相交。

总结:组建好的重心首要是放在事务逻辑层,首要是为了拆分事务逻辑,只针对事务逻辑

模块化是为了独自完结某一功用模块进行封装的办法,一个模块里或许具有n个根底组件搭配产生。模块化归于横向分块,每个模块像一条横向把n条竖直的线串联起来构成一个全体。

总结:模块化的重心首要是为了功用的重用,拿功用拆分一个个的插件,针对整个项目。

对比

关于Android组件化的深度分析篇(一)初始组件化

1.2.组件化和插件化的差异

组件化:是将一个App分红多个模块,每个模块都是一个组件(module),开发进程中能够让这些组件相互依靠或独立编译、调试部分组件,可是这些组件终究会兼并成一个完好的Apk去发布到运用商场。

总结:组件化咱们不论把咱们的事务拆分未多少个模块,终究在打包上线的时分咱们都会生成一个APK

插件化:是将整个App拆分红许多模块,每个模块都是一个Apk(组件化的每个模块是一个lib),终究打包的时分将宿主Apk和插件Apk分隔打包,只需发布宿主Apk到运用商场,插件Apk经过动态按需下发到宿主Apk

总结:插件化也是拆分未许多插件模块,可是终究打包之后,成为许多apk,终究咱们把它上传到咱们的服务器上面,用户运用的时分,只需求下载呼应的apk即可,然后运用动态加载技能,加载里边相应的activity.

1.3.组件化开发的优势

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

关于Android组件化的深度分析篇(一)初始组件化

1.4.组件化开发要遇到的问题

事务逻辑层:公共层和体系层

组件化开发要留意的几个点

1.要留意包名和资源文件命名抵触问题
2.Gradle中的版别号的一致办理
3.组件在APPlication和Library之间怎样做到随意切换。
4.AndroidManifest.xml文件的区别
5.Library不能在Gradle文件中有applicationld

1.5.从组件化实战视点来处理问题

创立组件化项目→创立两个APPlication组件,留意修正MainActivity的类名,以避免资源命名抵触问题

关于Android组件化的深度分析篇(一)初始组件化

关于Android组件化的深度分析篇(一)初始组件化
咱们增加了这么多模块,那么每个模块里边都有build.gradle,假设后边咱们需求晋级某一个模块中依靠的组件的版别,咱们需求修正每一个模块中的版别号么?这样肯定是不行的,所以咱们需求创立一个文件,一致办理咱们的版别号,而在各个模块中运用相同的版别操控东西才行

这儿有三种方法

  • 第一种方法:直接界说到gradle.properties,简略粗暴
  • 第二种方法:界说到咱们整个项目的build中
  • 第三种方法:咱们自己创立一个Gradle文件,专门去装咱们这些参数

咱们采用第三种方法

关于Android组件化的深度分析篇(一)初始组件化
文件创立好了,可是咱们的项目是不知道的,怎样让整个项目都能运用呢?

在项目的build.gradle中声明即可

关于Android组件化的深度分析篇(一)初始组件化
这样声明晰便是告知项目在咱们程序加载的时分去加载咱们的config

然后咱们就能够在主App和子Module下面运用咱们的config文件了

关于Android组件化的深度分析篇(一)初始组件化
改完之后,Sync now一下即可,没有报错便是成功了

新的问题来了,咱们一般修正完SDK版别号之后,咱们下面的各种依靠库的版别也要与之对应,怎样办呢?

当然,咱们也能够经过相同的方法,界说一个数组来处理问题

关于Android组件化的深度分析篇(一)初始组件化
相同的方法,在各个组件的依靠下面引进即可

关于Android组件化的深度分析篇(一)初始组件化
写到这儿,会有疑问,gradle文件里边,为什么有的直接用大括号?有的用等于号,有的用冒号,这儿解说下,直接用大括号表明数据集的意思,后边等于号表明一个目标的意思,冒号则是某个特点啦

写到这儿,咱们就将各种依靠库的版别号给一致办理啦

下一个问题:组件化怎样在Application和各种Module之间来回切换?

其实便是修正咱们的组件的打包方法即可

关于Android组件化的深度分析篇(一)初始组件化
把此处的Application改为library

可是假如咱们每次都需求手动修正,就比较麻烦了

咱们能够增加一个开关

ext {
     android = {
             compilesdkversion: 29,
             buildToolsversion: "30.0.0",
             minsdkversion    : 19,
             targetsdkversion : 29,
             versionCode      : 1,
             versionName      : "1.0",
             is_application   : true
     }
     dependencies = {
            publicImplementation: {
                    "org.jetbrains.kotlin:kotlin-stdlid:$kotlin_version",
                    'androidx.core:core-ktx:1.3.1',            
                    'androidx.appcompat:appcompat:1.2.0'      
                    'com.google.android.material:material:1.2.1',
                    'androidx.constraintlayout:constraintlayout:2.0.2'
            }
     }
 }

然后在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

关于Android组件化的深度分析篇(一)初始组件化
这个反常是由于什么呢?是由于咱们的defaultConfig中设置了applicationId,可是library中是不需求的

怎样处理这个反常呢?咱们在设置applicationId的时分增加判别即可

关于Android组件化的深度分析篇(一)初始组件化
这样即可。

模块设置

模块设置完毕了,那么终究咱们需求发布的时分,就需求让咱们的主App去依靠咱们的Module了

关于Android组件化的深度分析篇(一)初始组件化

关于Android组件化的深度分析篇(一)初始组件化

关于Android组件化的深度分析篇(一)初始组件化

如上图所示,咱们让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

这个时分就需求咱们创立给根底库了

为什么需求根底库呢?由于咱们每个模块都需求用到这些东西

关于Android组件化的深度分析篇(一)初始组件化

接下来问题来了,根底库创立好了,咱们要怎样写才能让每个Module都去依靠它呢?

咱们先在config中界说一个other库,然后再build.gradle中经过each语法,遍历,依靠即可.

关于Android组件化的深度分析篇(一)初始组件化

这样,咱们今后就能够只改动一个当地,就好了

接下来咱们要处理参数传递的问题,咱们需求将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文件,发现特点现已增加好了

关于Android组件化的深度分析篇(一)初始组件化
接下来咱们就能够在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)
   }
}

咱们有时分,需求在模块里边编译的时分加载一些用到的类,可是打包的时分不需求,怎样设置呢?

关于Android组件化的深度分析篇(一)初始组件化

如上图所示,咱们只需求创立一个文件夹,把java类拷贝进去,然后在sourceSets中增加这样一行代码即可

其实这样一行代码的意思便是声明我这个目录便是一个java的文件夹,这样做的好处便是,开发的时分,咱们运用Application编译,运用暂时的代码进行编译,可是呢?打包的时分,咱们就不需求用到了,也便是当其变为library的时分,咱们就不需求了。减少了无所谓的编译这种做法,就会避免咱们由于不断地切换而产生的BUG处理了资源糟蹋的问题

到此为止。咱们就把细节都处理的差不多了

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

1.6.Android组件化根底

当 App 项目复杂必定的程度,将项目组件化是必不可少的,组件化能够更好的进行功用的划分,提到组件化有人或许会想到模块化,其实组件化和模块化的本质是相同的,都是为了代码重用的事务解耦,模块化首要依照事务划分,而组件化首要依照功用划分,从组件化最根底的几个方面翻开组件化的大门。

  1. 组件之间的跳转
  2. 动态创立
  3. 资源抵触
  4. 静态常量

组件之间的跳转

组件化中两个功用模块时不直接依靠的,其依靠规矩是经过 Base module 直接依靠,当组件之间的 Activity 进行界面跳转时,由于没有相互依靠的关系,往往会无法引证另一个 module 中的Activity。

隐式跳转

隐式跳转是经过 Android 原生 Intent 匹配机制来完结相应跳转,便是运用 Action 来跳转到对应的 Activity,这样运用隐式跳转的方法就能够跨 module 完结 Activity 之间的跳转了,留意一点,假如移出 Activity 所在的 module 而不移出相应的跳转,假如继续跳转会出现反常,运用隐式 Intent 跳转需求验证是否会接纳该 Intent,需求对该Intent目标调用 resolveActivity() 办法来判别至少有一个运用能够处理该 Intent,经过隐式跳转的方法还能够设置exported 为 false 来保证只要自己的 App 才能够启动对应的组件。

ARouter跳转

在 Android 开发中可将 module 看成不同的网络,而对应的 Router 便是连接各个 module 的中转站,这个中转站能够对页面跳转的参数等进行一致处理,ARouter 是阿里开源出来的一个页面跳转路由,运用 ARouter 能够代替隐式跳转来完结不同 module、不同组件之间的跳转以及跳转进程的监听、参数的传递等,ARouter 支持途径跳转和 URL跳转两种方法,运用也十分灵活,ARouter 的详细运用这儿不做介绍,其详细运用会在独自一篇文章中详解,ARouter 与 Android 传统跳转方法的对比方下:

  1. 显现跳转需求依靠于类,而路由跳转经过指定的途径跳转;
  2. 隐式跳转经过 AndroidManifest 集中办理,导致协作开发困难;
  3. 原生运用 AndroidManifest 来注册,而路由运用注解注册
  4. 原生 startActivity 之后跳转进程交由 Android 体系操控,而路由跳转采用的是 AOP 切面编程可对跳转进程进行拦截和过滤。

动态创立

组件化开发中最重要的一点便是各个模块、各个组件之间要尽或许解耦,这样很简单就会想到运用 Java 中的反射机制,运用反射可在运转状态下获取某个类的一切信息,然后就能够动态操作这个类的特点和办法了。假如 Fragment独自作为一个组件来运用时,当这个 Fragment 组件不需求被移出后,假如是常规的 Fragment 则会由于索引不到该Fragment 而使得 App 溃散,想一下假如运用反射创立 Fragment 的方法则至少不会引起 App 溃散,这儿能够捕捉反常完结相关逻辑,这样是不是降低了耦合呢。可见,虽然反射有必定的功用问题,但运用反射确实能在必定程度上降低耦合,学习组件化 Java 反射机制应该是有必要的一部分。

组件化开发中要求每个组件都能独立运转,一般状况下每个组件都有必定的初始化步骤,最好的一种状况是项目需求的几个组件的初始化根本相同,那就可将初始化放在 BaseModule 中进行一致初始化,可是这种状况毕竟比较理想,一般状况是每个组件的初始化都不相同,或许你会想到在各自的 Application 初始化,假如在各自的Application 中初始化,当在终究编译由于 Application 的兼并不免会出一些问题,这种方法也不可取,到这儿又想到了反射,在各组件中创立初始化文件,然后在终究的 Application 中经过反射完结各个组件的初始化操作,这儿经过 Java 的反射机制完结了组件化开发中 Application 的动态装备。

资源抵触

组件化开发进程中,假如 ModuleA 的AmdroidManifest 文件中运用 android:name 特点指定了相应的Application,而主 App Module 的 AndroidManifest 文件中也运用 android:name 特点指定了相对应的Application,此刻就有必要在 主App Module 的 AndroidManifest 文件中运用tools:replace=”android:name” 来处理抵触,运用 replace 特点表明该特点也便是在 标签下的 android:name 特点可在编译进程中被替换,这样依据AndroidManifest 文件替换规矩终究指定的 Application 应该是 App Module 中的指定的 Application。

举一个比如,我在项目中的某个功用 Module 中运用 SMSSDK 来完结短信验证的功用,由于其他当地不用,所以只引进到了要运用的功用 Module 中,假如其他 Module 会运用应该将 SMSSDK 引进到 BaseModule 中,运用SMSSDK 假如不指定该 Module 的Application,MobSDK 会将com.mob.MobApplication 指定为该 Module 的Application,此刻在全体编译打包时就会出现 AndroidManifest 文件的 android:name 特点抵触,当然了处理办法便是运用 replace 特点了。 AndroidManifest 文件兼并后的首要抵触也便是这个问题了,当然 下的其他特点有抵触,也是运用 replace 特点。在实践的开发中多验证会更有收成喔。

组件化开发中另外需求留意的一点是避免资源称号相同导致终究兼并的时分,由于抵触造成资源引证错误或许某些资源丢掉等,如字符串、色彩值等资源等兼并的时分会被后边加载的相同称号的资源所替换,处理的思路是在资源命名上要有必定的规矩,能够在 build.gradle 文件中装备”resourcePrefix “组件称号”” 的方法强制约束开发者保证资源称号唯一,主张 Module 中资源的命名格局为 “Module称号功用其他”。

静态常量

组件化开发中,终究兼并时每个组件都是以 Lib Module 的形式存在,而 Lib Module 中 R.java 文件中界说的静态变量没有声明为 final,这就意味着不能在组件 Module 中运用相对应的常量了,如在时分 switch 句子就不能运用了,这就要求在组件中要运用 if 句子来代替 switch 句子,当然在组件独立运转的时分是没有这个问题的。

开发中经常会运用到 Butterknife,Butterknife 可十分方便的对 View 及 View 的事件等进行注解操作,它采用的是编译时注解机制,注解中只能运用常量,所以在 Butterknife 在组件化开发中应该运用 R2 代替 R,R2 实践上是 R 的拷贝, R2 对应声明的变量是 final,所以在组件化开发中假如运用 Butterknife 在相应的注解中要运用 R2 代替 R。

重视公众号:Android苦做舟
解锁 《Android十二大板块PDF》
音视频大合集,从初中高到面试包罗万象;让学习更靠近未来实战。已构成PDF版

十二个模块PDF内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开始一同学
4.功用优化大合集,离别优化烦恼
5.Framework大合集,从里到外剖析的明明白白
6.Flutter大合集,进阶Flutter高档工程师
7.compose大合集,拥抱新技能
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基稳固楼房平地起
11.Flutter番外篇:Flutter面试+项目实战+电子书
12.大厂高档Android组件化强化实战

整理不易,重视一下吧。ღ( ・ᴗ・` )