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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android app.build文件_网易友品 Android 客户端组件化演进

發布時間:2024/1/23 Android 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android app.build文件_网易友品 Android 客户端组件化演进 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文作者:簡書 - 四單老師

項目背景

主站業務經歷了長期的迭代維護,業務的增長同時帶來每個版本業務量繁重,迭代周期很快。同時團隊也在不斷的擴張,對應拆分了組內不同的業務線對接不同業務線的需求,最初的Android客戶端單一的設計架構已經逐漸不滿足快速的業務開發需求。歷經組內討論開始對項目整理進行組件化的遷移,通過組件化的方式滿足不同業務線業務開發的穩定性,是迭代開發更靈活,組內協作開發效率得到提升。同時又有新的項目立項需要投入開發,一方面可以通過新項目實踐和推進組件化的遷移,另一方面也可以通過組件化拆分后的技術組件復用來更快的搭建和開發新的項目。

組件化的準備

技術準備

1. 主站最初的app項目只有一個模塊,業務耦合嚴重,技術組件很難復用,所以我們采取的第一步是拆分部分基礎組件下沉為一個Base庫,盡量去解耦業務提取基礎技術組件達到多業務模塊的復用,也是為了支持新項目和主站項目多個app的技術支持。

2. 考慮組件化后的業務相對隔離,但是客戶端組件間需要建立訪問,所以需要組件間通信的介入。我們采取的方式是路由、服務和全局通知。

3. 搭建路由庫支持,目的是解決業務組件物理隔離后的UI跳轉和訪問,通過維護路由表的方式尋址到需要訪問的業務組件UI。我們采取的是技術實現是通過注解給對應的業務UI比如LoginActivity上用注解申明對應的路由地址,在公共依賴的接口處公開維護這個路由地址常量,暴露給其他業務組件通過方位該地址來跳轉到對應的業務組件UI。

@Router(RouterPath.LOGIN_PAGE) public class LoginActivity extends BaseCompatActivitypublic class RouterPath {/*** 登錄*/public static final String LOGIN_PAGE = "/native/youpin-login.html";/*** 搜索key*/public static final String SEARCH_KEY = "/native/youpin-search-key.html";... }

對應Act綁定上路由地址后,需要對路由的地址進行統一的收集管理。同時也為了支持某些服務動態下發的地址,策略是優先在本地的路由表進行匹配,如果查詢到了該地址有對應的Native界面優先跳轉到Native的界面,未匹配到則跳轉到由webView容器承載的網頁。目前我們采取的方式是通過APT自動生成對應路由注解后的activity的收集類。

//自動生成的類,命名規則是RouterGenerator+業務組件模塊名稱 //RouterGenerator_login.classpublic class RouterGenerator_login implements RouterProvider {public RouterGenerator_login() {}public void loadRouter(Map<String, Route> routerMap, Map<String, Route> pageNameRouterMap) {String keyLoginActivity = "((https|http|yiupin|native)://(w+.)?yiupin.com/native/youpin-login.html)|(" + RouteBuilder.generateUriFromClazz(LoginActivity.class) + ")";routerMap.put(keyLoginActivity, RouteBuilder.build(keyLoginActivity, 0, false, (String[])null, LoginActivity.class));} }

然后再通過ASM的方式在編譯期對所有加載到工程里面的模塊組件通過特定的規則進行上面路由輔助類的收集。

//收集路由地址 ['scanInterface' : 'com.kaola.annotation.provider.RouterProvider','scanSuperClasses' : [],'codeInsertToClassName': 'com.kaola.core.center.router.RouterMap',//未指定codeInsertToMethodName,默認插入到static塊中,故此處register必須為static方法'registerMethodName' : 'register','include' : ['com/kaola/annotation/provider/result/.*' ]//根據工程依賴的所有組件模塊收集所有實現RouterProvider的輔助類。 //然后插入到RouterMap的靜態代碼塊中,默認調用無參構造。 //遍歷執行RouterMap中的靜態方法register,添加所有路由地址信息到全局路由表sRouterMap中。public class RouterMap {private static Map<String, Route> sRouterMap = new ConcurrentHashMap<>();private static Map<String, Route> sPageRouterMap = new ConcurrentHashMap<>();private static void register(RouterProvider routerProvider) {routerProvider.loadRouter(sRouterMap, sPageRouterMap);} }

具體實現不再此展開了,此方式的好處就是可以根據需求加載需要的業務組件并且實現自動注冊和收集路由到路由表。如果覺得獨立開發路由庫的成本較高,也可以采取業界主流的一些路由庫比如ARouter等,基本類似。

