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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

Android

java路由总线_网易考拉Android客户端路由总线设计

發(fā)布時(shí)間:2023/12/20 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java路由总线_网易考拉Android客户端路由总线设计 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.前言

$ e7 |??~% L) i7 @7 B& t3 T5 h* P/ e2 s

當(dāng)前,Android路由框架已經(jīng)有很多了,如雨后春筍般出現(xiàn),大概是因?yàn)槿ツ晏岢隽薃ndroid組件化的概念。當(dāng)一個(gè)產(chǎn)品的業(yè)務(wù)規(guī)模上升到一定程度,或者是跨團(tuán)隊(duì)開(kāi)發(fā)時(shí),團(tuán)隊(duì)/模塊間的合作問(wèn)題就會(huì)暴露出來(lái)。如何保持團(tuán)隊(duì)間業(yè)務(wù)的往來(lái)?如何互不影響或干涉對(duì)方的開(kāi)發(fā)進(jìn)度?如何調(diào)用業(yè)務(wù)方的功能?組件化給上述問(wèn)題提供了一個(gè)答案。組件化所要解決的核心問(wèn)題是解耦,路由正是為了解決模塊間的解耦而出現(xiàn)的。本文闡述了考拉Android端的路由設(shè)計(jì)方案,盡管與市面上的方案大同小異,但更多的傾向于與考拉業(yè)務(wù)進(jìn)行一定程度的結(jié)合。

, o6 H, e5 F* L: J. w/ W1.1 傳統(tǒng)的頁(yè)面跳轉(zhuǎn)

' j* K' U4 f8 a- F+ Q9 g0 O頁(yè)面跳轉(zhuǎn)主要分為三種,App頁(yè)面間跳轉(zhuǎn)、H5跳轉(zhuǎn)回App頁(yè)面以及App跳轉(zhuǎn)至H5。

/ g3 z4 e% Z# r: I6 VApp頁(yè)面間跳轉(zhuǎn)

) x# t8 E& s9 D9 d9 AApp頁(yè)面間的跳轉(zhuǎn),對(duì)于新手來(lái)說(shuō)一般會(huì)在跳轉(zhuǎn)的頁(yè)面使用如下代碼:

1 V* {7 M. p2 {??_8 h; a, l4 q??hIntent intent = new Intent(this, MainActivity.class);intent.putExtra("dataKey", "dataValue");startActivity(intent);

對(duì)于有一定經(jīng)驗(yàn)的程序員,會(huì)在跳轉(zhuǎn)的類生成自己的跳轉(zhuǎn)方法:: w0 e+ \. J/ ^6 ]' m5 y* [4 w

public class OrderManagerActivity extends BaseActivity {? ? public static void launch(Context context, int startTab) {? ?? ???Intent i = new Intent(context, OrderManagerActivity.class);? ?? ???i.putExtra(INTENT_IN_INT_START_TAB, startTab);? ?? ???context.startActivity(i);? ? }}

無(wú)論使用哪種方式,本質(zhì)都是生成一個(gè)Intent,然后再通過(guò)Context.startActivity(Intent)/Activity.startActivityForResult(Intent, int)實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)。這種方式的不足之處是當(dāng)包含多個(gè)模塊,但模塊間沒(méi)有相互依賴時(shí),這時(shí)候的跳轉(zhuǎn)會(huì)變得相當(dāng)困難。如果已知其他模塊的類名以及對(duì)應(yīng)的路徑,可以通過(guò)Intent.setComponent(Component)方法啟動(dòng)其他模塊的頁(yè)面,但往往模塊的類名是有可能變化的,一旦業(yè)務(wù)方把模塊換個(gè)名字,這種隱藏的Bug對(duì)于開(kāi)發(fā)的內(nèi)心來(lái)說(shuō)是崩潰的。另一方面,這種重復(fù)的模板代碼,每次至少寫(xiě)兩行才能實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn),代碼存在冗余。5 P* E2 R2 s" F8 \

H5-App頁(yè)面跳轉(zhuǎn); I" j: C" y3 @1 @

對(duì)于考拉這種電商應(yīng)用,活動(dòng)頁(yè)面具有時(shí)效性和即時(shí)性,這兩種特性在任何時(shí)候都需要得到保障。運(yùn)營(yíng)隨時(shí)有可能更改活動(dòng)頁(yè)面,也有可能要求點(diǎn)擊某個(gè)鏈接就能跳轉(zhuǎn)到一個(gè)App頁(yè)面。傳統(tǒng)的做法是對(duì)WebViewClient.shouldOverrideUrlLoading(WebView, String)進(jìn)行攔截,判斷url是否有對(duì)應(yīng)的App頁(yè)面可以跳轉(zhuǎn),然后取出url中的params封裝成一個(gè)Intent傳遞并啟動(dòng)App頁(yè)面。0 k' w$ L, i) y; ]$ d??|7 \

感受一下在考拉App工程中曾經(jīng)出現(xiàn)過(guò)的下面這段代碼:

