日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

单独组件_阿里P8年薪百万大牛-教你打造一个Android组件化开发框架

發(fā)布時(shí)間:2025/3/15 Android 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单独组件_阿里P8年薪百万大牛-教你打造一个Android组件化开发框架 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者簡介

本篇來自 lucky_billy 的投稿,分享了他的開源組件化框架,詳細(xì)地講解框架形成的思路,希望對(duì)大家有所幫助。

lucky_billy 的博客地址:

http://blog.csdn.net/cdecde111

解讀開源框架設(shè)計(jì)思想

B站學(xué)習(xí)視頻

滿滿誠意:【實(shí)戰(zhàn)MVVM和Jetpack的完美結(jié)合讓頁面開發(fā)不再煩惱】
【1.什么是插件化】
【2.插件化能解決的問題及與組件化的區(qū)別】
【3.常用插件化框架對(duì)比】
。
。

【13.手寫實(shí)現(xiàn)插件的資源加載】

點(diǎn)擊【設(shè)計(jì)思想解讀開源框架】學(xué)習(xí)筆記,學(xué)習(xí)路線獲取!

前言

CC:Component Caller,一個(gè)android組件化開發(fā)框架, 已開源,github 地址:

https://github.com/luckybilly/CC

本文主要講解框架實(shí)現(xiàn)原理,如果只是想了解一下如何使用,可直接到 github上查看README文檔

首先說明一下,本文將講述的組件化與業(yè)內(nèi)的插件化(如:Atlas, RePlugin等)不是同一個(gè)概念


[圖片上傳失敗...(image-cd3f7b-1607780116731)]

組件化開發(fā):就是將一個(gè)app分成多個(gè)Module,每個(gè)Module都是一個(gè)組件(也可以是一個(gè)基礎(chǔ)庫供組件依賴),開發(fā)的過程中我們可以單獨(dú)調(diào)試部分組件,組件間不需要互相依賴,但可以相互調(diào)用,最終發(fā)布的時(shí)候所有組件以lib的形式被主app工程依賴并打包成1個(gè)apk。

插件化開發(fā):和組件化開發(fā)略有不用,插件化開發(fā)時(shí)將整個(gè)app拆分成很多模塊,這些模塊包括一個(gè)宿主和多個(gè)插件,每個(gè)模塊都是一個(gè)apk(組件化的每個(gè)模塊是個(gè)lib),最終打包的時(shí)候?qū)⑺拗鱝pk和插件apk(或其他格式)分開或者聯(lián)合打包。

本文將主要就以下幾個(gè)方面進(jìn)行介紹:

一、為什么需要組件化?

二、CC的功能介紹

三、CC技術(shù)要點(diǎn)

四、CC執(zhí)行流程詳細(xì)解析

為什么需要組件化?

關(guān)于使用組件化的理由,上網(wǎng)能搜到很多,如業(yè)務(wù)隔離、單獨(dú)以app運(yùn)行能提高開發(fā)及調(diào)試效率等等這里就不多重復(fù)了,我補(bǔ)充一條:組件化之后,我們能很容易地實(shí)現(xiàn)一些組件層面的AOP,例如:

  • 輕易實(shí)現(xiàn)頁面數(shù)據(jù)(網(wǎng)絡(luò)請(qǐng)求、I/O、數(shù)據(jù)庫查詢等)預(yù)加載的功能組件被調(diào)用時(shí),進(jìn)行頁面跳轉(zhuǎn)的同時(shí)異步執(zhí)行這些耗時(shí)邏輯頁面跳轉(zhuǎn)并初始化完成后,再將這些提前加載好的數(shù)據(jù)展示出來
  • 在組件功能調(diào)用時(shí)進(jìn)行登錄狀態(tài)校驗(yàn)
  • 借助攔截器機(jī)制,可以動(dòng)態(tài)給組件功能調(diào)用添加不同的中間處理邏輯