4. 關于組件間服務通信的方式,目前采取的是暴露對應的服務接口供各個業務組件方調用。每個業務組件都會申明需要對外暴露提供的方法,并在自己的業務組件模塊內實現這些具體被調用的方法。對外接口庫根據模塊劃分,可以申明和維護通信間的一些數據類型,比如公開的數據model和對應需要訪問的一些路由地址等。為了便于服務的動態收集,這些服務接口可以統一的繼承某個規則接口,然后采取上述路由的方式,對所有實現了該規則接口的服務接口統一的收集管理。

facade/pay/model/PayModel.classIPayService.class//IService.class,統一對繼承IService的服務接口的具體實現類進行收集interface PayService : IService {fun startH5PaySercive(context: Context)}pay_module/PayServiceImpl.classclass PayServiceImpl : PayService {override fun startH5PaySercive(context: Context) {//...}}

剩下一些特點場景的業務,比如:登錄成功后需要全局通知刷新多個UI某個業務狀態的時候,目前采取EventBus的方式進行訂閱通知。

5. 在組件base庫一定下沉和組件間通信方式的確立,開始對組件的具體的拆分粒度進行劃分。大致劃分為業務組件和技術組件兩部分。

組件化的拆分流程

拆分前的考慮

考慮新的項目投入的人力資源有限,并且需要快速的開發上線,同時業務也有重合的場景。所以當時采取的開發策略是將主站未組件化的代碼完全拷貝一份到新項目,并在此的基礎上進行改造。改造的原則必須遵循2個應用共建同一套BaseLib,但是由于主站的BaseLib里面會耦合一些自身的業務組件,同時避免對BaseLib的修改影響到主站的業務開發而增加不必要的工作量。當時采取的策略是通過增加一層業務基礎組件庫來做新項目組件化拆分的緩沖層BaseCompatLib。

拆分過程

拆分過程中有很多業務組件共用的情況,結合當時的開發周期可以適當的去解耦部分業務組件重新劃分到對應拆分后的業務模塊中。如果時間有限,可以先挪到BaseCompatLib這個緩沖成暫時共用待后續再拆,從而避免對2個項目共用的Base庫頻繁修改帶來的負擔。

初期的業務模塊獨立編譯的配置方式,僅供參考:

//gradle.properties中申明編譯配置是否是獨立編譯 # Module Build isModuleInjectBuild=true//moduleLibrary的build.gradle中申明編譯方式 if (isModuleInjectBuild.toBoolean()) {apply from: '../build_module.gradle' } else {apply from: '../build_app.gradle' }//新建一個appbuild文件,用來支業務組件以app方式編譯時所需的配置 //示例: java/appbuild/BuildInfo.class //獨立配置HomeServiceImpl.class //改寫應用啟動跳轉的UIApp.class //獨立編譯時的application,用于初始化配置android {//配置源碼路徑sourceSets {main {jniLibs.srcDirs = ['src/main/jnilibs']//如果是整體編譯,可以移除獨立編譯所需的額外代碼if (isModuleInjectBuild.toBoolean()) {java {exclude 'appbuild/**'}}}} }

遇到的問題

拆分后的獨立模塊由于一些基礎服務的初始化仍停留在app殼工程,一些sdk或者初始化服務沒有統一的管理。優先級混亂并且耦合大量的業務邏輯,導致業務模塊拆分后無法獨立運行,缺失對應組件所需服務的初始化步驟。開始改造初始化的業務,原理同自動收集一致。