: U! z4 o( V& j8 w9 u/ U; h% vpublic static Intent startActivityByUrl(Context context, String url, boolean fromWeb, boolean outer) {? ? if (StringUtils.isNotBlank(url) && url.startsWith(StringConstants.REDIRECT_URL)) {? ?? ?? ? try {? ?? ?? ?? ?String realUrl = Uri.parse(url).getQueryParameter("target");? ?? ?? ?? ?if (StringUtils.isNotBlank(realUrl)) {? ?? ?? ?? ?? ? url = URLDecoder.decode(realUrl, "UTF-8");? ?? ?? ?? ?}? ?? ???} catch (Exception e) {? ?? ?? ?? ?e.printStackTrace();? ?? ???}? ? }? ? Intent intent = null;? ? try {? ?? ???Uri uri = Uri.parse(url);? ?? ???String host = uri.getHost();? ?? ???List pathSegments = uri.getPathSegments();? ?? ???String path = uri.getPath();? ?? ???int segmentsLength = (pathSegments == null ? 0 : pathSegments.size());? ?? ???if (!host.contains(StringConstants.KAO_LA)) {? ?? ?? ?? ?return null;? ?? ???}? ?? ???if((StringUtils.isBlank(path))){? ?? ?? ?? ?do something...? ?? ?? ?? ?return intent;? ?? ???}? ?? ???if (segmentsLength == 2 && path.startsWith(StringConstants.JUMP_TO_GOODS_DETAIL)) {? ?? ?? ?? ?do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_SPRING_ACTIVITY_TAB)) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_SPRING_ACTIVITY_DETAIL) && segmentsLength == 3) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith(StringConstants.START_CART) && segmentsLength == 1) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_COUPON_DETAIL)? ?? ?? ?? ?? ? || (path.startsWith(StringConstants.JUMP_TO_COUPON) && segmentsLength == 2)) {? ?? ?? ?? ?do something...? ?? ???} else if (canOpenMainPage(host, uri.getPath())) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith(StringConstants.START_ORDER)) {? ?? ?? ?? ? if (!UserInfo.isLogin(context)) {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?} else {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?}? ?? ???} else if (path.startsWith(StringConstants.START_SAVE)) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_NEW_DISCOVERY)) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_NEW_DISCOVERY_2) && segmentsLength == 3) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith(StringConstants.START_BRAND_INTRODUCE)? ?? ?? ?? ?? ? || path.startsWith(StringConstants.START_BRAND_INTRODUCE2)) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith(StringConstants.START_BRAND_DETAIL) && segmentsLength == 2) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_ORDER_DETAIL)) {? ?? ?? ?? ???if (!UserInfo.isLogin(context) && outer) {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?} else {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?}? ?? ???} else if (path.startsWith("/cps/user/certify.html")) {? ?? ?? ?? ?? ?do something...? ?? ???} else if (path.startsWith(StringConstants.IDENTIFY)) {? ?? ?? ?? ? do something...? ?? ???} else if (path.startsWith("/album/share.html")) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith("/album/tag/share.html")) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith("/live/roomDetail.html")) {? ?? ?? ?? ?? ?do something...? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_ORDER_COMMENT)) {? ?? ?? ?? ? if (!UserInfo.isLogin(context) && outer) {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?} else {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?}? ?? ???} else if (openOrderDetail(url, path)) {? ?? ?? ?? ?if (!UserInfo.isLogin(context) && outer) {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?} else {? ?? ?? ?? ?? ? do something...? ?? ?? ?? ?}? ?? ???} else if (path.startsWith(StringConstants.JUMP_TO_SINGLE_COMMENT)) {? ?? ?? ?? ???do something...? ?? ???} else if (path.startsWith("/member/activity/vip_help.html")) {? ?? ?? ?? ?do something...? ?? ???} else if (path.startsWith("/goods/search.html")) {? ?? ?? ?? ?do something...? ?? ???} else if(path.startsWith("/afterSale/progress.html")){? ?? ?? ?? ???do something...? ?? ???} else if(path.startsWith("/afterSale/apply.html")){? ?? ?? ?? ???do something...? ?? ???} else if(path.startsWith("/order/track.html")) {? ?? ?? ?? ? do something...? ?? ???}? ? } catch (Exception e) {? ?? ???e.printStackTrace();? ? }? ? if (intent != null && !(context instanceof Activity)) {? ?? ???intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);? ? }? ? return intent;}

這段代碼整整260行,看到代碼時(shí)我的內(nèi)心是崩潰的。這種做法的弊端在于:" I3 h9 k# j1 H$ R+ d

3 g1 J' |! g, s* ~9 Q??X

判斷不合理。上述代碼僅判斷了HOST是否包含StringConstants.KAO_LA,然后根據(jù)PATH區(qū)分跳轉(zhuǎn)到哪個(gè)頁(yè)面,PATH也只判斷了起始部分,當(dāng)URL越來(lái)越多的時(shí)候很有可能造成誤判。

