為何要組件化開發(fā)
最近也不知道什么原因就突然換了公司兴溜,新的公司新的需求。由于新的項(xiàng)目比較大耻陕,各個(gè)模塊需要解耦拙徽,所以就用到了組件化開發(fā)。而且APP版本不斷的迭代诗宣,新功能的不斷增加膘怕,業(yè)務(wù)也會變的越來越復(fù)雜,APP業(yè)務(wù)模塊的數(shù)量有可能還會繼續(xù)增加召庞,而且每個(gè)模塊的代碼也變的越來越多岛心,這樣發(fā)展下去單一工程下的APP架構(gòu)勢必會影響開發(fā)效率,增加項(xiàng)目的維護(hù)成本篮灼,每個(gè)工程師都要熟悉如此之多的代碼忘古,將很難進(jìn)行多人協(xié)作開發(fā),而且Android項(xiàng)目在編譯代碼的時(shí)候電腦會非匙缬眨卡髓堪,又因?yàn)閱我还こ滔麓a耦合嚴(yán)重,每修改一處代碼后都要重新編譯打包測試娘荡,導(dǎo)致非常耗時(shí)干旁,最重要的是這樣的代碼想要做單元測試根本無從下手,所以必須要有更靈活的架構(gòu)代替過去單一的工程架構(gòu)炮沐。
怎么組建化開發(fā)
上圖是組件化工程模型疤孕,為了方便理解這張架構(gòu)圖,下面會列舉一些組件化工程中用到的名詞的含義:
1.集成模式 :所有的業(yè)務(wù)組件被“app殼工程”依賴央拖,組成一個(gè)完整的APP;
2.組件模式:可以獨(dú)立開發(fā)業(yè)務(wù)組件,每一個(gè)業(yè)務(wù)組件就是一個(gè)APP鲜戒;
3.app殼工程:負(fù)責(zé)管理各個(gè)業(yè)務(wù)組件专控,和打包apk,沒有具體的業(yè)務(wù)功能遏餐;
4.業(yè)務(wù)組件:根據(jù)公司具體業(yè)務(wù)而獨(dú)立形成一個(gè)的工程伦腐;
5.功能組件 提供開發(fā)APP的某些基礎(chǔ)功能,例如打印日志失都、樹狀圖等柏蘑;
6.Main組件 屬于業(yè)務(wù)組件,指定APP啟動(dòng)頁面粹庞、主界面咳焚;
7.Common組件 屬于功能組件,支撐業(yè)務(wù)組件的基礎(chǔ)庞溜,提供多數(shù)業(yè)務(wù)組件需要的功能革半,例如提供網(wǎng)絡(luò)請求功能;
Android APP組件化架構(gòu)的目標(biāo)是告別結(jié)構(gòu)臃腫流码,讓各個(gè)業(yè)務(wù)變得相對獨(dú)立又官,業(yè)務(wù)組件在組件模式下可以獨(dú)立開發(fā),而在集成模式下又可以變?yōu)閍rr包集成到“app殼工程”中漫试,組成一個(gè)完整功能的APP六敬;
從組件化工程模型中可以看到,業(yè)務(wù)組件之間是獨(dú)立的驾荣,沒有關(guān)聯(lián)的外构,這些業(yè)務(wù)組件在集成模式下是一個(gè)個(gè)library,被app殼工程所依賴秘车,組成一個(gè)具有完整業(yè)務(wù)功能的APP應(yīng)用典勇,但是在組件開發(fā)模式下,業(yè)務(wù)組件又變成了一個(gè)個(gè)application叮趴,它們可以獨(dú)立開發(fā)和調(diào)試割笙,由于在組件開發(fā)模式下,業(yè)務(wù)組件們的代碼量相比于完整的項(xiàng)目差了很遠(yuǎn)眯亦,因此在運(yùn)行時(shí)可以顯著減少編譯時(shí)間伤溉。
組件化實(shí)施流程
1>組件模式和集成模式的轉(zhuǎn)換
Android Studio中的Module主要有兩種屬性,分別為:
1妻率、application屬性乱顾,可以獨(dú)立運(yùn)行的Android程序,也就是我們的APP宫静;
apply plugin: ‘com.android.application’
2.library屬性走净,不可以獨(dú)立運(yùn)行券时,一般是Android程序依賴的庫文件;
apply plugin: ‘com.android.library’
Module的屬性是在每個(gè)組件的 build.gradle 文件中配置的伏伯,當(dāng)我們在組件模式開發(fā)時(shí)橘洞,業(yè)務(wù)組件應(yīng)處于application屬性,這時(shí)的業(yè)務(wù)組件就是一個(gè) Android App说搅,可以獨(dú)立開發(fā)和調(diào)試炸枣;而當(dāng)我們轉(zhuǎn)換到集成模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)該處于 library 屬性弄唧,這樣才能被我們的“app殼工程”所依賴适肠,組成一個(gè)具有完整功能的APP;
Gradle自動(dòng)構(gòu)建工具有一個(gè)重要屬性候引,可以幫助我們完成這個(gè)事情侯养。每當(dāng)我們用AndroidStudio創(chuàng)建一個(gè)Android項(xiàng)目后,就會在項(xiàng)目的根目錄中生成一個(gè)文件 gradle.properties背伴,我們將使用這個(gè)文件的一個(gè)重要屬性:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來沸毁;那么我們在上面提到解決辦法就有了實(shí)際行動(dòng)的方法,首先我們在gradle.properties中定義一個(gè)常量值 isModule(是否是組件開發(fā)模式傻寂,true為是息尺,false為否):
每次更改“isModule”的值后,需要點(diǎn)擊 "Sync Project" 按鈕
isModule=false
然后我們在業(yè)務(wù)組件的build.gradle中讀取 isModule疾掰,但是 gradle.properties 還有一個(gè)重要屬性: gradle.properties 中的數(shù)據(jù)類型都是String類型搂誉,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換;也就是說我們讀到 isModule 是個(gè)String類型的值静檬,而我們需要的是Boolean值炭懊,代碼如下:
if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
全局Context的獲取及組件數(shù)據(jù)初始化
1.當(dāng)Android程序啟動(dòng)時(shí),Android系統(tǒng)會為每個(gè)程序創(chuàng)建一個(gè) Application 類的對象拂檩,并且只創(chuàng)建一個(gè)侮腹,application對象的生命周期是整個(gè)程序中最長的,它的生命周期就等于這個(gè)程序的生命周期稻励。在默認(rèn)情況下應(yīng)用系統(tǒng)會自動(dòng)生成 Application 對象父阻,但是如果我們自定義了 Application,那就需要在 AndroidManifest.xml 中聲明告知系統(tǒng)望抽,實(shí)例化的時(shí)候加矛,是實(shí)例化我們自定義的,而非默認(rèn)的煤篙。
但是我們在組件化開發(fā)的時(shí)候斟览,可能為了數(shù)據(jù)的問題每一個(gè)組件都會自定義一個(gè)Application類,如果我們在自己的組件中開發(fā)時(shí)需要獲取 全局的Context辑奈,一般都會直接獲取 application 對象苛茂,但是當(dāng)所有組件要打包合并在一起的時(shí)候就會出現(xiàn)問題已烤,因?yàn)樽詈蟪绦蛑挥幸粋€(gè) Application,我們組件中自己定義的 Application 肯定是沒法使用的味悄,因此我們需要想辦法再任何一個(gè)業(yè)務(wù)組件中都能獲取到全局的 Context草戈,而且這個(gè) Context 不管是在組件開發(fā)模式還是在集成開發(fā)模式都是生效的。
2.在 組件化工程模型圖中侍瑟,功能組件集合中有一個(gè) Common 組件, Common 有公共丙猬、公用涨颜、共同的意思,所以這個(gè)組件中主要封裝了項(xiàng)目中需要的基礎(chǔ)功能茧球,并且每一個(gè)業(yè)務(wù)組件都要依賴Common組件庭瑰,Common 組件就像是萬丈高樓的地基,而業(yè)務(wù)組件就是在 Common 組件這個(gè)地基上搭建起來我們的APP的抢埋,Common 組件會專門在一個(gè)章節(jié)中講解弹灭,這里只講 Common組件中的一個(gè)功能,在Common組件中我們封裝了項(xiàng)目中用到的各種Base類揪垄,這些基類中就有BaseApplication 類穷吮。
BaseApplication 主要用于各個(gè)業(yè)務(wù)組件和app殼工程中聲明的 Application 類繼承用的,只要各個(gè)業(yè)務(wù)組件和app殼工程中聲明的Application類繼承了 BaseApplication饥努,當(dāng)應(yīng)用啟動(dòng)時(shí) BaseApplication 就會被動(dòng)實(shí)例化捡鱼,這樣從 BaseApplication 獲取的 Context 就會生效,也就從根本上解決了我們不能直接從各個(gè)組件獲取全局 Context 的問題酷愧;
組件之間調(diào)用和通信
在組件化開發(fā)的時(shí)候驾诈,組件之間是沒有依賴關(guān)系,我們不能在使用顯示調(diào)用來跳轉(zhuǎn)頁面了溶浴,因?yàn)槲覀兘M件化的目的之一就是解決模塊間的強(qiáng)依賴問題乍迄,假如現(xiàn)在要從A業(yè)務(wù)組件跳轉(zhuǎn)到業(yè)務(wù)B組件,并且要攜帶參數(shù)跳轉(zhuǎn)士败,這時(shí)候怎么辦呢闯两?而且組件這么多怎么管理也是個(gè)問題,這時(shí)候就需要引入“路由”的概念了拱烁,由本文開始的組件化模型下的業(yè)務(wù)關(guān)系圖可知路由就是起到一個(gè)轉(zhuǎn)發(fā)的作用生蚁。通過路由實(shí)現(xiàn)模塊之間的通信。
組件開發(fā)的demo已經(jīng)上傳到github上 :
開發(fā)流程:
1.配置工程
Android 組件開發(fā)指南
- Android 組件開發(fā)指南
1. 簡介
ACMobi跨平臺移動(dòng)開發(fā)框架內(nèi)部采用組件化結(jié)構(gòu)設(shè)計(jì)戏自。組件之間相互獨(dú)立邦投,不存在依賴關(guān)系,通過框架層的路由組件進(jìn)行數(shù)據(jù)通信降低耦合度擅笔。組件化使得不同團(tuán)隊(duì)并行開發(fā)各業(yè)務(wù)志衣,極大提高迭代效率屯援。本文介紹了組件的開發(fā)、調(diào)試念脯、測試狞洋、發(fā)布等一系列流程。
Android插件入口類必須繼承ACComponentBase類绿店。Android組件入口類類名需要配置到component.xml中吉懊。
2. 開發(fā)環(huán)境
已經(jīng)有Android開發(fā)環(huán)境的可以略過本段
Android的開發(fā)環(huán)境搭建主要包括JDK、Android Studio的安裝假勿。
2.1.JDK安裝驗(yàn)證
安裝完成之后借嗽,可以在檢查JDK是否安裝成功。打開終端转培,輸入java –version 查看JDK的版本信息恶导。出現(xiàn)類似下面的畫面表示安裝成功了:
[圖片上傳失敗...(image-7c4f9d-1533362955000)]
2.2.Android Studio
Android Studio 的安裝教程網(wǎng)上有很多,請自行搜索一下浸须,可能需要使用代理
2.3.源碼
點(diǎn)擊下載Android組件開發(fā)基礎(chǔ)工程
3. 組件開發(fā)
3.1.配置工程
下載組件開發(fā)基礎(chǔ)工程惨寿, Android Studio點(diǎn)擊File->Open...,選擇剛剛下載下來的基礎(chǔ)工程ACMobiNativeMainDebug打開删窒。
3.2.新建TabBar組件
在工程下點(diǎn)擊New->Module裂垦,新建ACComponentTabBar模塊,如下圖:
3.3.定義啟動(dòng)的Activity
啟動(dòng)的activity固定的全類名為:com.appcan.activity.MainActivity
因此需要在java下新建com.appcan.activity包易稠,如下:
在該包中新建MainAcitivity缸废,繼承Acitivity,如下:
該Acitivity中定義一個(gè)簡單的button驶社。
3.4.路由配置
3.4.1.路由引用
在模塊ACComponentTabBar中的build.gradle文件中添加如下引用:
compile 'com.appcan.engine:ACRouter:1.0.0'//注意1.0.0為初始版本企量,開發(fā)時(shí)注意替換為最新版本
添加完成之后,點(diǎn)擊Sync Now亡电,等待同步完成届巩。
3.4.2.新建入口類
在ACComponentTabBar模塊java中新建Class,需繼承ACComponentBase類份乒,如下圖:
該類必須重寫onCreate方法恕汇,在該方法中做一些初始化的操作。因?yàn)樵谠O(shè)置啟動(dòng)頁時(shí)或辖,不需要對外提供方法瘾英,所以這里Initializer類的onCreate方法里面可以為空,若需要對外提供方法颂暇,則注冊路由必須在這個(gè)方法中實(shí)現(xiàn)(實(shí)現(xiàn)方式后文介紹)缺谴。
3.4.3.配置文件
在ACComponentTabBar模塊的res目錄下,新建xml文件夾耳鸯,并新建component.xml文件湿蛔,如下:
內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<acplugins>
<!--其中className屬性值即為繼承自ACComponentBase的入口類膀曾,moduleId屬性值為組件唯一標(biāo)示-->
<plugin className="com.appcan.component.accomponenttabbar.Initializer" moduleId="main"/>
</acplugins>
配置中className屬性值為繼承自ACComponentBase的入口類,moduleId屬性值為組件唯一標(biāo)示
并將該配置加入app模塊的res/xml/component.xml文件中阳啥。如下:
3.4.4.導(dǎo)入組件
在app模塊下的build.gradle文件中添谊,新增ACComponentTabBar模塊的工程引用,如下:
compile project(':ACComponentTabBar')
3.4.5.AndroidManifest.xml配置說明
關(guān)于ACMobiNativeMainDebug工程AndroidManifest.xml的配置有如下幾點(diǎn)需要注意
1察迟、工程依賴了NativeMain引擎斩狱,在NativeMain引擎的AndroidManifest.xml中已經(jīng)指定了Application的具體實(shí)現(xiàn)類,其中包含了引擎的初始化等重要操作卷拘,所以在ACMobiNativeMainDebug工程任何模塊的AndroidManifest.xml中<application/>標(biāo)簽下都不允許添加android:name="xxxx"屬性再來指定其它的Application實(shí)現(xiàn)類喊废,如下圖。
2栗弟、另外NativeMain引擎中已經(jīng)配置好入口Activity,所以要把入口組件的啟動(dòng)界面命名為com.appcan.activity.MainActivity工闺,如上圖乍赫。
3、ACMobiNativeMainDebug中的app模塊是示例工程的啟動(dòng)模塊陆蟆,其作用僅為創(chuàng)建程序入口雷厂,關(guān)聯(lián)ACComponentTabBar和ACComponentActivity。通過打包服務(wù)器打包時(shí)是不需要上傳app模塊的叠殷,app中的修改并不會在打包生成的apk中生效改鲫,為了避免造成困擾請不要在app模塊中做任何修改。
4林束、所有組件不允許配置android:allowBackup屬性像棘,引擎中已指定該屬性值為fasle,當(dāng)組件中配置為true時(shí)會出現(xiàn)沖突導(dǎo)致打包失敗壶冒。
5缕题、為了做到主題可配置,引擎中并未指定android:theme屬性胖腾,但該屬性又是打包必要屬性烟零,缺少該屬性會導(dǎo)致打包失敗,所以必須在入口組件中進(jìn)行配置咸作。
3.4.6.組件初始化
上面提到NativeMain引擎封裝了Application的實(shí)現(xiàn)锨阿,那么組件中有些需要在應(yīng)用初始化時(shí)執(zhí)行的操作該如何處理?
引擎的做法是將Application中onCreate记罚、onConfigurationChanged墅诡、onTerminate、onLowMemory毫胜、onTrimMemory等系統(tǒng)方法向組件分發(fā)书斜,并將Application實(shí)例以方法參數(shù)的形式傳遞給各個(gè)組件诬辈。所有組件中必須有一個(gè)繼承于ACComponentBase.java的類來完成組件的初始化工作和其它Application生命周期的響應(yīng)。其中onCreate被定義為抽象方法組件必須實(shí)現(xiàn)荐吉,組件初始化需要執(zhí)行的操作必須放在這里焙糟。其它onConfigurationChanged、onTerminate样屠、onLowMemory穿撮、onTrimMemory等方法可根據(jù)情況重寫。
以ACComponentTabBar組件為例痪欲,Initializer.java繼承了ACComponentBase.java實(shí)現(xiàn)了OnCreate方法悦穿,在該組件初始化時(shí)將自己注冊到路由中供其它組件調(diào)用。
public class Initializer extends ACComponentBase {
@Override
public void onCreate(Application application) {
ACRouter.getACRouter().regist(new ServiceStub<IntentBridge>() {
@Override
public String initModule() {
return getId(Initializer.class);
}
});
}
}
3.4.7.運(yùn)行app
點(diǎn)擊運(yùn)行app模塊业踢,ACComponentTabBar模塊中定義的MainActivity就會被啟動(dòng)栗柒,該activity中定義了一個(gè)按鈕,啟動(dòng)其他模塊的Activity知举,下文講述啟動(dòng)其他模塊Activity的方法瞬沦。
3.5.模塊之間通過路由調(diào)用
在新模塊中定義activity供ACComponentTabBar模塊啟動(dòng)。新建模塊ACComponentActivity雇锡,根據(jù)步驟3.4.1逛钻,引入ACRoute。
3.5.1.創(chuàng)建入口類
根據(jù)步驟3.4.2中描述的同樣方法創(chuàng)建入口類锰提,在入口類的onCreate方法中添加路由的注冊曙痘。注冊代碼如下:
package com.appcan.component.accomponenttabbar;
import android.app.Application;
import com.appcan.router.ACComponentBase;
import com.appcan.router.ACRouter;
import com.appcan.router.ServiceStub;
public class Initializer extends ACComponentBase {
@Override
public void onCreate(Application application) {
ACRouter.getACRouter().regist(new ServiceStub<IntentBridge>() {
@Override
public String initModule() {
return getId(Initializer.class);//這里通過路由提供的獲取該模塊唯一標(biāo)示的方法getId(Class clazz)返回該模塊注冊路由時(shí)的唯一標(biāo)識,在其他模塊調(diào)用時(shí)需要用到立肘。
}
});
}
}
以上代碼中指定路由方法的接口類為IntentBridge边坤,則需要在該模塊下新建IntentBridge接口類。在ACComponentActivity模塊java中新建Class赛不,修改類型為interface惩嘉,繼承RouterService類,如下圖:
在該類中定義對外提供的接口踢故,定義代碼如下:
@RouterUri(ACUri.Patten.ACTIVITY)//RouterUri注解指定接口類型文黎,必須是Patten常量
@RouterImp(impClass = SecondActivity.class)//RouterImp注解指定接口實(shí)現(xiàn)類
void startSecondActivity(RouterCallback callback
, @RouterParam("context") Context context
, @RouterParam("param") String param1
, @RouterParam(value = ActivityProcessor.ACTIVITY_REQUEST_CODE, type = int.class) int requestCode);//startSecondActivity為方法名稱,callback為固定必選項(xiàng)殿较,RouterParam注解指定的參數(shù)為可選項(xiàng)耸峭,注解括號中的內(nèi)容指定在其他模塊調(diào)用時(shí)參數(shù)的關(guān)鍵字,緊跟著注解的是參數(shù)類型淋纲。
然后新建SecondActivity,將SecondActivity注冊到AndroidManifest.xml中劳闹。
下一步,根據(jù)步驟3.4.3和3.4.4配置xml文件,并將配置添加到app中的component.xml文件中本涕,同時(shí)在app的build.gradle文件中添加ACComponentActivity模塊的引用业汰。
3.5.2.接口調(diào)用
接口配置完成之后,可以在模塊ACComponentTabBar中調(diào)用ACComponentActivity模塊中的方法菩颖。調(diào)用方法如下:
String uri = "second://activity/startSecondActivity?param=test&" + ACTIVITY_REQUEST_CODE + "=" + REQUEST_SECOND_CODE;
ACRouter.getACRouter().getmClient().invoke(MainActivity.this
, new ACUri(uri)
, new RouterCallback() {
@Override
public void callback(RouterCallback.Result result) {
Log.i("callback", result + "");
}
});
其中uri的組合規(guī)則為"模塊在路由中的唯一標(biāo)識符://接口類型/接口名稱?接口參數(shù)1=value1&接口參數(shù)2=value2"
對應(yīng)關(guān)系如下圖:(相同顏色箭頭表示一個(gè)對應(yīng)關(guān)系)
注意startSecondActivity方法定義了通過setResult返回給前一個(gè)Activity數(shù)據(jù)样漆,則需要被啟動(dòng)的SecondActivity類中調(diào)用setResult方法,調(diào)用如下:
setResult(Activity.RESULT_OK);
finish();
同時(shí)晦闰,需要在調(diào)用startSecondActivity方法的MainActivity中重寫onActivityResult方法放祟。代碼如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_SECOND_CODE){
Toast.makeText(this, R.string.activity_back, Toast.LENGTH_SHORT).show();
}
}
至此,啟動(dòng)其他模塊Activity的調(diào)用方法全部配置完畢呻右, 可以運(yùn)行程序了跪妥。
3.5.3.METHOD類型方法定義
上文講述的是ACTIVITY類型方法定義,即在定義方法時(shí)RouterUri注解指定的ACUri.Patten常量類型声滥,本節(jié)講述METHOD類型方法的定義眉撵。
ACComponentActivity模塊中的SecondActivity中定義一個(gè)按鈕,點(diǎn)擊該按鈕改變ACComponentTabBar模塊中MainActivity界面的背景顏色落塑。
3.5.3.1.ACComponentTabBar模塊定義changeTabBarBackground方法
根據(jù)上文講述的方法执桌,ACComponentTabBar模塊定義繼承自ACComponentBase的入口類Initializer,在Initializer的onCreate方法代碼如下:
@Override
public void onCreate(Application application) {
ACRouter.getACRouter().regist(new ServiceStub<IntentBridge>() {
@Override
public String initModule() {
return getId(Initializer.class);//在路由中定義的唯一標(biāo)識符
}
});
}
以及繼承自RouterService的接口IntentBridge類,在IntentBridge類中定義接口芜赌,代碼如下:
@RouterUri(ACUri.Patten.METHOD)//接口類型為METHOD
@RouterImp(impClass = ImplementMethod.class)//接口的實(shí)現(xiàn)在ImplementMethod類中
void changeTabBarBackground(RouterCallback callback, @RouterParam("color") String bgColor);//接口的名稱changeTabBarBackground,固定參數(shù)callback和RouterParam注解指定color參數(shù)伴逸。
ImplementMethod類中代碼如下:
public class ImplementMethod {
public void changeTabBarBackground(RouterCallback callback, String color){
RouterCallback.Result result;
try {
MainActivity.getInstance().setBackgroundColor(color);//MainActivity方法需要定義setBackgroundColor方法
result = new RouterCallback.Result(
RouterCallback.Result.SUCCESS,
null,
null);
} catch (Exception e) {
e.printStackTrace();
result = new RouterCallback.Result(
RouterCallback.Result.FAIL,
e.getMessage(),
null);
}
callback.callback(result);
}
}
對應(yīng)關(guān)系如下圖:
緊接著在MainActivity方法中定義setBackgroundColor方法實(shí)現(xiàn)背景顏色的更換設(shè)置缠沈,代碼如下:
public void setBackgroundColor(String color) {
findViewById(R.id.bgLinearLayout).setBackgroundColor(Color.parseColor(color));
}
至此,ACComponentTabBar模塊METHOD類型接口定義完畢错蝴,下一步在ACComponentActivity接口中調(diào)用洲愤。
3.5.3.2.ACComponentActivity模塊調(diào)用changeTabBarBackground方法
在ACComponentActivity的按鈕點(diǎn)擊事件中加入如下代碼:
String uri = "main://method/changeTabBarBackground?color=#ff0000";
ACRouter.getACRouter().getmClient().invoke(new ACUri(uri), new RouterCallback() {
@Override
public void callback(Result result) {
if(result != null && result.getCode() == Result.SUCCESS){
Toast.makeText(SecondActivity.this, "設(shè)置成功", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(SecondActivity.this, "設(shè)置失敗", Toast.LENGTH_LONG).show();
}
}
});
到這里,ACComponentActivity模塊調(diào)用ACComponentTabBar模塊的method類型接口定義及調(diào)用完成顷锰。
以上代碼柬赐,Android組件開發(fā)Demo中,開發(fā)者可自行下載查看源碼及運(yùn)行查看效果官紫。
4.路由SDK
4.1.注解
4.1.1.RouterUri
該注解指定接口的類型肛宋,在繼承自RouterService的接口類中定義方法時(shí)需要使用,目前只支持兩種束世,activity和method
4.1.1.1.activity
activity類型的接口表示啟動(dòng)一個(gè)Activity酝陈,定義方法時(shí)指定RouterUri注解的值為:ACUri.Patten.ACTIVITY,如下:
@RouterUri(ACUri.Patten.ACTIVITY)
@RouterImp(impClass = SecondActivity.class)
void startSecondActivity(RouterCallback callback
, @RouterParam("context") Context context
, @RouterParam("param") String param
, @RouterParam(value = ActivityProcessor.ACTIVITY_REQUEST_CODE, type = int.class) int requestCode);
調(diào)用時(shí)uri的組成為:
String uri = "唯一標(biāo)識符://activity/...";
4.1.1.2.method
method類型的接口表示調(diào)用一個(gè)方法毁涉,定義方法時(shí)指定RouterUri的值為:ACUri.Patten.METHOD沉帮,如下:
@RouterUri(ACUri.Patten.METHOD)
@RouterImp(impClass = ImplementMethod.class)
void changeTabBarBackground(RouterCallback callback, @RouterParam("color") String bgColor);
調(diào)用時(shí)uri的組成為:
String uri = "唯一標(biāo)識符://method/...";
4.1.2.RouterImp
該注解指定實(shí)現(xiàn)類,若RouterUri注解值為activity,則RouterImp即是要啟動(dòng)的Activity類穆壕;若RouterUri注解值為method待牵,則RouterImp是一個(gè)class,class中需包含方法名稱及參數(shù)都相同的方法喇勋。如4.1.1.2中定義的方法就表示需要一個(gè)ImplementMethod.class類缨该,該類中包含方法名稱為changeTabBarBackground,參數(shù)為callback和color的方法茄蚯,如下:
public class ImplementMethod {
public void changeTabBarBackground(RouterCallback callback, String color){
}
}
若一個(gè)模塊中包含多個(gè)method類型的方法压彭,這些方法都可以定義在同一個(gè)實(shí)現(xiàn)類中。
4.1.3.RouterParam
若是自定義的參數(shù)渗常,則可以直接寫成:
@RouterParam("參數(shù)名稱") 參數(shù)類型 形參名稱
參數(shù)名稱是指定調(diào)用時(shí)uri中參數(shù)的關(guān)鍵字壮不,兩者必須保持一致。形參名稱可隨意定義皱碘,無影響询一。
若是路由開放的特定的參數(shù),即參數(shù)名稱和參數(shù)類型固定癌椿,則寫法如下:
@RouterParam(value = 參數(shù)名稱, type = 參數(shù)類型) int 形參名稱)
目前特定的參數(shù)有兩種健蕊,
一種是requestCode:
@RouterParam(value = ActivityProcessor.ACTIVITY_REQUEST_CODE, type = int.class) int requestCode)
該參數(shù)表示啟動(dòng)的Activity可以通過setResult回傳給調(diào)用者數(shù)據(jù),上文有詳細(xì)講解踢俄。
另一種是flags:
@RouterParam(value = ActivityProcessor.ACTIVITY_FLAGS, type = int.class) int flags
該參數(shù)表示啟動(dòng)Activity時(shí)的intent的flags參數(shù)缩功。
4.2.callback
callback參數(shù)固定表示通過路由調(diào)用其他模塊接口時(shí)的回調(diào)方法。該方法有一個(gè)Result類型的參數(shù)都办,Result是一個(gè)內(nèi)部JavaBean的類嫡锌,聲明了三個(gè)成員變量,code琳钉,msg和data势木,開發(fā)者可根據(jù)需要定義這三個(gè)參數(shù)的值。
private int code;
private String msg;
private String data;
若RouterUri指定為activity類型歌懒,則callback回調(diào)方法是路由處理的啦桌,不會傳遞給被啟動(dòng)的Activity觸發(fā)。組件開發(fā)時(shí)及皂,只需要在調(diào)用方法時(shí)對回調(diào)的內(nèi)容做處理甫男,如下:
String uri = "second://activity/startSecondActivity?param=test&" + ACTIVITY_REQUEST_CODE + "=" + REQUEST_SECOND_CODE;
ACRouter.getACRouter().getmClient().invoke(MainActivity.this
, new ACUri(uri)
, new RouterCallback() {
@Override
public void callback(RouterCallback.Result result) {
//只有在啟動(dòng)activity失敗時(shí),該方法被調(diào)用躲庄,result包含code和data參數(shù)查剖。code表示錯(cuò)誤碼,data表示錯(cuò)誤信息噪窘。
}
});
若RouterUri指定為method類型笋庄,則callback回調(diào)方法會傳遞給方法實(shí)現(xiàn)中去觸發(fā)效扫,如下:
public void changeTabBarBackground(RouterCallback callback, String color){
RouterCallback.Result result;
try {
MainActivity.getInstance().setBackgroundColor(color);
result = new RouterCallback.Result(
RouterCallback.Result.SUCCESS,
null,
null);
} catch (Exception e) {
e.printStackTrace();
result = new RouterCallback.Result(
RouterCallback.Result.FAIL,
e.getMessage(),
null);
}
callback.callback(result);
}
5.上傳組件打包測試
5.1.腳本引用
在ACComponentTabBar模塊的build.gradle文件末尾加上如下代碼:
apply plugin: 'ACComponent'
apply plugin: 'maven'
def VERSION_NAME = '1.0.0'//組件版本號
def DESCRIPTION = '初始化'//更新日志
def ARTIFACT_ID = 'ACComponentTabBar'//指定組件名稱
ACComponent{
name = "$ARTIFACT_ID"
versionName = "$VERSION_NAME"
description = "$DESCRIPTION"
}
加入代碼之后,還需要在工程下的gradle.properties文件中添加MARVEN_PATH,該值為ACMobi的官方maven庫地址直砂,其中包括ACComponent(組件出包腳本)等官方腳本,在工程的build.gradle中需要加入對該腳本的引用.
buildscript {
repositories {
jcenter()
maven {
url MARVEN_PATH
}
}
...
}
配置完成之后點(diǎn)擊“Sync Now”等待同步完成菌仁。
5.2.新建組件配置文件夾
在ACComponentTabBar模塊的根目錄下,新建跟build.gradle中定義的ARTIFACT_ID變量值相同名稱的文件夾静暂,即ACComponentTabBar济丘,在ACComponentTabBar文件夾下新建文件名為ACComponentTabBar(即ARTIFACT_ID的值)的xml文件以及info.xml文件。如下圖:
ACComponentTabBar.xml文件內(nèi)容如下:(該文件內(nèi)容固定洽蛀,無需更改)
<?xml version="1.0" encoding="utf-8"?>
<plugin>
<manifest_config>
</manifest_config>
<dependencies>
</dependencies>
</plugin>
info.xml文件內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<acplugins>
<plugin acName="ACComponentTabBar" version="1.0.0" build="0" desc="xxxx" type="compoment">
<info>0:測試組件Demo</info>
</plugin>
</acplugins>
特別注意:
- info文件只需要按照上述內(nèi)容做初始化設(shè)置即可摹迷,后面版本升級不需要手動(dòng)修改該文件,該文件中除了acName的屬性值"ACComponentTabBar"和info標(biāo)簽內(nèi)的文字"測試組件Demo"會根據(jù)組件不同而改變郊供,其余內(nèi)容必須保證不變峡碉。
- gradle出包腳本會根據(jù)build.gradle的配置,自動(dòng)更新inof.xml文件內(nèi)容驮审。請參考5.5章節(jié)鲫寄。
-
desc
屬性的值為組件功能簡介,必須填寫疯淫。 - type="compoment"不可修改地来,表示庫為組件類型。
5.3.本地aar引用配置
如果組件中有用到本地aar文件熙掺,需要在工程根目錄下新建aar文件夾未斑,并在工程的build.gradle文件中加入對aar文件夾的配置。
所有被引用的*.aar文件必須放置于該目錄下 如圖:
repositories {
...
flatDir {
dirs '../aar'
}
}
dependencies {
...
compile(name:'testaar-1.0.0', ext:'aar')
}
5.4.組件混淆配置
在打包服務(wù)器打正式包時(shí)會進(jìn)行代碼混淆,組件需要配置混淆文件告訴打包服務(wù)器該如何混淆。
5.4.1.新建組件混淆文件
在組件module的根目錄下新建proguard.pro文件瘪阁,并將混淆配置寫入該文件臭蚁,注意編寫時(shí)請嚴(yán)格遵循ProGuard語法,如下圖:
注意proguard.pro文件只需要按照上述內(nèi)容編寫肌似、放置即可费就,不需要在組件build.gradle中的buildTypes里配置模塊混淆,打包服務(wù)器會自動(dòng)合并
5.5.運(yùn)行腳本
根據(jù)需求先修改build.gradle中以下兩個(gè)變量的值川队。
def VERSION_NAME = '1.0.0'//組件版本號
def DESCRIPTION = '初始化'//更新日志
組件升級只需要更新VERSION_NAME
和 DESCRIPTION
即可力细,gradle出包腳本會自動(dòng)更新變量值到info.xml中。
5.5.1.buildComponent
和步驟5.5.1一樣的方式固额,雙擊ACComponentTabBar模塊下的other->buildComponent執(zhí)行該task眠蚂,如下圖:
運(yùn)行完成之后,生成的組件包在ACComponentTabBar根目錄下斗躏,文件名為:ACComponentTabBar-android-1.0.0.zip逝慧,將該組件包上傳到打包服務(wù)器即可以打包在項(xiàng)目中使用。
6.ACComponentMyView
ACComponentMyView是一個(gè)提供視圖組件的demo,該組件中定義了一個(gè)TextView笛臣,并提供了一個(gè)路由方法getTextView(...)供其他組件調(diào)用云稚,最后通過回調(diào)的方式讓其他組件獲得這個(gè)TextView。
6.1.創(chuàng)建View
在ACComponentMyView中創(chuàng)建了一個(gè)layout文件myview_textview.xml沈堡,文件中僅包含一個(gè)TextView
通過LayoutInflater方法初始化TextView對象
6.2.傳遞View
ImplementMethod類中g(shù)etTextView方法是TextView傳遞的具體實(shí)現(xiàn)静陈,具體內(nèi)容就是將TextView解析出來通過RouterCallback返回給其他組件,RouterCallback<T>中的范型T要和View類型保持一致這里是TextView
@RouterUri(ACUri.Patten.METHOD)
@RouterImp(impClass = ImplementMethod.class)
void getTextView(@RouterParam("context") Context context, RouterCallback<TextView> callback);
public class ImplementMethod {
public void getTextView(Context context, RouterCallback<TextView> callback){
RouterCallback.Result<TextView> result;
try {
TextView textView = (TextView) MyViewActivity.getInstance().getWindow().getLayoutInflater().inflate(R.layout.myview_textview, null);
textView.setText("我是ACComponentMyView組件中的TextView");
result = new RouterCallback.Result<>(
RouterCallback.Result.SUCCESS,
null,
textView);
} catch (Exception e) {
e.printStackTrace();
result = new RouterCallback.Result<>(
RouterCallback.Result.FAIL,
e.getMessage(),
null);
}
callback.callback(result);
}
}
ACComponentActivity組件中拿到TextView后將其添加到一個(gè)LinearLayout中
6.3.注意
注意一
如果ACComponentMyView中的View對象已經(jīng)被添加到了具體的布局中诞丽,那么這個(gè)對象在傳遞到其他的組件后是不允許添加到其他父布局的鲸拥,要遵循一個(gè)View只能被添加到一個(gè)父布局的原則。
例子里是通過LayoutInflater初始化了一個(gè)新的TextView對象僧免,沒有被添加到任何布局刑赶,所以在傳遞到ACComponentActivity組件后能夠被添加到ACComponentActivity的布局中。
注意二
對于自定義View由于組件之間類型不能識別猬膨,自定義View在傳遞到其他組件之后只能作為Android系統(tǒng)View使用角撞。如果要用到自定義屬性和方法,需要將自定義View定義到公共包中勃痴,并在兩個(gè)組件中添加引用谒所。