1.組件化重構效果
這里先看下我們重構前后的框架圖比較:
重構前:
傳統代碼架構.png
重構后
組件化代碼架構.png
ft_xxx
表示業務層模塊lib_xxx
表示基礎庫模塊
重構后的架構圖如下 :
服務接口調用.png
重構前的代碼業務封裝在宿主app中,業務耦合嚴重,如果修改一個業務模塊,需要對整個app進行完整測試,測試工作量巨大
而重構后,我們只需要對單一app進行獨立調試即可。
重構后的框架結構:所有的業務組件之間通訊都通過ft_base_service
進行通訊
2.組件化重構準則
- 1.單一業務可以單獨調試,也可以作為lib提供給宿主app使用
- 2.同一級別的模塊不允許直接調用,比如我們的ft_home組件不允許直接調用ft_login組件,不然組件化的意義就不存在了
- 3.組件間通訊不能直接使用顯示的class文件跳轉,可以考慮很用ARouter框架進行解耦
- 4.每個組件可打包為aar或者jar上傳到maven私服,宿主使用的時候,直接引用私服中aar包即可
能做到以上幾點,你的app就可以稱為一個組件化框架的app了。
3.組件化重構思路
重構思路.png
-
-
拆 :拆代碼,拆資源,拆構建
由于所有業務和資源都耦合在宿主app中,所以需要將代碼和資源拆開到對應模塊中
當然我們的構建build.gradle也需要拆分到不同模塊中
-
-
-
接 :對外提供接口
組件化之間不能直接通訊,需要使用暴露接口的方式對外通訊
-
-
-
測 :反復測試
重構后代碼,需要反復測試,防止出現意想不到的bug
-
4.組件化重構過程
這里我以登錄業務ft_login
為例子:
1. 步驟1 :首先新建一個業務模塊ft_login
,然后在宿主app中將登錄功能相關聯的代碼和資源抽離到ft_login
中
2. 步驟2 :將和登錄構建相關的依賴分配到ft_login
構建中。
3. 步驟3 :單獨調試功能實現
- 3.1:在
gradle.properties
中創建一個全局變量:isRunAlone=true
- 3.2:在
build.gradle
中:
if(isRunAlone.toBoolean()){
apply plugin:'com.android.application'
}else{
apply plugin:'com.android.library'
}
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
defaultConfig {
if(isRunAlone.toBoolean()){
applicationId 'com.anna.ft_login'
}
...
}
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/res']
}
aidl {
srcDirs = ['src/main/aidl']
}
manifest {
if(isRunAlone.toBoolean()){
srcFile 'src/main/manifest/AndroidManifest.xml'
}else {
srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
}
def dependList = [rootProject.depsLibs.okhttp,
rootProject.depsLibs.gson,
rootProject.depsLibs.appcompact,
rootProject.depsLibs.design,
rootProject.depsLibs.eventbus,
rootProject.depsLibs.arouterapi,
':lib_network',':lib_common_ui',':ft_base_service']
dependencies {
if(!isRunAlone.toBoolean()){
dependList.each { String depend ->
depend.startsWithAny(':lib',':ft')? compileOnly(project(depend)):compileOnly(depend){
switch (depend){
case rootProject.depsLibs.arouterapi:
exclude group: 'com.android.support'
break;
}
}
}
}else {
dependList.each { String depend ->
depend.startsWithAny(':lib',':ft')? implementation(project(depend)):implementation(depend) {
switch (depend) {
case rootProject.depsLibs.arouterapi:
exclude group: 'com.android.support'
break;
}
}
}
}
//arouter注解處理器
annotationProcessor rootProject.depsLibs.aroutercompiler
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
單獨調試狀態下注意四點 :
- 1.引用application插件
- 2.引入applicationId
- 3.引入不同給的sourceSets構建路徑
- 4.引入的庫單獨調試狀態下需要使用
implementation
導入,不能使用compileOnly
實現上面四點, 只要打開isRunAlone就可作為一個單獨app運行了 。
4.步驟4:組件間通訊
這里,我們引入一個ft_base_service
模塊,這個模塊用來實現組件間通訊用,需要調用別的業務模塊都需要使用這個模塊才能通訊、
業務模塊與ft_base_service
之間通訊使用的是路由ARouter
:
關于ARouter
的使用可以參考這篇文章:
Android開源系列-組件化框架Arouter-(一)使用方式詳解
- 1.創建
ft_base_service
,在這個模塊中:創建一個LoginService
接口繼承IProvider
引入ARouter依賴
:
android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
//arouter核心api
implementation rootProject.depsLibs.arouterapi
//arouter注解處理器
annotationProcessor rootProject.depsLibs.aroutercompiler
創建LoginService:
public interface LoginService extends IProvider {
boolean hasLogin();
void login(Context context);
}
- 2.在
ft_login
業務模塊中實現LoginService接口
注意這里因為使用了ARouter注解,所以也需要引入ARouter
依賴
@Route(path = "/login/login_service")
public class LoginServiceImpl implements LoginService {
Context context;
@Override
public boolean hasLogin() {
return UserManager.getInstance().hasLogined();
}
@Override
public void login(Context context) {
LoginActivity.start(context);
}
@Override
public void init(Context context) {
Log.d("TAG","LoginServiceImpl is init");
}
}
- 3.在
ft_base_service
模塊中對LoginService
接口進行依賴注入
public class LoginImpl {
@Autowired(name = "/login/login_service")
public LoginService mLoginService;
private static LoginImpl mLoginImpl = null;
public static LoginImpl getInstance() {
if (mLoginImpl == null) {
synchronized (LoginImpl.class) {
if (mLoginImpl == null) {
mLoginImpl = new LoginImpl();
}
return mLoginImpl;
}
}
return mLoginImpl;
}
private LoginImpl(){
ARouter.getInstance().inject(this);
}
public boolean hasLogin(){
return mLoginService.hasLogin();
}
public void login(Context context){
mLoginService.login(context);
}
}
筆者使用了一個 單例類LoginImpl ,在構造器中對LoginService
依賴注入
ARouter.getInstance().inject(this);
然后宿主app或者其他模塊引用登錄業務功能時,需要依賴ft_base_service
模塊,并使用LoginImpl
的接口即可。
這里要說明下,平時我們使用的四大組件跳轉也可以使用這個方式來處理,在服務接口中定義跳轉接口即可。當然也可以使用Arouter的Activity跳轉方式或者Fragment實例獲取方式
- 5.代碼打包aar上傳到
maven私服
:
關于這塊maven私服更多內容可以參考這篇文章:
Gradle筑基篇(六)-使用Maven實現組件化類庫發布
這里我們封裝了一個通用組件發布庫:
apply plugin: 'maven'
uploadArchives {
repositories {
mavenDeployer {
// 是否快照版本
def isSnapShot = Boolean.valueOf(MAVEN_IS_SNAPSHOT)
def versionName = MAVEN_VERSION
if (isSnapShot) {
versionName += "-SNAPSHOT"
}
// 組件信息
pom.groupId = MAVEN_GROUP_ID
pom.artifactId = MAVEN_ARTIFACTID
pom.version = versionName
// 快照倉庫路徑
snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) {
authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
// 發布倉庫路徑
repository(url: uri(MAVEN_RELEASE_URL)) {
authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
println("###################################"
+ "\\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging
+ "\\nrepository =" + (isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL)
+ "\\n###################################"
)
}
}
}
然后在對應的組件下面引用:
apply from:file('../maven.gradle')
發布的時候直接在Gradle
面板中點擊uploadArchives
任務即可
task面板.png
經過上面幾個步驟就基本完成了login組件的封裝并發布,且對外提供了login組件接口 其他組件也是按照上面的邏輯進行重構
更多詳細信息可以自己拿到項目源代碼查看。
5.組件化重構總結
組件化不僅是一種架構,更是一種思想,架構是可以變得,但是核心思想卻是統一的,在拆分代碼的時候,要注意模塊的顆粒度,不是顆粒度越小就越好,模塊分離的好,后期對組件改造會有很大幫助, 關于組件化的文章就講到這里,組件化重構的項目已經上傳到Github。后面會出一期插件化
的項目改造。敬請期待。
** demo地址: https://github.com/ByteYuhb/anna_music_app **
-
Android
+關注
關注
12文章
3943瀏覽量
127743 -
APP
+關注
關注
33文章
1577瀏覽量
72671 -
代碼
+關注
關注
30文章
4823瀏覽量
68900
發布評論請先 登錄
相關推薦
評論