interface IInitializer {fun loadInQueue(queue: PriorityQueue<InitialTask>) //收集需要的服務進隊列fun init(processName: String) //對應初始化服務的實現 }class InitialManager {companion object {private val mInitializerQueue = PriorityQueue<InitialTask>() //服務隊列private var mCurProcessName: String = "" //當前啟動的進程//應用初始化時的調用的入口函數@JvmStaticfun initial(curProcessName: String) {mCurProcessName = curProcessNameinitialInProcess()}@JvmStaticfun initialInProcess() {loop@ while (mInitializerQueue.isNotEmpty()) { //搜索接入了多少三方sdk功能,總任務隊列val initialTask = mInitializerQueue.poll() //按優先級取//根據是否擁有權限去加載普通任務//特殊不需要檢查權限的任務,包括:Config和Permission初始化本身的任務。//目前這些優先級必須高于普通任務,否則會被提前打斷,等到權限獲取后才會執行。when {PermissionUtils.isNecessaryPermissionGranted() || initialTask.isNoNeedPermissionCheck() -> {executeTask(initialTask)}else -> {//一旦被權限檢查打斷不能執行,取出的任務重新放回隊列。跳出任務隊列,等待權限獲取后的再次執行。mInitializerQueue.add(initialTask)break@loop}}}}/*** 執行任務,匹配對應進程,對應進程啟動對應需要初始化的任務,沿用主站的邏輯*/private fun executeTask(initialTask: InitialTask) {initialTask.processName.forEach {//當前進程和服務需要初始化的進程相匹配或者是全進程需要就加載if (it == mCurProcessName || it == InitialTask.INITIAL_ALL_PROCESS) {Log.d("InitialManager", "initial - process:$mCurProcessName & initialTask:${initialTask.initialName}")initialTask.initializer.init(mCurProcessName)return@forEach}}}@JvmStaticfun register(initializer: IInitializer) {initializer.loadInQueue(mInitializerQueue)}}//示例服務 class QiyuSdkInitial : IInitializer {override fun loadInQueue(queue: PriorityQueue<InitialTask>) {//主進程需要val initialTask = InitialTask(processName = mutableListOf(ProcessConst.MAIN_PROCESS, ProcessConst.NIM_PROCESS),initialName = this::class.java.simpleName,initializer = this)queue.add(initialTask)}override fun init(processName: String) {try {QiyuSdk.initUnicorn(AppDelegate.sApplication)} catch (e: Throwable) {e.printStackTrace()}} }

徹底組件化

組件庫的獨立發布和維護

原有拆分的本地組件徹底分離出去,采取獨立發布和維護的方式迭代更新。

  • 新建git倉庫和本地組件項目,然后以module的方式將原有項目中的業務module導入到本地新建的項目中。推送該項目到git的獨立倉庫。(目前未采取git subModule的 方式管理,但大致差不多)
  • 新建的本地項目中再新建一個對應的接口工程用于對外暴露模塊中的業務訪問。
  • project: component-login/app //殼工程/login //登錄模塊module/login-facade //登錄模塊接口module
  • 添加打包aar發布到maven倉庫的腳本用來獨立發布login和login-facade模塊。
  • 遵循對應的發布規范,不同項目的app殼工程根據自身的業務需求進行對應的組件依賴,版本開發階段可采取snapshot的進行依賴。不同的業務組件也可以通過依賴其他不同的業務組件接口達到訪問的目的。(如需實際運行,不光需要再接入接口庫還需要依賴對應的組件工程)
  • 目前友品采取的是jenkins的打包發布方式,僅供參考。
  • 本地開發調試模式

    在組件開發過程中,單純的依靠遠程方式依賴,對開發階段的頻繁修改不友好。所以我們采取依賴覆蓋的方式,讓原有的依賴在編譯過程中替換掉遠程的版本改用本地的版本進行引用。

    // 自定義const.gradle環境聲明 def version = '1.5.11' ext.sdk = [YpBase : { "com.kaola:ypbase:${version}" } ]//app build.gradle dependencies {api gradle.sdk.YpBase(this) }//setting.gradle gradle.ext {sdk = sdk } //本地依賴時需要修改為本地的路徑 def YpBase_PATH = "localpath/base" def YpBase_as_aar = [] def YpBase_as_sources = [['YpBase', ":base", ['type': 'project', 'path': "${YpBase_PATH}/base"]], ] def overrideList = YpBase_as_aar // *核心* 打開注釋使用源碼引入YpBase overrideList = YpBase_as_sourcesdef overrideLibrary(Map define, String whichLibrary, String name, Map prjType) {def overrideType = prjType.get("type")if (overrideType == 'module') {include(name)define.put(whichLibrary, {it.project(name)})} else if (overrideType == 'project') {include(name)project(name).projectDir = new File(prjType.get('path'))define.put(whichLibrary, {it.project(name)})} else if (overrideType == 'aar') {define.put(whichLibrary, { prjType.get('path') })} else {; // ignore} }for (int i = 0; i < overrideList.size(); i++) {def override = overrideList[i]println 'override: ' + override[0]overrideLibrary(sdk, override[0], override[1], override[2]) }

    通過以上的方式讓Base的依賴從遠程替換為本地module的形式。開發階段就可以通過AS的refactor進行代碼的優化和重構,對本地Base修改后到Base的git分支進行對應的提交或MR合回主分支然后走規范的發布打包流程。

    組件版本依賴管理

    組件項目中會有對Base或者接口庫的引用,對于Base我們可以選擇compileOnly的方式,也可以選擇直接依賴的方式。在集成到項目中后依賴會遵循gradle的依賴傳遞原則。特別注意:

