組件化中路由框架學(xué)習(xí)筆記

在組件化之前的一種業(yè)務(wù)業(yè)務(wù)劃分架構(gòu)是一種單一分層的結(jié)構(gòu)镐依,整個APP是一個Module喊式,不同的業(yè)務(wù)拆分在不同的包下:

  • 不管分包做的多好僻他,隨著項目的增大笆包,項目會失去層次感环揽,導(dǎo)致接受項目時會比較吃力。(臃腫)
  • 包名約束是一種弱約束庵佣,一不小心就會導(dǎo)致不同的包名之間相互引用歉胶,導(dǎo)致包名之間耦合度高。(耦合度高)
  • 多人聯(lián)合開發(fā)中巴粪,在版本管理中很容易出現(xiàn)代碼沖突和代碼覆蓋的問題通今。(版本管理困難)

組件化是一個化繁為簡的過程,將多個功能模塊進(jìn)行拆分肛根、重組的過程辫塌。即將APP按照業(yè)務(wù)劃分為不同的模塊,最后在打包為完整的APP時再整合為一起派哲。


image.png

如上圖所示:可分為APP殼工程臼氨、業(yè)務(wù)組件層、功能組件層和基礎(chǔ)庫層芭届。

APP殼工程負(fù)責(zé)管理各個業(yè)務(wù)組件和打包APK储矩,沒有具體的業(yè)務(wù)功能。

業(yè)務(wù)組件層是根據(jù)不同的業(yè)務(wù)拆分的不同的業(yè)務(wù)組件褂乍。

功能組件層是為上層提供基礎(chǔ)的功能服務(wù)持隧。

基礎(chǔ)庫中包含了各種開源庫以及和業(yè)務(wù)無關(guān)的各種自研工具庫。

比如可以新建項目如下:
image.png

切換到 project視圖逃片,與app同目錄舆蝴,建立business文件夾,然后在其中建立兩個Android Library题诵,分比為food和waimai洁仗。

新建config.gradle文件,在其中定義變量:

ext {
    //標(biāo)識是以組件化模式運(yùn)行還是集成化模式運(yùn)行性锭,
    // 如果是true赠潦,以集成化方式運(yùn)行,如果是false草冈,以組件化方式運(yùn)行
    isModule = false
}

isModule用來標(biāo)識food是以Android 項目運(yùn)行還是作為一個普通的Android Module她奥。

在project 的build.gradle文件中引入該文件:

apply from : "config.gradle"

分別修改app瓮增、food和waimai的build.gradle文件:

app的build.gradle文件:

///根據(jù)變量的取值來決定運(yùn)行方式
if(rootProject.ext.isModule){
    apply plugin: 'com.android.library'
}else{
    apply plugin: 'com.android.application'
}

