一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

WMRouter:美團外賣Android開源路由框架

 fishpan_oliver 2018-08-28

WMRouter是一款A(yù)ndroid路由框架,基于組件化的設(shè)計思路,功能靈活,使用也比較簡單。

WMRouter最初用于解決美團外賣App在業(yè)務(wù)演進過程中的實際問題,之后逐步推廣到了美團其他App,因此我們決定將其開源,希望更多技術(shù)同行一起開發(fā),應(yīng)用到更廣泛的場景里去。GitHub項目地址與使用文檔詳見 https://github.com/meituan/WMRouter 。

本文先簡單介紹WMRouter的功能和適用場景,然后詳細介紹WMRouter的發(fā)展背景和過程。

功能簡介

WMRouter主要提供URI分發(fā)、ServiceLoader兩大功能。

URI分發(fā)功能可用于多工程之間的頁面跳轉(zhuǎn)、動態(tài)下發(fā)URI鏈接的跳轉(zhuǎn)等場景,特點如下:

  1. 支持多scheme、host、path

  2. 支持URI正則匹配

  3. 頁面配置支持Java代碼動態(tài)注冊,或注解配置自動注冊

  4. 支持配置全局和局部攔截器,可在跳轉(zhuǎn)前執(zhí)行同步/異步操作,例如定位、登錄等

  5. 支持單次跳轉(zhuǎn)特殊操作:Intent設(shè)置Extra/Flags、設(shè)置跳轉(zhuǎn)動畫、自定義StartActivity操作等

  6. 支持頁面Exported控制,特定頁面不允許外部跳轉(zhuǎn)

  7. 支持配置全局和局部降級策略

  8. 支持配置單次和全局跳轉(zhuǎn)監(jiān)聽

  9. 完全組件化設(shè)計,核心組件均可擴展、按需組合,實現(xiàn)靈活強大的功能

