ARouter系列三:依賴注入暴露服務(wù)

如果對于 ARouter 不是很了解的,建議先行閱讀:
ARouter系列一:Activity跳轉(zhuǎn)原理詳解
ARouter系列二:@Autowired屬性注入

想要提供服務(wù)暴露給其他人使用,ARouter提供了一個(gè)接口:IProvider

// Provider interface, base of other interface.
public interface IProvider {
    /**
     * Do your init work in this method, it well be call when processor has been load.
     *
     * @param context ctx
     */
    void init(Context context);
}

IProvider 只有一個(gè)方法 init, 參數(shù)為 Context。剛看到這接口的時(shí)候不是很明白,等把本篇文章看完之后就會明白,參數(shù)Context不可或缺。注意IProvider的注釋:IProvider是其它 Interface 的基礎(chǔ)??梢夾Router推薦的用法是 一個(gè)Interface 繼承 IProvider。

這里大家可能會有疑問,為什么需要Context參數(shù),為什么推薦是一個(gè)接口 繼承 IProvider,而不是實(shí)體類。帶著這些疑問,我們繼續(xù)探尋

一、使用

新建一個(gè)項(xiàng)目,有兩個(gè)moudle:applocation。location中封裝了地圖相關(guān)服務(wù),例如:開始定位,獲取位置等等。 app 依賴 location

1.1 location模塊

LocationService 定義此模塊提供的基礎(chǔ)服務(wù):開始定位 和 獲取地理位置。并且實(shí)現(xiàn)了 上述的 IProvider

package com.daddyno1.location;
public interface LocationService extends IProvider {
    //開始定位
    void startLocate();
    //獲取經(jīng)緯度信息
    Point getLocation();
}

BaiduLocationService 是提供服務(wù)的最終實(shí)現(xiàn)類。此處模擬使用了百度來實(shí)現(xiàn)具體功能。并且使用了 @Route(path = "/Loc/LocationService") 來標(biāo)注真實(shí) Location 服務(wù)實(shí)現(xiàn)類。

@Route(path = "/Loc/LocationService")
public class BaiduLocationService implements LocationService {

    private static final String TAG = "BaiduLocationService";

    @Override
    public void startLocate() {
        Log.i(TAG, "BaiduLocationService - startLocate");
    }

    @Override
    public Point getLocation() {
        return new Point(11,12);
    }

    @Override
    public void init(Context context) {
        Log.i(TAG, "BaiduLocationService - init");
    }
}

編譯一下,我們看一下 apt 幫我們生成了哪些 輔助類
ARouter$$Root$$location、ARouter$$Group$$LocARouter$$Providers$$locationARouter系列一:Activity跳轉(zhuǎn)原理詳解 的不難理解,ARouter$$Root$$location存儲了location module 下所有路由組信息;ARouter$$Group$$Loc 存儲了 location module 下 路由組名為 Loc 的路由表信息。

package com.alibaba.android.arouter.routes;
...
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$location implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("Loc", ARouter$$Group$$Loc.class);
  }
}
package com.alibaba.android.arouter.routes;
...
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$Loc implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/Loc/LocationService", RouteMeta.build(RouteType.PROVIDER, BaiduLocationService.class, "/loc/locationservice", "loc", null, -1, -2147483648));
  }
}

ARouter$$Providers$$location 這個(gè)類長什么樣?我們來看一下:

package com.alibaba.android.arouter.routes;
...
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Providers$$location implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.daddyno1.location.LocationService", RouteMeta.build(RouteType.PROVIDER, BaiduLocationService.class, "/Loc/LocationService", "Loc", null, -1, -2147483648));
  }
}

ARouter$$Providers$$location 這個(gè)類有什么用呢,我們先往后看,一會再說。

1.2 app模塊

這里新建一個(gè) Activity 使用我們創(chuàng)建好的 Location 服務(wù)

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        // 使用一:by name
        LocationService locationService = (LocationService) ARouter.getInstance().build("/Loc/LocationService").navigation();
        locationService.startLocate();

        //使用二: by type
        LocationService locationService1 = ARouter.getInstance().navigation(LocationService.class);
        locationService1.startLocate();
    }
}

