請參考ARouter原始碼淺析
服務管理
依賴注入
通過依賴注入的方式我們可以像上一篇文章中獲取跳轉引數一樣獲取服務,具體的實現我們還是通過原始碼來看一下吧。 當我們隊一個自定義的service標註上@Autowired註解的時候,重新編譯以後,APT會為我們自動生成如下程式碼:
至於inject方法在哪被呼叫上一篇文章已經分析過了,這裡可以看到服務的注入跟引數的注入還是有區別的,具體的實現是外掛幫我們做了,我節選了一點程式碼添上如下: 注意我圈出來的程式碼,這裡區分了服務和普通型別分別生成不同的程式碼。而服務內部注入的例項還是通過普通方式獲取的,也就是我下面要分析的第二種獲取服務的方式。普通方式
普通方式也分為2種,一種是根據name,另一種就是根據type。 下面只分析第一種根據name來獲取服務的方式,因為根據type來獲取例項是差不多的。 ###_ARouter#_navigationprivate Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
……
break;
case PROVIDER:
//注意這行程式碼
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
複製程式碼
最後會根據postcard.getType()型別來匹配PROVIDER,然後返回provider例項,而provider的set是在LogisticsCenter#completion中:
好了,例項獲取到了其他的也就沒什麼好分析的了。 這裡提醒一下,根據type來獲取例項有侷限性,只支援全域性只有一個實現了相應介面的實現類,否則獲取到的例項是系統掃描到的實現最後一個例項,同上一篇文章的PathReplaceService。WHY:那為什麼根據type和根據那麼來獲取例項會有區別呢?通過原始碼我們發現type是從providersIndex Map中去獲取例項的,而name是從providers Map中去獲取例項的。
相信到這大家應該已經明白了吧。
攔截器
我們在實現攔截器的時候需要實現IInterceptor,而IInterceptor也實現了IProvider,所以IInterceptor也是一個服務。所以interceptor的例項獲取方式也是跟普通的service是一樣的,這裡不做分析。
對應上文的普通服務。 下面讓我們來找一下具體得到攔截邏輯是在哪實現的呢?請看下面的程式碼:InterceptorServiceImpl#_excute
InterceptorServiceImpl屬於arouter核心服務類
InterceptorServiceImpl#doInterceptions
_ARouter#navigation
到這又回到我們上一篇文章頁面跳轉主流程的邏輯,他其實就是在真正跳轉之前,先判斷有沒有符合的攔截器,有的話則先進行攔截器裡的操作。這裡要注意一點的是:攔截器是有優先順序的,按照priority排序,priority越小優先順序越高。這是因為 interceptorsIndex的型別是TreeMap,他具有以下兩條特性:到此,攔截器的實現原理我們也就分析清楚了。1、TreeMap如不指定排序器,預設將按照key值進行升序排序,如果指定了排序器,則按照指定的排序器進行排序。 2、具體的排序規則,開發人員可以在int compare()方法中進行指定。
降級策略
我們知道使用系統自帶的StartActivity()啟動後就無法插手其中任何環節了,只能交給系統管理,這就導致了在跳轉失敗的情況下無法降級,而是會直接丟擲運營級的異常,甚至導致崩潰,這個給使用者的感覺就不是很好。所以ARouter就為我們提供了降級策略,主要分為2中方式,單獨降級和全域性降級(demo中這樣稱呼的)。
單獨降級(介面回撥)&全域性降級
如下程式碼所示:
主要回撥的就是onLost,那它又是在什麼時候才被呼叫的呢?我們看如下程式碼
它在completion中主動丟擲異常被捕獲時呼叫的,如果callback不為null就會被呼叫,由此也可以看出單獨降級的優先順序是高於全域性降級的,else中的程式碼就是全域性降級的程式碼。使用方式也跟普通的服務是一樣的這裡就不分析了。 我們再看看LogisticsCenter.completion這個方法,這個是與編譯期間生成的對映檔案直接打交道的模組。先在載入到記憶體的節點倉庫中查詢是否有該目標節點的存在,沒有就到組倉庫中找,找不到就報錯NoRouteFoundException,這就到了我們上面的邏輯中。LogisticsCenter#completion
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
}
}
}
複製程式碼
#總結 經過兩篇文章,我們基本上已經把ARouter的主要功能都分析了。其實看著很神奇的功能,只要我們深入到原始碼裡就會發現其實也就那麼回事。不過我們還是能從優秀得到框架中學到很多優秀的設計思想和前沿的技術的,比如IOC、APT, AOP等。