CC的功能介紹

  • 支持組件間相互調(diào)用(不只是Activity跳轉(zhuǎn),支持任意指令的調(diào)用/回調(diào))
  • 支持組件調(diào)用與Activity、Fragment的生命周期關(guān)聯(lián)
  • 支持app間跨進(jìn)程的組件調(diào)用(組件開發(fā)/調(diào)試時(shí)可單獨(dú)作為app運(yùn)行)在獨(dú)立運(yùn)行組件時(shí)非常有用,比如:一個(gè)組件的某個(gè)功能要用到用戶的登錄信息,若未登錄則調(diào)起登錄組件的登錄頁面,若已登錄則獲取當(dāng)前用戶信息。此時(shí)可以直接使用主app中的登錄組件及用戶在主app中的登錄狀態(tài),該組件作為app獨(dú)立運(yùn)行時(shí)無需依賴登錄組件。
  • 支持app間調(diào)用的開關(guān)及權(quán)限設(shè)置(滿足不同級(jí)別的安全需求,默認(rèn)打開狀態(tài)且不需要權(quán)限)
  • 支持同步/異步方式調(diào)用
  • 支持同步/異步方式實(shí)現(xiàn)組件
  • 調(diào)用方式不受實(shí)現(xiàn)方式的限制(例如:可以異步調(diào)用另一個(gè)組件的同步實(shí)現(xiàn)功能。注:不要在主線程同步調(diào)用耗時(shí)操作)
  • 支持添加自定義攔截器(按添加的先后順序執(zhí)行)
  • 支持超時(shí)設(shè)置
  • 支持手動(dòng)取消
  • 編譯時(shí)自動(dòng)注冊(cè)組件(IComponent),無需手動(dòng)維護(hù)組件注冊(cè)表(使用ASM修改字節(jié)碼的方式實(shí)現(xiàn))
  • 支持動(dòng)態(tài)注冊(cè)/反注冊(cè)組件(IDynamicComponent)
  • 支持組件間傳遞Fragment等非基礎(chǔ)類型的對(duì)象(組件在同一個(gè)app內(nèi)時(shí)支持、跨app傳遞非基礎(chǔ)類型的對(duì)象暫不支持)
  • 盡可能的解決了使用姿勢不正確導(dǎo)致的crash:組件調(diào)用處、回調(diào)處、組件實(shí)現(xiàn)處的crash全部在框架內(nèi)部catch住同步返回或異步回調(diào)的CCResult對(duì)象一定不為null,避免空指針
  • demo效果演示

    組件A 打包在主app中,組件B 為單獨(dú)運(yùn)行的組件app,下圖演示了在主app中調(diào)用兩者的效果,并將結(jié)果以Json的格式顯示在下方:

    CC技術(shù)要點(diǎn)

    實(shí)現(xiàn)CC組件化開發(fā)框架主要需要解決的問題有以下幾個(gè)方面:

    • 組件如何自動(dòng)注冊(cè)?
    • 如何兼容同步/異步方式調(diào)用組件?
    • 如何兼容同步/異步方式實(shí)現(xiàn)組件?
    • 如何進(jìn)行跨進(jìn)程組件任意功能的調(diào)用(不只是啟動(dòng)Activity)?
    • 組件如何更方便地在application和library之間切換?
    • 如何實(shí)現(xiàn)startActivityForResult?
    • 如何阻止非法的外部調(diào)用?
    • 如何與Activity、Fragment的生命周期關(guān)聯(lián)起來

    組件如何自動(dòng)注冊(cè)?

    為了減少后期維護(hù)成本,想要實(shí)現(xiàn)的效果是:當(dāng)需要添加某個(gè)組件到app時(shí),只需要在gradle中添加一下對(duì)這個(gè) module 的依賴即可(通常都是maven依賴,也可以是project依賴)

    最初想要使用的是 annotationProcessor 通過編譯時(shí)注解動(dòng)態(tài)生成組件映射表代碼的方式來實(shí)現(xiàn)。但嘗試過后發(fā)現(xiàn)行不通,因?yàn)榫幾g時(shí)注解的特性只在源碼編譯時(shí)生效,無法掃描到aar包里的注解(project依賴、maven依賴均無效),也就是說必須每個(gè)module編譯時(shí)生成自己的代碼,然后要想辦法將這些分散在各aar種的類找出來進(jìn)行集中注冊(cè)。

    ARouter 的解決方案是:

    • 每個(gè) module 都生成自己的 java類,這些類的包名都是 ’com.alibaba.android.arouter.routes’
    • 然后在運(yùn)行時(shí)通過讀取每個(gè) dex 文件中的這個(gè)包下的所有類通過反射來完成映射表的注冊(cè),詳見 ClassUtils.java 源碼運(yùn)行時(shí)通過讀取所有 dex 文件遍歷每個(gè) entry 查找指定包內(nèi)的所有類名,然后反射獲取類對(duì)象。這種效率看起來并不高。

    ActivityRouter 的解決方案是(demo中有2個(gè)組件名為’app’和’sdk’):

    • 在主app module中有一個(gè) @Modules({“app”, “sdk”}) 注解用來標(biāo)記當(dāng)前app內(nèi)有多少組件,根據(jù)這個(gè)注解生成一個(gè)RouterInit類
    • 在 RouterInit類 的 init方法 中生成調(diào)用同一個(gè)包內(nèi)的 RouterMapping_app.map
    • 每個(gè) module 生成的類(RouterMapping_app.java 和 RouterMapping_sdk.java)都放在com.github.mzule.activityrouter.router包內(nèi)(在不同的aar中,但包名相同)
    • 在 RouterMapping_sdk類 的 map()方法 中根據(jù)掃描到的當(dāng)前 module 內(nèi)所有路由注解,生成了調(diào)用Routers.map(…)方法來注冊(cè)路由的代碼
    • 在 Routers 的所有api接口中最終都會(huì)觸發(fā) RouterInit.init()方法,從而實(shí)現(xiàn)所有路由的映射表注冊(cè)這種方式用一個(gè) RouterInit類 組合了所有 module 中的路由映射表類,運(yùn)行時(shí)效率比掃描所有 dex 文件的方式要高,但需要額外在主工程代碼中維護(hù)一個(gè)組件名稱列表注解: @Modules({“app”, “sdk”})

    還有沒有更好的辦法呢?

    Transform API: 可以在編譯時(shí)(dex/proguard之前)掃描當(dāng)前要打包到apk中的所有類,包括: 當(dāng)前module中java文件編譯后的class、aidl文件編譯后的class、jar包中的class、aar包中的class、project依賴中的class、maven依賴中的class。

    ASM: 可以讀取分析字節(jié)碼、可以修改字節(jié)碼

    二者結(jié)合,可以做一個(gè)gradle插件,在編譯時(shí)自動(dòng)掃描所有組件類(IComponent接口實(shí)現(xiàn)類),然后修改字節(jié)碼,生成代碼調(diào)用掃描到的所有組件類的構(gòu)造方法將其注冊(cè)到一個(gè)組件管理類(ComponentManager)中,生成組件名稱與組件對(duì)象的映射表。

    此gradle插件被命名為:AutoRegister,現(xiàn)已開源,并將功能升級(jí)為編譯時(shí)自動(dòng)掃描任意指定的接口實(shí)現(xiàn)類(或類的子類)并自動(dòng)注冊(cè)到指定類的指定方法中。只需要在app/build.gradle中配置一下掃描的參數(shù),沒有任何代碼侵入,原理詳細(xì)介紹:

    http://blog.csdn.net/cdecde111/article/details/78074692

    如何兼容同步/異步方式調(diào)用組件?

    通過實(shí)現(xiàn) java.util.concurrent.Callable 接口同步返回結(jié)果來兼容同步/異步調(diào)用:

    • 同步調(diào)用時(shí),直接調(diào)用 CCResult result = Callable.call() 來獲取返回結(jié)果
    • 異步調(diào)用時(shí),將其放入線程池中運(yùn)行,執(zhí)行完成后調(diào)用回調(diào)對(duì)象返回結(jié)果: IComponentCallback.onResult(cc, result)

    ExecutorService.submit(callable)

    如何兼容同步/異步方式實(shí)現(xiàn)組件?

    調(diào)用組件的 onCall方法 時(shí),可能需要異步實(shí)現(xiàn),并不能同步返回結(jié)果,但同步調(diào)用時(shí)又需要返回結(jié)果,這是一對(duì)矛盾。

    此處用到了 Object 的wait-notify機(jī)制,當(dāng)組件需要異步返回結(jié)果時(shí),在CC框架內(nèi)部進(jìn)行阻塞,等到結(jié)果返回時(shí),通過notify中止阻塞,返回結(jié)果給調(diào)用方

    注意,這里要求在實(shí)現(xiàn)一個(gè)組件時(shí),必須確保組件一定會(huì)回調(diào)結(jié)果,即:需要確保每一種導(dǎo)致調(diào)用流程結(jié)束的邏輯分支上(包括if-else/try-catch/Activity.finish()-back鍵-返回按鈕等等)都會(huì)回調(diào)結(jié)果,否則會(huì)導(dǎo)致調(diào)用方一直阻塞等待結(jié)果,直至超時(shí)。類似于向服務(wù)器發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求后服務(wù)器必須返回請(qǐng)求結(jié)果一樣,否則會(huì)導(dǎo)致請(qǐng)求超時(shí)。

    如何進(jìn)行跨進(jìn)程組件任意功能的調(diào)用(不只是啟動(dòng)Activity)?

    市面上常見的組件化框架采用的通信解決方案有:

    URLScheme(例如:ActivityRouter、ARouter等)

    • 優(yōu)勢有:基因中自帶支持從webview中調(diào)用不用互相注冊(cè)(不用知道需要調(diào)用的app的進(jìn)程名稱等信息)
    • 劣勢有:只能單向地給組件發(fā)送信息,適用于啟動(dòng)Activity和發(fā)送指令,不適用于獲取數(shù)據(jù)(例如:獲取用戶組件的當(dāng)前用戶登錄信息)需要有個(gè)額外的中轉(zhuǎn)Activity來統(tǒng)一處理URLScheme
      如果設(shè)備上安裝了多個(gè)使用相同URLScheme的app,會(huì)彈出選擇框(多個(gè)組件作為app同時(shí)安裝到設(shè)備上時(shí)會(huì)出現(xiàn)這個(gè)問題)
      無法進(jìn)行權(quán)限設(shè)置,無法進(jìn)行開關(guān)設(shè)置,存在安全性風(fēng)險(xiǎn)

    AIDL (例如:ModularizationArchitecture)

    • 優(yōu)勢有:可以傳遞Parcelable類型的對(duì)象效率高
      可以設(shè)置跨app調(diào)用的開關(guān)
    • 劣勢有:調(diào)用組件之前需要提前知道該組件在那個(gè)進(jìn)程,否則無法建立ServiceConnection組件在作為獨(dú)立app和作為lib打包到主app時(shí),進(jìn)程名稱不同,維護(hù)成本高

    設(shè)計(jì)此功能時(shí),我的出發(fā)點(diǎn)是:作為組件化開發(fā)框架基礎(chǔ)庫,想盡量讓跨進(jìn)程調(diào)用與在進(jìn)程內(nèi)部調(diào)用的功能一致,對(duì)使用此框架的開發(fā)者在切換app模式和lib模式時(shí)盡量簡單,另外需要盡量不影響產(chǎn)品安全性。因此,跨組件間通信實(shí)現(xiàn)的同時(shí),應(yīng)該滿足以下條件:

    • 每個(gè)app都能給其它app調(diào)用
    • app可以設(shè)置是否對(duì)外提供跨進(jìn)程組件調(diào)用的支持
    • 組件調(diào)用的請(qǐng)求發(fā)出去之后,能自動(dòng)探測當(dāng)前設(shè)備上是否有支持此次調(diào)用的app
    • 支持超時(shí)、取消

    基于這些需求,我最終選擇了 BroadcastReceiver + Service + LocalSocket 來作為最終解決方案:

    如果 appA 內(nèi)發(fā)起了一個(gè)當(dāng)前app內(nèi)不存在的組件:Component1,則建立一個(gè)LocalServerSocket,同時(shí)發(fā)送廣播給設(shè)備上安裝的其它同樣使用了此框架的 app,同時(shí),若某個(gè) appB 內(nèi)支持此組件,則根據(jù)廣播中帶來的信息與 LocalServerSocket 建立連接,并在 appB 內(nèi)調(diào)用組件 Component1,并將結(jié)果通過 LocalSocket 發(fā)送給 appA。

    BroadcastReceiver 是 android 四大組件之一,可以設(shè)置接收權(quán)限,能避免外部惡意調(diào)用。并且可以設(shè)置開關(guān),接收到此廣播后決定是否響應(yīng)(假裝沒接收到…)。

    之所以建立 LocalSocket 鏈接,是為了能繼續(xù)給這次組件調(diào)用請(qǐng)求發(fā)送超時(shí)和取消的指令。

    用這種方式實(shí)現(xiàn)時(shí),遇到了3個(gè)問題:

    • 由于廣播接收器定義在基礎(chǔ)庫中,所有app內(nèi)都有,當(dāng)用戶在主線程中同步調(diào)用跨app的組件時(shí),調(diào)用方主線程被阻塞,廣播接收器也在需要主線程中運(yùn)行,導(dǎo)致廣播接收器無法運(yùn)行,直至timeout,組件調(diào)用失敗。將廣播接收器放到子進(jìn)程中運(yùn)行問題得到解決
    • 被調(diào)用的app未啟動(dòng)或被手動(dòng)結(jié)束進(jìn)程,遇到廣播接收不到的問題這個(gè)問題暫時(shí)未很好的解決,但考慮到組件化開發(fā)只在開發(fā)期間需要用到跨進(jìn)程通信,開發(fā)者可以通過手動(dòng)在系統(tǒng)設(shè)置中給對(duì)應(yīng)的app賦予自啟動(dòng)權(quán)限來解決問題
    • 跨進(jìn)程調(diào)用時(shí),只能傳遞基本數(shù)據(jù)類型,無法獲取Fragment等java對(duì)象這個(gè)問題在app內(nèi)部調(diào)用時(shí)不存在,app內(nèi)部來回傳遞的都是Map,可以傳遞任何數(shù)據(jù)類型。但由于進(jìn)程間通信是通過字符串來回發(fā)送的,暫時(shí)支持不了非基本數(shù)據(jù)類型,未來可以考慮支持Serializable

    組件如何更方便地在application和library之間切換?

    關(guān)于切換方式在網(wǎng)絡(luò)上有很多文章介紹,基本上都是一個(gè)思路:在 module 的 build.gradle 中設(shè)置一個(gè)變量來控制切換 apply plugin: ‘com.android.application’ 或 apply plugin: ‘com.android.library’ 以及 sourceSets 的切換。

    為了避免在每個(gè) module 的 build.gradle 中配置太多重復(fù)代碼,我做了個(gè)封裝,默認(rèn)為 library模式,提供2種方式切換為application模式:在module的build.gradle中添加 ext.runAsApp = true 或在工程根目錄中 local.properties 中添加 module_name=true

    使用這個(gè)封裝只需一行代碼:

    //將原來的 apply plugin: 'com.android.application'或apply plugin: 'com.android.library' //替換為下面這一行 apply from: 'https://raw.githubusercontent.com/luckybilly/CC/master/cc-settings.gradle'

    如何實(shí)現(xiàn)startActivityForResult?

    android 的 startActivityForResult 的設(shè)計(jì)也是為了頁面?zhèn)髦?#xff0c;在CC組件化框架中,頁面?zhèn)髦蹈静恍枰玫?startActivityForResult,直接作為異步實(shí)現(xiàn)的組件來處理(在原來 setResult 的地方調(diào)用 CC.sendCCResult(callId, ccResult),另外需要注意:按back鍵及返回按鈕的情況也要回調(diào)結(jié)果)即可。

    如果是原來項(xiàng)目中存在大量的 startActivityForResult 代碼,改造成本較大,可以用下面這種方式來保留原來的 onActivityResult(…) 及 activity 中 setResult 相關(guān)的代碼:

    • 在原來調(diào)用 startActivityForResult 的地方,改用CC方式調(diào)用,將當(dāng)前context傳給組件

    CC.obtainBuilder("demo.ComponentA") .setContext(context) .addParams("requestCode", requestCode) .build() .callAsync();

    • 在組件的 onCall(cc)方法 中用 startActivityForResult 的方式打開 Activity

    @Override public boolean onCall(CC cc) { Context context = cc.getContext(); Object code = cc.getParams().get("requestCode"); Intent intent = new Intent(context, ActivityA.class); if (!(context instanceof Activity)) { //調(diào)用方?jīng)]有設(shè)置context或app間組件跳轉(zhuǎn),context為application intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } if (context instanceof Activity && code != null && code instanceof Integer) { ((Activity)context).startActivityForResult(intent, (Integer)code); } else { context.startActivity(intent); } CC.sendCCResult(cc.getCallId(), CCResult.success()); return false; }

    如何阻止非法的外部調(diào)用?

    為了適應(yīng)不同需求,有2個(gè)安全級(jí)別可以設(shè)置:

    • 權(quán)限驗(yàn)證(給進(jìn)程間通信的廣播設(shè)置權(quán)限,一般可設(shè)置為簽名級(jí)權(quán)限校驗(yàn)),步驟如下:
    • 新建一個(gè)module
    • 在該module的build.gradle中添加對(duì)基礎(chǔ)庫的依賴,如: compile ‘com.billy.android:cc:0.3.0′
    • 在該module的src/main/AndroidManifest.xml中設(shè)置權(quán)限及權(quán)限的級(jí)別,參考component_protect_demo
    • 其它每個(gè)module都額外依賴此module,或自定義一個(gè)全局的cc-settings.gradle,參考cc-settings-demo-b.gradle
    • 外部調(diào)用是否響應(yīng)的開關(guān)設(shè)置(這種方式使用起來更簡單一些)
    • 在Application.onCreate()中調(diào)用CC.enableRemoteCC(false)可關(guān)閉響應(yīng)外部調(diào)用

    為了方便開發(fā)者接入,默認(rèn)是開啟了對(duì)外部組件調(diào)用的支持,并且不需要權(quán)限驗(yàn)證。app正式發(fā)布前,建議調(diào)用 CC.enableRemoteCC(false) 來關(guān)閉響應(yīng)外部調(diào)用本app的組件。

    如何與Activity、Fragment的生命周期關(guān)聯(lián)起來

    背景:在使用異步調(diào)用時(shí),由于callback對(duì)象一般是使用匿名內(nèi)部類,會(huì)持有外部類對(duì)象的引用,容易引起內(nèi)存泄露,這種內(nèi)存泄露的情況在各種異步回調(diào)中比較常見,如Handler.post(runnable)、Retrofit的Call.enqueue(callback)等。

    為了避免內(nèi)存泄露及頁面退出后取消執(zhí)行不必要的任務(wù),CC添加了生命周期關(guān)聯(lián)的功能,在onDestroy方法被調(diào)用時(shí)自動(dòng)cancel頁面內(nèi)所有未完成的組件調(diào)用

    • Activity生命周期關(guān)聯(lián)

    在api level 14 (android 4.0)以上可以通過注冊(cè)全局activity生命周期回調(diào)監(jiān)聽,在onActivityDestroyed方法中找出所有此activity關(guān)聯(lián)且未完成的cc對(duì)象,并自動(dòng)調(diào)用取消功能:

    application.registerActivityLifecycleCallbacks(lifecycleCallback);

    • android.support.v4.app.Fragment生命周期關(guān)聯(lián)

    support庫從25.1.0開始支持給fragment設(shè)置生命周期監(jiān)聽:

    FragmentManager.registerFragmentLifecycleCallbacks(callback)

    可在其 onFragmentDestroyed 方法中取消未完成的cc調(diào)用

    • andorid.app.Fragment生命周期關(guān)聯(lián)(暫不支持)

    CC執(zhí)行流程詳細(xì)解析

    組件間通信采用了組件總線的方式,在基礎(chǔ)庫的組件管理類(ComponentMananger)中注冊(cè)了所有組件對(duì)象,ComponentMananger通過查找映射表找到組件對(duì)象并調(diào)用。

    當(dāng)ComponentMananger接收到組件的調(diào)用請(qǐng)求時(shí),查找當(dāng)前app內(nèi)組件清單中是否含有當(dāng)前需要調(diào)用的組件

    有:執(zhí)行App內(nèi)部CC調(diào)用的流程:

    沒有:執(zhí)行App之間CC調(diào)用的流程

    組件的同步/異步實(shí)現(xiàn)和組件的同步/異步調(diào)用原理

    組件實(shí)現(xiàn)時(shí),當(dāng)組件調(diào)用的相關(guān)功能結(jié)束后,通過CC.sendCCResult(callId, ccResult)將調(diào)用結(jié)果發(fā)送給框架

    IComponent實(shí)現(xiàn)類(組件入口類)onCall(cc)方法的返回值代表是否異步回調(diào)結(jié)果:

    • true: 將異步調(diào)用CC.sendCCResult(callId, ccResult)
    • false: 將同步調(diào)用CC.sendCCResult(callId, ccResult)。意味著在onCal方法執(zhí)行完之前會(huì)調(diào)用此方法將結(jié)果發(fā)給框架

    當(dāng)IComponent.onCall(cc)返回 false 時(shí),直接獲取CCResult并返回給調(diào)用方

    當(dāng)IComponent.onCall(cc)返回true時(shí),將進(jìn)入wait()阻塞,知道獲得CCResult后通過notify()中止阻塞,繼續(xù)運(yùn)行,將CCResult返回給調(diào)用方

    通過ComponentManager調(diào)用組件時(shí),創(chuàng)建一個(gè)實(shí)現(xiàn)了java.util.concurrent.Callable接口ChainProcessor類來負(fù)責(zé)具體組件的調(diào)用

    • 同步調(diào)用時(shí),直接執(zhí)行ChainProcessor.call()來調(diào)用組件,并將CCResult直接返回給調(diào)用方
    • 異步調(diào)用時(shí),將ChainProcessor放入線程池中執(zhí)行,通過IComponentCallback.onResult(cc, ccResult)將CCResult回調(diào)給調(diào)用方

    執(zhí)行過程如下圖所示:

    自定義攔截器(ICCInterceptor)實(shí)現(xiàn)原理

    所有攔截器按順序存放在調(diào)用鏈(Chain)中

    在自定義攔截器之前有1個(gè)CC框架自身的攔截器:

    • ValidateInterceptor

    在自定義攔截器之后有2個(gè)CC框架自身的攔截器:

    • LocalCCInterceptor(或RemoteCCInterceptor)
    • Wait4ResultInterceptor

    Chain類負(fù)責(zé)依次執(zhí)行所有攔截器interceptor.intercept(chain)

    攔截器intercept(chain)方法通過調(diào)用Chain.proceed()方法獲取CCResult

    App內(nèi)部CC調(diào)用流程

    當(dāng)要調(diào)用的組件在當(dāng)前app內(nèi)部時(shí),執(zhí)行此流程,完整流程圖如下:

    CC的主體功能由一個(gè)個(gè)攔截器(ICCInterceptor)來完成,攔截器形成一個(gè)調(diào)用鏈(Chain),調(diào)用鏈由ChainProcessor啟動(dòng)執(zhí)行,ChainProcessor對(duì)象在ComponentManager中被創(chuàng)建。

    因此,可以將ChainProcessor看做一個(gè)整體,由ComponentManager創(chuàng)建后,調(diào)用組件的onCall方法,并將組件執(zhí)行后的結(jié)果返回給調(diào)用方。

    ChainProcessor內(nèi)部的Wait4ResultInterceptor

    ChainProcessor的執(zhí)行過程可以被timeout和cancel兩種事件中止。

    App之間CC調(diào)用流程

    當(dāng)要調(diào)用的組件在當(dāng)前app內(nèi)找不到時(shí),執(zhí)行此流程,完整流程圖如下:

    結(jié)語

    本文比較詳細(xì)地介紹了android組件化開發(fā)框架《CC》的主要功能、技術(shù)方案及執(zhí)行流程,并給出了使用方式的簡單示例。

    大家如果感興趣的話可以從GitHub上clone源碼來進(jìn)行具體的分析,如果有更好的思路和方案也歡迎貢獻(xiàn)代碼進(jìn)一步完善CC。

    轉(zhuǎn)載請(qǐng)注明:Android開發(fā)中文站 ? 教你打造一個(gè)Android組件化開發(fā)框架

    總結(jié)

    以上是生活随笔為你收集整理的单独组件_阿里P8年薪百万大牛-教你打造一个Android组件化开发框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。