基于SPI (Service Provider Interfaces的設(shè)計思想,WMRouter提供了ServiceLoader模塊,類似Java中的java.util.ServiceLoader,但功能更加完善。通過ServiceLoader可以在一個App的多個模塊之間通過接口調(diào)用代碼,實現(xiàn)模塊解耦,便于實現(xiàn)組件化、模塊間通信,以及和依賴注入類似的功能等。其特點如下:

  1. 使用注解自動配置

  2. 支持獲取接口的所有實現(xiàn),或根據(jù)Key獲取特定實現(xiàn)

  3. 支持獲取Class或獲取實例

  4. 支持無參構(gòu)造、Context構(gòu)造,或自定義Factory、Provider構(gòu)造

  5. 支持單例管理

  6. 支持方法調(diào)用

其他特性:

  1. 優(yōu)化的Gradle插件,對編譯耗時影響很小

  2. 編譯期和運行時配置檢查,避免配置沖突和錯誤

  3. 編譯期自動添加Proguard混淆規(guī)則,免去手動配置的繁瑣

  4. 完善的調(diào)試功能,幫助及時發(fā)現(xiàn)問題


適用場景

WMRouter適用但不限于以下場景:

  1. Native H5混合開發(fā)模式,需要進行頁面之間的互相跳轉(zhuǎn),或進行靈活的運營跳轉(zhuǎn)鏈接下發(fā)??梢岳肳MRouter統(tǒng)一頁面跳轉(zhuǎn)邏輯,根據(jù)不同的協(xié)議(HTTP、HTTPS、用于Native頁面的自定義協(xié)議)跳轉(zhuǎn)對應(yīng)頁面,且在跳轉(zhuǎn)過程中可以使用UriInterceptor對跳轉(zhuǎn)鏈接進行修改,例如跳轉(zhuǎn)H5頁面時在URL中加參數(shù)。

  2. 統(tǒng)一管理來自App外部的URI跳轉(zhuǎn)。來自App外部的URI跳轉(zhuǎn),如果使用Android原生的Manifest配置,會直接啟動匹配的Activity,而很多時候希望先正常啟動App打開首頁,完成常規(guī)初始化流程(例如登錄、定位等)后再跳轉(zhuǎn)目標(biāo)頁面。此時可以使用統(tǒng)一的Activity接收所有外部URI跳轉(zhuǎn),到首頁時再用WMRouter啟動目標(biāo)頁面。

  3. 頁面跳轉(zhuǎn)有復(fù)雜判斷邏輯的場景。例如多個頁面都需要先登錄、先定位后才允許打開,如果使用常規(guī)方案,這些頁面都需要處理相同的業(yè)務(wù)邏輯;而利用WMRouter,只需要開發(fā)好UriInterceptor并配置到各個頁面即可。

  4. 多工程、組件化、平臺化開發(fā)。多工程開發(fā)要求各個工程之間能互相通信,也可能遇到和外賣App類似的代碼復(fù)用、依賴注入、編譯等問題,這些問題都可以利用WMRouter的URI分發(fā)和ServiceLoader模塊解決。

  5. 對業(yè)務(wù)埋點需求較強的場景。頁面跳轉(zhuǎn)作為最常見的業(yè)務(wù)邏輯之一,常常需要埋點。給每個頁面配置好URI,使用WMRouter統(tǒng)一進行頁面跳轉(zhuǎn),并在全局的OnCompleteListener中埋點即可。

  6. 對App可用性要求較高的場景。一方面,可以對頁面跳轉(zhuǎn)失敗進行埋點監(jiān)控上報,及時發(fā)現(xiàn)線上問題;另一方面,頁面跳轉(zhuǎn)時可以執(zhí)行判斷邏輯,發(fā)現(xiàn)異常(例如服務(wù)端異常、客戶端崩潰等)則自動打開降級后的頁面,保證關(guān)鍵功能的正常工作,或給用戶友好的提示。

  7. 頁面A/B測試、動態(tài)配置等場景。在WMRouter提供的接口基礎(chǔ)上進行少量開發(fā)配置,就可以實現(xiàn):根據(jù)下發(fā)的A/B測試策略跳轉(zhuǎn)不同的頁面實現(xiàn);根據(jù)不同的需要動態(tài)下發(fā)一組路由表,相同的URI跳轉(zhuǎn)到不同的一組頁面(實現(xiàn)方面可以自定義UriInterceptor,對匹配的URI返回301的UriResult使跳轉(zhuǎn)重定向)。

基本概念解釋

下面開始介紹WMRouter的發(fā)展背景和過程。為了方便后文的理解,我們先簡單了解和回顧幾個基本概念。

路由

根據(jù)維基百科的解釋,路由(routing)可以理解成在互聯(lián)的網(wǎng)絡(luò)通過特定的協(xié)議把信息從源地址傳輸?shù)侥康牡刂返倪^程。一個典型的例子就是在互聯(lián)網(wǎng)中,路由器可以根據(jù)IP協(xié)議將數(shù)據(jù)發(fā)送到特定的計算機。

URI

URI(Uniform Resource Identifier,統(tǒng)一資源標(biāo)識符)是一個用于標(biāo)識某一互聯(lián)網(wǎng)資源名稱的字符串。URI的組成如下圖所示:

一些常見的URI舉例如下,包括平時經(jīng)常用到的網(wǎng)址、IP地址、FTP地址、文件、打電話、發(fā)郵件的協(xié)議等。

  • http://www.meituan.com

  • http://127.0.0.1:8080

  • ftp://example.org/resource.txt

  • file:///Users/

  • tel:863-1234

  • mailto:chris@example.com

在Android中也提供了android.net.Uri工具類用于處理URI,Android中URI常用的幾個部分主要是scheme、host、path和query。

Android中的Intent跳轉(zhuǎn)

在Android中的Intent跳轉(zhuǎn),分為顯式跳轉(zhuǎn)和隱式跳轉(zhuǎn)兩種。

顯式跳轉(zhuǎn)即指定ComponentName(類名)的Intent跳轉(zhuǎn),一般通過Bundle傳參,示例代碼如下:

Intent intent = new Intent(context, TestActivity.class);
intent.putExtra('param''value')
startActivity(intent);

隱式跳轉(zhuǎn)即不指定ComponentName的Intent跳轉(zhuǎn),通過IntentFilter找到匹配的組件,IntentFilter支持action、category和data的匹配,其中data就是URI。例如下面的代碼,會啟動系統(tǒng)默認(rèn)的瀏覽器打開網(wǎng)頁:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse('http://www.meituan.com'))
startActivity(intent);

Activity通過Manifest配置IntentFilter,例如下面的配置可以匹配所有形如demo_scheme://demo_host/***的URI。