....


    defaultConfig {
        if(!rootProject.ext.isModule){
            applicationId "com.example.zujianhuapro"
        }
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    
....

food的build.gradle文件:

if (rootProject.ext.isModule) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
...
    defaultConfig {
        if (rootProject.ext.isModule) {
            applicationId "com.example.food"
        }
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
    }
...

waimai的同上。

可自行改變isModule 的值查看效果哩俭。

為什么要使用組件化
  • 各個組件專注自身功能的實現(xiàn)绷跑,模塊中代碼高度內(nèi)聚,只負(fù)責(zé)一項任務(wù)凡资,也就是常說的單一職責(zé)原則砸捏。
  • 各業(yè)務(wù)研發(fā)可以互不干擾,提升協(xié)作效率隙赁。
  • 業(yè)務(wù)組件可以進(jìn)行插拔垦藏,靈活多變。
  • 業(yè)務(wù)組件之間將不再直接應(yīng)用和依賴伞访,各個業(yè)務(wù)模塊組件更加獨(dú)立掂骏,降低耦合。
  • 加快編譯速度厚掷,提高開發(fā)效率弟灼。

最簡化最核心的就是動態(tài)的切換library和application。

組件化最需要解決的問題--頁面跳轉(zhuǎn)

路由跳轉(zhuǎn)一般可采用隱式跳轉(zhuǎn)和顯示跳轉(zhuǎn)兩種方式冒黑,但是在組件化結(jié)構(gòu)中袜爪,因為不同組件不會相互依賴,所以無法采用顯示跳轉(zhuǎn)的方式薛闪,只可以采用隱式跳轉(zhuǎn),但是隱式跳轉(zhuǎn)也存在問題俺陋,使用隱式跳轉(zhuǎn)時豁延,必須先在生命文件中用intent-filter來限定隱式Action的啟動,其他的Module才可以使用隱式的Action跳轉(zhuǎn)到響應(yīng)的Activity腊状。但是在組件化中使用這種方式并不友好诱咏,不僅多人開發(fā)困難,還存在安全隱患缴挖。

而路由組件正是為此而存在的袋狞。
image.png

首先需要得到目標(biāo)的頁面地址,然后在路由表中尋址映屋,找到目標(biāo)頁后苟鸯,得到Activity的Class對象,然后啟動目標(biāo)頁棚点。

可以看出早处,路由組件的關(guān)鍵在于路由表,而路由表就是一系列特定的URL和特定的Activity之間的映射集合瘫析,是一個Map結(jié)構(gòu)砌梆,key是一個字符串即URL默责,value是Activity的Class對象。

路由表需要保證只有一份咸包,所以需要使用單例模式桃序,而路由框架需要被所有的組件依賴到。

在項目中新建路由Module烂瘫,如下所示


image.png

在app媒熊、food和waimai中引入改Module,

    implementation project(':router-api')

在路由Module中新建如下類

/**
 * 項目名稱 zujianhuaPro
 * 創(chuàng)建人 xiaojinli
 * 創(chuàng)建時間 2020/8/29 9:31 AM
 * 路由表忱反,需要被所有的組件依賴泛释,所以需要使用單例模式
 **/
public class Router {
    private static Router mRouter;
    private static Context mContext;
    //路由表
    private static Map<String,Class<? extends Activity>> routers = new HashMap<>();
    
    public void init(Application application){
        mContext = application;
    }

    public static Router getInstance(){
        if(mRouter == null){
            synchronized (Router.class){
                if(mRouter == null){
                    mRouter = new Router();
                }
            }
        }
        return mRouter;
    }
    
    //向路由表中注冊一個Activity
    public void register(String path,Class<? extends Activity> cls){
        routers.put(path,cls);
    }

    //啟動路由表中的一個Activity
    public void startActivity(String path){
        Class<? extends Activity> cls = routers.get(path);
        if(cls == null){
            return;
        }
        Intent intent = new Intent(mContext,cls);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
    }
}

關(guān)鍵方法有三個,一個是實現(xiàn)單例模式方法温算,因為需要被所有的業(yè)務(wù)Module引用怜校,所以需要保持唯一性。二是向路由表中注冊一個頁面的register方法注竿,三是啟動一個路由表中頁面的方法茄茁。

使用時如下所示:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Router.getInstance().init(getApplication());

        Router.getInstance().register("/food/FoodActivity", FoodActivity.class);
        Router.getInstance().register("/waimai/WaiMaiActivity", WaiMaiActivity.class);

    }

    public void jumpToFood(View view) {
        Router.getInstance().startActivity("/food/FoodActivity");
    }
}

在殼Activity中初始化路由組件以及注冊頁面信息,就可實現(xiàn)跳轉(zhuǎn)巩割。

在food組件中跳轉(zhuǎn)到waimai組件可使用如下方式

Router.getInstance().startActivity("/waimai/WaiMaiActivity");

在waimai組件中跳轉(zhuǎn)到food組件可使用如下方式

Router.getInstance().startActivity("/food/FoodActivity");

但是這種方式存在一個明顯的缺點裙顽,即我們需要自己在路由表中注冊所有的頁面,不易維護(hù)宣谈。

下面我們會通過APT來進(jìn)行優(yōu)化愈犹。

什么是APT

APT是注解處理器,是javac處理注解的一種工具闻丑,他用來在編譯時掃描和處理注解漩怎,簡單來說就是在編譯期間,通過注解采集信息嗦嗡,生成.java文件勋锤,減少重復(fù)代碼的編寫。很多框架都是用了APT侥祭,包括Butterknife和Glide等叁执。