  • 避免環形依賴的產生。比如:facade -> base, base -> facade。遇到這種情況需要拆分所需依賴到另外一層。
  • 在遠程依賴替換為本地依賴做開發修改時可能會遇到遠程依賴和本地依賴的沖突。比如:app -> login -> com.xxx:base; app -> home -> /localpath/base。 此時可以采取下面的方式進行依賴優先選擇本地的方式排除掉其他組件中的遠程依賴。
  • //setting.gradle def base_exist = false for (int i = 0; i < overrideList.size(); i++) {def override = overrideList[i]println 'override: ' + override[0]if (override[0] == 'YpBase') {base_exist = true}overrideLibrary(sdk, override[0], override[1], override[2]) }gradle.ext {kulabase_exist = base_exist }//app.gradle if (gradle.kulabase_exist) {println 'kulabase_exist exist, exclude all aar dependences'android {configurations {all*.exclude group: 'com.xxx', module: 'base'}} }

    后續

    到此為止基本上組件化就可以持續穩定的開發和維護了,組件化后也給團隊的開發效率帶來一定的提升,代碼也可以在一定可控的范圍內穩定的維護。并且在各自維護的組件中,大家也可以根據各自需求選擇合適自己業務的開發框架比如:mvp、LiveData、Rx等或者嘗試使用新語言Kotlin去編寫。解決業務耦合帶來的負擔同時也使各個組件達到了較高的可復用性,靈活的支持不同的應用項目,達到可插拔的方式集成開發。后續項目也會做一些優化,針對版本依賴的管理和簡化組件編譯和發布集成的流程來提高協作開發的效率。

    ASM自動收集參考:https://github.com/luckybilly/AutoRegister

    總結

    以上是生活随笔為你收集整理的android app.build文件_网易友品 Android 客户端组件化演进的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 大咪咪dvd| 一区二区三区四区精品 | 国产干b| youjizz日韩 | 林由奈在线观看 | 日韩免费久久 | 亚洲一区电影 | 主播一区二区 | 性久久久久久久久久久 | 韩国av电影网站 | 天天射天天舔 | 亚洲性少妇 | 中文字幕 视频一区 | 狠狠操夜夜爽 | kendra lust free xxx | 性一交一黄一片 | 男女h网站 | 捆绑无遮挡打光屁股调教女仆 | 精品99999| 免费成人在线观看视频 | 久久久视频在线观看 | 猫咪av网 | 日韩视频久久 | 福利视频一区二区三区 | 日韩av女优在线观看 | 亚洲尤物在线 | 美女扒开大腿让男人桶 | 少妇久久精品 | 最新地址在线观看 | 成年人网站黄色 | 久久久久久亚洲精品中文字幕 | 免费污片网站 | 亚洲精品一区二区18漫画 | 国产精品3| 青青草原成人网 | 性高潮久久久久 | 少女情窦初开的第4集在线观看 | 91视频在线免费观看 | www.chengren| 中文天堂资源在线 | juliaann欧美二区三区 | 亚洲巨乳在线 | 午夜精品视频 | 不卡中文字幕av | 国产女人18水真多18精品一级做 | 婷婷九月丁香 | 色一情一乱一伦一区二区三区 | 久久天天躁狠狠躁夜夜av | 国产精品入口久久 | 人人爽爽爽 | 国产精品无码毛片 | 亚色在线观看 | 色偷偷综合 | 国产极品久久 | 韩国明星乱淫(高h)小说 | 777中文字幕 | 激情五月激情综合网 | 中文字幕av有码 | 图书馆的女友在线观看 | 精品无码人妻一区二区三 | 毛片一区二区三区 | 99久久精品免费 | 久久免费国产视频 | 成人人伦一区二区三区 | 北条麻妃一区二区三区四区五区 | 精品人妻天天爽夜夜爽视频 | 天天摸日日摸 | 久久久黄色大片 | 日韩一区二区a片免费观看 伊人网综合在线 | 亚洲无限av| 亚洲蜜臀av | 国产a黄| 香蕉视频在线观看黄 | 欧美 日韩 国产一区 | 日本一区二区在线免费 | 91红桃视频 | 欧美10p | 综合久久久久久久 | 国产亚洲av在线 | 91中文字日产乱幕4区 | 色片在线免费观看 | 蕾丝视频污| 欧美一级专区免费大片 | 他趴在我两腿中间添得好爽在线看 | 色综合中文字幕 | 手机天堂网 | 水蜜桃亚洲精品 | 伊人狼人久久 | 尤物视频在线观看免费 | 国产农村妇女aaaaa视频 | 日韩欧美理论 | 久久久精品国产sm调教 | 男女久久久 | 精品99在线 | 婷婷激情综合 | 亚洲va欧美va国产综合久久 | 人人九九 | 色多多视频在线 | 黄色av网站在线免费观看 |