我們運(yùn)行一下,看一下結(jié)果:

I: BaiduLocationService - init
I: BaiduLocationService - startLocate
I: BaiduLocationService - startLocate

注意: init 執(zhí)行了一次, startLocate 調(diào)用了兩次。

二、源碼分析

2.1 init

之前在分析Activity跳轉(zhuǎn)的時(shí)候分析了ARouter的初始化,這里不再細(xì)講。

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
 ...
 for (String className : routerMap) {
     if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
     //com.alibaba.android.arouter.routes.ARouter$$Root
    // This one of root elements, load root.
     ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
     //com.alibaba.android.arouter.routes.ARouter$$Interceptors
     // Load interceptorMeta
      ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
     //com.alibaba.android.arouter.routes.ARouter$$Providers
     // Load providerIndex
    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
     }
  }
  ...
}

唯一需要提一下的是,經(jīng)過路由器初始化之后,Warehouse的數(shù)據(jù)如下:

ARouter$$Providers$$location 這個(gè)是之前提到的apt生成的輔助類,它的作用就是幫助路由在初始化的時(shí)候,把相關(guān)數(shù)據(jù)注入到 Warehouse.providersIndex,這個(gè)有什么用呢,其實(shí)對于by name的使用方式來說,Warehouse.providersIndexARouter$$Providers$$location 根本沒用上,只是 by type 時(shí)候的輔助類。

2.2 by name
LocationService locationService = (LocationService) ARouter.getInstance().build("/Loc/LocationService").navigation();
2.3 by type
LocationService locationService1 = ARouter.getInstance().navigation(LocationService.class);
2.4 小結(jié)

具體的實(shí)現(xiàn)邏輯和 Activity 路由過程很相似,自己去看代碼吧。唯一的區(qū)別是,如果RouteType是 PROVIDER 的話,直接根據(jù)反射創(chuàng)建一個(gè)路由Destination 類的實(shí)例,存到Postcard里,最后返回給用戶。

本例中ARouter會使用反射創(chuàng)建一個(gè)BaiduLocationService 的實(shí)體對象,回調(diào)init方法,返回給使用者,之后把 實(shí)體對象緩存到 Warehouse.providers(Map<Class, IProvider>),下次直接在使用的話,直接從緩存獲取,不會再反射創(chuàng)建了。這也是本例為什么 init 只執(zhí)行了一次。

三、總結(jié)

1、為什么IProvider要提供一個(gè)Context?
我們知道ARouter是幫助我們實(shí)現(xiàn)項(xiàng)目組件化的工具,很多情況下一個(gè)Moudle是拿不到應(yīng)用程序Context的,本例中的場景就是。但很多服務(wù)的實(shí)現(xiàn)又會依賴于Context,所以有必要把Context傳遞給各個(gè)Moudle。

2、為什么推薦 Interface 集成 IPriovider?
ARouter說 通過依賴注入暴露服務(wù)。本例中抽象了一個(gè) LocationService 接口,此接口抽象了所有用到的服務(wù),用戶通過ARouter可以拿到 LocationService 的具體實(shí)現(xiàn)類,然后強(qiáng)轉(zhuǎn)成 LocationService 使用,可以不用關(guān)心具體的實(shí)現(xiàn)類是誰。這就是通過依賴注入的方式 實(shí)現(xiàn) 控制翻轉(zhuǎn),從而實(shí)現(xiàn)使用者 和 具體實(shí)現(xiàn)解耦的目的。假如某一天又想時(shí)候用 高德SDK 實(shí)現(xiàn)具體的 Location 服務(wù),使用者的代碼不用修改,實(shí)現(xiàn)完全的解耦。使用 高德 實(shí)現(xiàn) Location 服務(wù):

package com.daddyno1.location;
...
@Route(path = "/Loc/LocationService")
public class GaodeLocationService implements LocationService {

    private static final String TAG = "GaodeLocationService";

    @Override
    public void startLocate() {
        Log.i(TAG, " GaodeLocationService - startLocate");
    }

    @Override
    public Point getLocation() {
        return new Point(1,2);
    }

    @Override
    public void init(Context context) {
        Log.i(TAG, " GaodeLocationService - init");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容