% J. [/ @! Y9 I

耦合性太強(qiáng)。已知攔截的所有頁(yè)面的引用都必須能夠拿到,否則無(wú)法跳轉(zhuǎn);+ W# ^& [8 P/ O. r

代碼混亂。PATH非常多,從眾多的PATH中匹配多個(gè)已知的App頁(yè)面,想必要判斷匹配規(guī)則就要寫(xiě)很多函數(shù)解決;% o1 K# h! S* C# ^8 z; a0 S( ~

攔截過(guò)程不透明。開(kāi)發(fā)者很難在URL攔截的過(guò)程中加入自己的業(yè)務(wù)邏輯,如打點(diǎn)、啟動(dòng)Activity前添加特定的Flag等;# B2 l. Z- C& ~3 Q% u, I# V8 t

沒(méi)有優(yōu)先級(jí)概念,也無(wú)法降級(jí)處理。同一個(gè)URL,只要第一個(gè)匹配到App頁(yè)面,就只能打開(kāi)這個(gè)頁(yè)面,無(wú)法通過(guò)調(diào)整優(yōu)先級(jí)跳轉(zhuǎn)到別的頁(yè)面或者使用H5打開(kāi)。

I; f% h, n0 IApp頁(yè)面-H5跳轉(zhuǎn)

% v4 i; Q2 B3 t8 h) |5 h& z這種情況不必多說(shuō),啟動(dòng)一個(gè)WebViewActivity即可。) v- f* p: j0 W% A

1.2 頁(yè)面路由的意義& s??c3 z# {. n0 o* D6 l1 ~* r" Z$ S

路由最先被應(yīng)用于網(wǎng)絡(luò)中,路由的定義是通過(guò)互聯(lián)的網(wǎng)絡(luò)把信息從源地址傳輸?shù)侥康牡刂返幕顒?dòng)。頁(yè)面跳轉(zhuǎn)也是相當(dāng)于從源頁(yè)面跳轉(zhuǎn)到目標(biāo)頁(yè)面的過(guò)程,每個(gè)頁(yè)面可以定義為一個(gè)統(tǒng)一資源標(biāo)識(shí)符(URI),在網(wǎng)絡(luò)當(dāng)中能夠被別人訪問(wèn),也可以訪問(wèn)已經(jīng)被定義了的頁(yè)面。路由常見(jiàn)的使用場(chǎng)景有以下幾種:

' ~. ^/ b: G, M8 F# S8 o8 @: i3 Q2 l% [( k- I

App接收到一個(gè)通知,點(diǎn)擊通知打開(kāi)App的某個(gè)頁(yè)面(OuterStartActivity)

3 V, y5 e- U0 P8 f+ ?

瀏覽器App中點(diǎn)擊某個(gè)鏈接打開(kāi)App的某個(gè)頁(yè)面(OuterStartActivity)

/ J# _* O. [- ~2 P' b( R+ K

App的H5活動(dòng)頁(yè)面打開(kāi)一個(gè)鏈接,可能是H5跳轉(zhuǎn),也可能是跳轉(zhuǎn)到某一個(gè)native頁(yè)面(WebViewActivity)

* {% v* ~3 q! q& g8 T# |2 m

打開(kāi)頁(yè)面需要某些條件,先驗(yàn)證完條件,再去打開(kāi)那個(gè)頁(yè)面(需要登錄)

c# b- z??{) @4 ^* x. I

App內(nèi)的跳轉(zhuǎn),可以減少手動(dòng)構(gòu)建Intent的成本,同時(shí)可以統(tǒng)一攜帶部分參數(shù)到下一個(gè)頁(yè)面(打點(diǎn))' S) c9 m2 D3 l! M& F9 i* S2 A. v

除此之外,使用路由可以避免上述弊端,能夠降低開(kāi)發(fā)者頁(yè)面跳轉(zhuǎn)的成本。: z2 E3 e4 z. X$ l( c2.考拉路由總線

& X* J3 S( C8 D9 u, @9 a" Y- L) I

! d8 i2 t. ~, V( G9 Y- J2.1 路由框架

8 Z; l; n, I! v$ [" W/ k, P- l

( n6 |1 J; i6 C8 A??[* b8 X

考拉路由框架主要分為三個(gè)模塊:路由收集、路由初始化以及頁(yè)面路由。路由收集階段,定義了基于Activity類的注解,通過(guò)Android Processing Tool(以下簡(jiǎn)稱“APT”)收集路由信息并生成路由表類;路由初始化階段,根據(jù)生成的路由表信息注入路由字典;頁(yè)面路由階段,則通過(guò)路由字典查找路由信息,并根據(jù)查找結(jié)果定制不同的路由策略略。. E1 B9 g# H" {6 n& e

9 {+ C* }' k& x

2.2 路由設(shè)計(jì)思路

總結(jié)

以上是生活随笔為你收集整理的java路由总线_网易考拉Android客户端路由总线设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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