<activity android:name='.app.UriProxyActivity' android:exported='true'>
    <intent-filter>
        <action android:name='android.intent.action.VIEW'/>

        <category android:name='android.intent.category.DEFAULT'/>
        <category android:name='android.intent.category.BROWSABLE'/>

        <data android:scheme='demo_scheme' android:host='demo_host'/>
    </intent-filter>
</activity>


URI跳轉(zhuǎn)


在美團外賣APP早期開發(fā)過程中,產(chǎn)品希望通過后臺下發(fā)URI控制客戶端跳轉(zhuǎn)指定頁面,從而實現(xiàn)靈活的運營配置。外賣App采用了Native H5的混合開發(fā)模式,Native頁面定義了專用的URI,而H5頁面則使用HTTP/HTTPS鏈接在專門的WebView容器中加載,兩種鏈接的跳轉(zhuǎn)邏輯不同,實現(xiàn)起來比較繁瑣。

Native頁面的URI跳轉(zhuǎn)最開始使用的是Android原生的IntentFilter,通過隱式跳轉(zhuǎn)啟動,但是這種方式存在靈活性差、功能缺失、Bug多等問題。例如:

  1. 從外部(瀏覽器、微信等)跳轉(zhuǎn)外賣的URI時,系統(tǒng)會直接打開相應(yīng)的Activity,而沒有經(jīng)過歡迎頁的正常啟動流程,一些代碼邏輯可能沒有執(zhí)行,例如定位邏輯。

  2. 有很多頁面在打開前需要確保用戶先登錄或先定位,每個頁面都寫一遍判斷登錄、定位的邏輯非常麻煩,提高了開發(fā)維護成本。

  3. 運營人員可能會配錯URI,頁面跳轉(zhuǎn)失敗,有些跳轉(zhuǎn)的地方?jīng)]有做try-catch處理,會產(chǎn)生Crash;有些地方雖然加了try-catch,但跳轉(zhuǎn)失敗后沒有任何響應(yīng),用戶體驗差;跳轉(zhuǎn)失敗沒有監(jiān)控,不能及時發(fā)現(xiàn)和解決線上業(yè)務(wù)異常。

為了解決上述問題,我們希望有一個Android的URI分發(fā)組件,可以根據(jù)URI中不同的scheme、host、path,進行不同的處理,同時能夠在頁面跳轉(zhuǎn)過程中進行更靈活的干預(yù)。調(diào)研發(fā)現(xiàn),現(xiàn)有的一些Android路由組件主要都是在解決多工程之間解耦的問題,而URI往往只支持通過path分發(fā),頁面跳轉(zhuǎn)的配置也不夠靈活,難以滿足實際需要。于是我們決定自行設(shè)計實現(xiàn)。

核心設(shè)計思路

下圖展示了WMRouter中URI分發(fā)機制的核心設(shè)計思路。借鑒網(wǎng)絡(luò)請求的機制,WMRouter中的每次URI跳轉(zhuǎn)視為發(fā)起一個UriRequest;URI跳轉(zhuǎn)請求被WMRouter逐層分發(fā)給一系列的UriHandler進行處理;每個UriHandler處理之前可以被UriInterceptor攔截,并插入一些特殊操作。

頁面跳轉(zhuǎn)來源

常見的頁面跳轉(zhuǎn)來源如下:

  1. 來自App內(nèi)部Native頁面的跳轉(zhuǎn)

  2. 來自App內(nèi)Web容器的跳轉(zhuǎn),即H5頁面發(fā)起的跳轉(zhuǎn)

  3. 從App外通過URI喚起App的跳轉(zhuǎn),例如來自瀏覽器、微信等

  4. 從通知中心Push喚起App的跳轉(zhuǎn)

對于來自App內(nèi)部和Web容器的跳轉(zhuǎn),我們把所有跳轉(zhuǎn)代碼統(tǒng)一改成調(diào)用WMRouter處理,而來自外部和Push通知的跳轉(zhuǎn)則全部使用一個獨立的中轉(zhuǎn)Activity接收,再調(diào)用WMRouter處理。

UriRequest

UriRequest中包含Context、URI和Fields,其中Fields為HashMap,可以通過Key存放任意數(shù)據(jù)。簡單起見,UriRequest類同時承擔(dān)了Response的功能,跳轉(zhuǎn)請求的結(jié)果,也會被保存到Fields中。Fields可以根據(jù)需要自定義,其中一些常見字段舉例如下:

  • Intent的Extra參數(shù),Bundle類型

  • 用于startActivityForResult的RequestCode,int類型

  • 用于overridePendingTransition方法的頁面切換動畫資源,int[]類型

  • 本次跳轉(zhuǎn)結(jié)果的監(jiān)聽器,OnCompleteListener類型