通過上面的路由表可以看出,我們需要的關(guān)鍵信息是Activity的一個字符串參數(shù)以及Activity的Class對象矮冬,我們都可以通過注解來采集到谈宛。

創(chuàng)建JavaLib,依賴下面的依賴監(jiān)控到編譯期

    //構(gòu)建  -----》》》》----【編譯時期】------》》》打包-------》》》安裝
    // As-3.4.1  +  gradle-5.1.1-all + auto-service:1.0-rc4
    //在編譯器可以通過以下依賴讓我們自定義的AbstractProcessor工作胎署,即可以通過這兩個服務(wù)讓我們在編譯器做一些事情
    compileOnly'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'

首先我們需要創(chuàng)建一個JavaLib入挣,來創(chuàng)建一個注解

@Target(ElementType.TYPE) //作用在類上
@Retention(RetentionPolicy.CLASS) //保留到CLASS,因為我們在編譯器需要硝拧,所以需要保留到Class
public @interface Route {
    String value(); //詳細(xì)路徑名径筏,比如/main/MainActivity葛假,接收一個字符串參數(shù)
    String group() default "";//路由組名,比如main
}

其次我們需要再定義一個JavaLib滋恬,來創(chuàng)建一個處理注解的類聊训,需要繼承AbstractProcessor,默認(rèn)實現(xiàn)process方法恢氯。需要注意添加如下注解:

//自動注冊带斑,以便該類可以在編譯器干活
@AutoService(Processor.class)
//允許支持的注解類型,讓注解處理器處理
@SupportedAnnotationTypes(ProcessorConfig.ROUTER_PACKAGE)
//指定JDK編譯版本勋拟,必須寫
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//注解處理器接收的參數(shù)
@SupportedOptions({ProcessorConfig.OPTIONS,ProcessorConfig.APT_PACKAGE})

此外我們需要在其他的Module中引入該處理器Module勋磕,注意需要使用annotationProcessor關(guān)鍵字

annotationProcessor project(path: ':route_compile')

待一切都準(zhǔn)備完成之后,點擊錘子符號即Make Project按鈕敢靡,如果一切都沒有問題挂滓,處理器文件夾下會出現(xiàn)build文件夾,在其中可以找到如下類啸胧,意味著注解處理器配置成功赶站。

image.png

此時當(dāng)我們項目進(jìn)入編譯器時,會在APP工程中掃描引入該注解處理器module的組件中的類纺念,查看是否用使用了Route注解贝椿。下面就是在我們自定義的注解處理器內(nèi)添加相應(yīng) 的邏輯生成需要的java文件。

完整的項目地址:

https://github.com/lxj-helloworld/zujianhuaPro

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陷谱,一起剝皮案震驚了整個濱河市烙博,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烟逊,老刑警劉巖渣窜,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異焙格,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)夷都,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門眷唉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人囤官,你說我怎么就攤上這事冬阳。” “怎么了党饮?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵肝陪,是天一觀的道長。 經(jīng)常有香客問我刑顺,道長氯窍,這世上最難降的妖魔是什么饲常? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮狼讨,結(jié)果婚禮上贝淤,老公的妹妹穿的比我還像新娘。我一直安慰自己政供,他們只是感情好播聪,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著布隔,像睡著了一般离陶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衅檀,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天招刨,我揣著相機(jī)與錄音,去河邊找鬼术吝。 笑死计济,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的排苍。 我是一名探鬼主播沦寂,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淘衙!你這毒婦竟也來了传藏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤彤守,失蹤者是張志新(化名)和其女友劉穎毯侦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體具垫,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侈离,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了筝蚕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卦碾。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖起宽,靈堂內(nèi)的尸體忽然破棺而出洲胖,到底是詐尸還是另有隱情,我是刑警寧澤坯沪,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布绿映,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏叉弦。R本人自食惡果不足惜丐一,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卸奉。 院中可真熱鬧钝诚,春花似錦、人聲如沸榄棵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疹鳄。三九已至拧略,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘪弓,已是汗流浹背垫蛆。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留腺怯,地道東北人袱饭。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像呛占,于是被迫代替她去往敵國和親虑乖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355