每次URI跳轉(zhuǎn)請求會有一個ResultCode(類似HTTP請求的ResponseCode),表示跳轉(zhuǎn)結(jié)果,也存放在Fields中。常見Code如下,用戶也可以自定義Code:

  • 200:跳轉(zhuǎn)成功

  • 301:重定向到其他URI,會再次跳轉(zhuǎn)

  • 400:請求錯誤,通常是Context或URI為空

  • 403:禁止跳轉(zhuǎn),例如跳轉(zhuǎn)白名單以外的HTTP鏈接、Activity的exported為false等

  • 404:找不到目標(biāo)(Activity或UriHandler)

  • 500:發(fā)生錯誤

總結(jié)來說,UriRequest用于實現(xiàn)一次URI跳轉(zhuǎn)中所有組件之間的通信功能。

UriHandler

UriHandler用于處理URI跳轉(zhuǎn)請求,可以嵌套從而逐層分發(fā)和處理請求。UriHandler是異步結(jié)構(gòu),接收到UriRequest后處理(例如跳轉(zhuǎn)Activity等),如果處理完成,則調(diào)用callback.onComplete()并傳入ResultCode;如果沒有處理,則調(diào)用callback.onNext()繼續(xù)分發(fā)。下面的示例代碼展示了一個只處理HTTP鏈接的UriHandler的實現(xiàn):

public interface UriCallback {

    /**
     * 處理完成,繼續(xù)后續(xù)流程。
     */

    void onNext();

    /**
     * 處理完成,終止分發(fā)流程。
     *
     * @param resultCode 結(jié)果
     */

    void onComplete(int resultCode);
}

public class DemoUriHandler extends UriHandler {
    public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
        Uri uri = request.getUri();
        // 處理HTTP鏈接
        if ('http'.equalsIgnoreCase(uri.getScheme())) {
            try {
                // 調(diào)用系統(tǒng)瀏覽器
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.setData(uri);
                request.getContext().startActivity(intent);
                // 跳轉(zhuǎn)成功
                callback.onComplete(UriResult.CODE_SUCCESS);
            } catch (Exception e) {
                // 跳轉(zhuǎn)失敗
                callback.onComplete(UriResult.CODE_ERROR);
            }
        } else {
            // 非HTTP鏈接不處理,繼續(xù)分發(fā)
            callback.onNext();
        }
    }
    // ...
}


UriInterceptor

UriInterceptor為攔截器,不做最終的URI跳轉(zhuǎn)操作,但可以在最終的跳轉(zhuǎn)前進行各種同步/異步操作,常見操作舉例如下:

  • URI跳轉(zhuǎn)攔截,禁止特定的URI跳轉(zhuǎn),直接返回403(例如禁止跳轉(zhuǎn)非meituan域名的HTTP鏈接

  • URI參數(shù)修改(例如在HTTP鏈接末尾添加query參數(shù)

  • 各種中間處理(例如打開登錄頁登錄、獲取定位、發(fā)網(wǎng)絡(luò)請求

  • ……

每個UriHandler都可以添加若干UriInterceptor。在UriHandler基類中,handle()方法先調(diào)用抽象方法shouldHandle()判斷是否要處理UriRequest,如果需要處理,則逐個執(zhí)行Interceptor,最后再調(diào)用handleInternal()方法進行跳轉(zhuǎn)操作。

public abstract class UriHandler {

    // ChainedInterceptor將多個UriInterceptor合并成一個
    protected ChainedInterceptor mInterceptor;

    public UriHandler addInterceptor(@NonNull UriInterceptor interceptor) {
        if (interceptor != null) {
            if (mInterceptor == null) {
                mInterceptor = new ChainedInterceptor();
            }
            mInterceptor.addInterceptor(interceptor);
        }
        return this;
    }

    public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
        if (shouldHandle(request)) {
            if (mInterceptor != null) {
                mInterceptor.intercept(request, new UriCallback() {
                    @Override
                    public void onNext() {
                        handleInternal(request, callback);
                    }

                    @Override
                    public void onComplete(int result) {
                        callback.onComplete(result);
                    }
                });
            } else {
                handleInternal(request, callback);
            }
        } else {
            callback.onNext();
        }
    }

    /**
     * 是否要處理給定的uri。在Interceptor之前調(diào)用。
     */

    protected abstract boolean shouldHandle(@NonNull UriRequest request);

    /**
     * 處理uri。在Interceptor之后調(diào)用。
     */

    protected abstract void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback);
}


URI的分發(fā)與降級

在外賣App中的URI分發(fā)示意如下圖。所有URI跳轉(zhuǎn)都會分發(fā)到RootUriHandler,然后根據(jù)不同的scheme分發(fā)到不同的子Handler。例如waimai協(xié)議分發(fā)到WmUriHandler,然后進一步根據(jù)不同的path分發(fā)到子Handler,從而啟動相應(yīng)的Activity;HTTP/HTTPS協(xié)議分發(fā)到HttpHandler,啟動WebView容器;對于其他類型URI(tel、mailto等),前面的幾個Handler都無法處理,則會分發(fā)到StartUriHandler,嘗試使用Android原生的隱式跳轉(zhuǎn)啟動系統(tǒng)應(yīng)用。

每個UriHandler都可以根據(jù)實際需要實現(xiàn)降級策略,也可以不作處理繼續(xù)分發(fā)給其他UriHandler。RootUriHandler中提供了一個全局的分發(fā)完成事件監(jiān)聽器,當(dāng)UriHandler處理失敗返回異常ResultCode或所有子UriHandler都沒有處理時,執(zhí)行全局降級策略。

平臺化與兩端復(fù)用

隨著外賣App業(yè)務(wù)的演進,團隊成員擴充了數(shù)倍,商超生鮮等垂直品類的拆分,以及異地研發(fā)團隊的建立,客戶端的平臺化被提上日程。關(guān)于外賣平臺化更詳細的內(nèi)容,可參考團隊之前已經(jīng)發(fā)布的文章 美團外賣Android平臺化架構(gòu)演進實踐

為了滿足實際開發(fā)需要,在長時間的探索后,逐步形成了如圖所示的三層工程結(jié)構(gòu)。

原有的單個工程拆分成多個工程,就不可避免的涉及到多工程之間的耦合問題,主要包括通信問題、復(fù)用問題、依賴注入、編譯問題,下面詳細介紹。

通信問題

當(dāng)原先的一個工程拆分到各個業(yè)務(wù)庫后,業(yè)務(wù)庫之間的頁面需要進行通信,最主要的場景就是頁面跳轉(zhuǎn)并通過Intent傳遞參數(shù)。

原先的頁面跳轉(zhuǎn)使用顯式跳轉(zhuǎn),Activity之間存在強引用,當(dāng)Activity被拆分到不同的業(yè)務(wù)庫,業(yè)務(wù)庫不能直接互相依賴,因此需要進行解耦。

利用WMRouter的URI分發(fā)機制,剛好可以很容易的解決這個問題。將將所有業(yè)務(wù)庫的Activity注冊到WMRouter,各個業(yè)務(wù)庫之間就可以進行頁面跳轉(zhuǎn)了。

此時WMRouter已經(jīng)承載了兩項功能:

  1. 后臺下發(fā)的運營URI跳轉(zhuǎn) (waimai://*)

  2. 內(nèi)部頁面跳轉(zhuǎn),用于代替原有的顯式跳轉(zhuǎn),實現(xiàn)工程解耦 (wm_router://page/*)

由于后臺下發(fā)的URI是和產(chǎn)品、運營、H5、iOS等各端統(tǒng)一制定的協(xié)議,支持的頁面、格式、參數(shù)等都不能隨意改動,而內(nèi)部頁面跳轉(zhuǎn)使用的URI,則需要根據(jù)實際開發(fā)需要進行配置,兩套URI協(xié)議不能兼容,因此使用了不同的scheme。

復(fù)用問題與ServiceLoader模塊

業(yè)務(wù)庫之間經(jīng)常需要復(fù)用代碼。一些通用代碼邏輯可以下沉到平臺層從而復(fù)用,例如業(yè)務(wù)無關(guān)的通用View組件;而有些代碼不適合下沉到平臺層,例如業(yè)務(wù)庫A要使用業(yè)務(wù)庫B中的某個界面模塊,而這個界面模塊和業(yè)務(wù)庫B的耦合很緊密。具體到外賣實際業(yè)務(wù)場景中,商家頁在商家休息時會展示推薦商家列表,其樣式和首頁相同(如圖),而兩個頁面不在一個工程中,商家頁希望能直接從首頁業(yè)務(wù)庫中獲取商家列表的實現(xiàn)。

為了解決上述問題,我們調(diào)研了解到Java中SPI (Service Provider Interfaces的設(shè)計思想與java.util.ServiceLoader工具類,還學(xué)習(xí)到美團平臺為了解決類似問題而開發(fā)的ServiceLoader組件。

考慮到實際需求差異,我們重新開發(fā)了自己的ServiceLoader實現(xiàn)。相比Java中的實現(xiàn),WMRouter的實現(xiàn)借鑒了美團平臺的設(shè)計思路,不僅支持通過接口獲取所有實現(xiàn)類,還支持通過接口和唯一的Key獲取特定的實現(xiàn)類。另外WMRouter的實現(xiàn)還支持直接加載實現(xiàn)類的Class、用Factory和Provider創(chuàng)建對象、單例管理、方法調(diào)用等功能。在Gradle插件的實現(xiàn)思路上,借鑒了美團平臺的ServiceLoader并做了性能優(yōu)化,給平臺提出了改進建議。

業(yè)務(wù)庫之間代碼復(fù)用的需求示意如圖,業(yè)務(wù)庫A需要復(fù)用業(yè)務(wù)庫B中的ServiceImpl但又不能直接引用,因此通過WMRouter加載:

  1. 抽取接口(或父類,后面不再重復(fù)說明)下沉到平臺層,實現(xiàn)類ServiceImpl實現(xiàn)該接口,并聲明一個Key(字符串類型)。

  2. 調(diào)用方通過接口和Key,由ServiceLoader加載實現(xiàn)類,通過接口訪問實現(xiàn)類。

URI跳轉(zhuǎn)和ServiceLoader看起來似乎沒有關(guān)聯(lián),但通信和復(fù)用需求的本質(zhì)都可以理解成路由,頁面通過URI分發(fā)跳轉(zhuǎn)時的協(xié)議是Activity URI,在這里ServiceLoader的協(xié)議是Interface Key。

依賴注入

為了兼容外賣獨立App和美團App外賣頻道的兩端差異,平臺層的一些接口要在兩個主工程分別實現(xiàn),并注入到底層。常規(guī)Java代碼注入的方式寫起來很繁瑣,而使用WMRouter的ServiceLoader功能可以更簡單的實現(xiàn)和依賴注入類似的效果。

對于WMRouter來說,所有依賴它的工程(包括主工程、業(yè)務(wù)庫、平臺庫)都是一樣的,任何一個庫想要調(diào)用其他庫中的代碼,都可以通過WMRouter路由轉(zhuǎn)發(fā)。前面的通信和復(fù)用問題,是同級的業(yè)務(wù)庫之間通過WMRouter調(diào)用,而依賴注入則是底層庫通過WMRouter調(diào)用上層庫,其本質(zhì)和實現(xiàn)都是相同的。

ServiceLoader模塊在加載實現(xiàn)類時提供了單例管理功能,可用于管理一些全局的Service/Manager,例如用戶賬戶管理類UserManager。

編譯問題

由于歷史原因,主工程作為一個沒有業(yè)務(wù)邏輯的殼工程,對業(yè)務(wù)庫卻有較多依賴,特別是對業(yè)務(wù)庫的初始化配置繁瑣,和各業(yè)務(wù)庫耦合緊密。另一方面,WMRouter跳轉(zhuǎn)的頁面、加載的實現(xiàn)類,需要在Application初始化時注冊到WMRouter中,也會增加主工程和業(yè)務(wù)庫的耦合。開發(fā)過程中各業(yè)務(wù)庫頻繁更新,經(jīng)常出現(xiàn)無法編譯的問題,嚴(yán)重影響開發(fā)。

為了解決這個問題,一方面WMRouter增加了注解支持,在Activity類、ServiceLoader實現(xiàn)類上配置注解,就可以在編譯期間自動生成代碼注冊到WMRouter中。

// 沒有注解時,需要在Application初始化時代碼注冊各個頁面,耦合嚴(yán)重
register('/home', HomeActivity.class);
register('/order', OrderListActivity.class);
register('/shop', ShopActivity.class)
register('/account', MyAccountActivity.class);
register('/address', MyAddressActivity.class);
// ...
// 增加注解后,只需要在各個Activity上通過注解配置即可
@RouterUri(path = '/shop')
public class ShopActivity extends BaseActivity {

}

另一方面,ServiceLoader還支持指定接口加載所有實現(xiàn)類,主工程可以通過統(tǒng)一接口,加載各個業(yè)務(wù)庫中所有實現(xiàn)類并進行初始化,最終實現(xiàn)和業(yè)務(wù)庫的徹底隔離。

開發(fā)過程中,各個業(yè)務(wù)庫可以像插件一樣按需集成到主工程,能大幅減少不能編譯的問題,同時由于編譯時可以跳過不需要的業(yè)務(wù)庫,編譯速度也能得到提高。

WMRouter的推廣

在WMRouter解決了外賣App的各種問題后,發(fā)現(xiàn)公司內(nèi)甚至公司外的其他App也遇到了相似的問題和需求,于是決定對WMRouter進行推廣和開源。

由于WMRouter是一個開放式組件化框架,UriRequest可以存放任意數(shù)據(jù),UriHandler、UriInterceptor可以完全自定義,不同的UriHandler可以任意組合,具有很大的靈活性。但過于靈活容易導(dǎo)致易用性的下降,即使對于最常規(guī)最簡單的應(yīng)用,也需要復(fù)雜的配置才能完成功能。

為了在靈活性與易用性之間平衡,一方面,WMRouter對包結(jié)構(gòu)進行了合理的劃分,核心接口和實現(xiàn)類提供基礎(chǔ)通用能力,盡可能保留最大的靈活性;另一方面,封裝了一系列通用實現(xiàn)類,并組合成一套默認(rèn)實現(xiàn),從而滿足絕大多數(shù)常規(guī)使用場景,盡可能降低其他App的接入成本,方便推廣。

總結(jié)

目前業(yè)界已有的一些Android路由框架,不能滿足外賣App在開發(fā)過程中的實際需要,因此我們開發(fā)了WMRouter路由框架。借鑒網(wǎng)絡(luò)請求的思想,設(shè)計了基于UriRequest、UriHandler、UriInterceptor的URI分發(fā)機制,在保證功能靈活強大的同時,又盡可能的降低了使用難度;另一方面,借鑒SPI的設(shè)計思想、Java和美團平臺的ServiceLoader實現(xiàn),開發(fā)了自己的ServiceLoader模塊,解決外賣平臺化過程中的四個問題(通信問題、復(fù)用問題、依賴注入、編譯問題)。在經(jīng)過了近一年的不斷迭代完善后,WMRouter已經(jīng)成為美團多個App中的核心基礎(chǔ)組件之一。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美人妻免费一区二区三区 | 中文字幕无线码一区欧美| 国产成人一区二区三区久久| 日本免费一本一二区三区| 视频在线播放你懂的一区| 一区二区三区日韩中文| 国产精品夜色一区二区三区不卡| 久久99精品日韩人妻| 一区二区三区国产日韩| 在线中文字幕亚洲欧美一区| 国产高清视频一区不卡| 欧美日韩国产精品黄片| 午夜精品在线视频一区| 国产成人综合亚洲欧美日韩| 国产中文字幕一区二区| 国产欧美一区二区色综合| 成在线人免费视频一区二区| 自拍偷拍一区二区三区| 99视频精品免费视频播放| 91免费一区二区三区| 好吊妞视频只有这里有精品| 丰满少妇被猛烈撞击在线视频| 国产精品视频久久一区| 麻豆在线观看一区二区| 精品偷拍一区二区三区| 国产精品亚洲一级av第二区| 亚洲午夜福利不卡片在线| 日韩免费国产91在线| 日本熟妇五十一区二区三区| 国产欧美日韩综合精品二区| 精品人妻av区波多野结依| 国产毛片av一区二区三区小说| 亚洲日本韩国一区二区三区| 亚洲国产成人av毛片国产| 亚洲一区二区三区熟女少妇| 黄片在线免费看日韩欧美| 国产又大又猛又粗又长又爽| 国产中文字幕一区二区| 国产色一区二区三区精品视频| 午夜免费精品视频在线看| 日本东京热视频一区二区三区|