本來這期應(yīng)該分享IoC思想和ARouter的自動注入這塊內(nèi)容便锨,但是在自動注入這塊涉及到服務(wù)的主動注入劝堪,而我們前面只說到Activity的發(fā)現(xiàn)晚岭,所以還是決定先做個服務(wù)和Fragment實例發(fā)現(xiàn)的分享嗤无。這也是ARouter的分享系列的第四篇壶愤,前面三篇分別是:
ARouter解析一:基本使用及頁面注冊源碼解析
ARouter解析二:頁面跳轉(zhuǎn)源碼分析
ARouter解析三:URL跳轉(zhuǎn)本地頁面源碼分析
服務(wù)和Fragment的發(fā)現(xiàn)和Activity的發(fā)現(xiàn)會有很多重疊的地方麦轰,我們不會再重復(fù)說乔夯,建議小伙伴們在開始之前先看下解析系列的第二篇,再來看這篇會輕松很多款侵。今天我們這次分享分成三部分末荐。
1.服務(wù)的發(fā)現(xiàn)
2.Fragment的發(fā)現(xiàn)
3.服務(wù)發(fā)現(xiàn)的源碼分析
4.Fragment實例獲取的源碼分析
Demo還是慣例使用官方的,我們看下效果圖新锈,點擊BYNAME調(diào)用服務(wù)或者BYTYPE調(diào)用服務(wù)
點擊獲取FRAGMENT實例可以獲取BlackFragment實例鞠评。
這期涉及到的內(nèi)容Demo沒有什么可觀賞的,內(nèi)容比較有意思壕鹉。好了剃幌,開始進入正題~~~
1.發(fā)現(xiàn)服務(wù)
這里說到的服務(wù)不是Android四大組件中的Service,這里的服務(wù)是服務(wù)端開發(fā)的概念晾浴,就是將一部分功能和組件封裝起來成為接口负乡,以接口的形式對外提供能力,所以在這部分就可以將每個功能作為一個服務(wù)脊凰,而服務(wù)的實現(xiàn)就是具體的業(yè)務(wù)功能
ARouter發(fā)現(xiàn)服務(wù)主要有兩種方式抖棘,ByName和ByType。先看看這兩種方式分別是怎么使用狸涌。ByName就是需要傳遞path路徑來進行發(fā)現(xiàn)切省,ByType就是通過服務(wù)class來進行查找。
case R.id.navByName:
((HelloService) ARouter.getInstance().build("/service/hello").navigation()).sayHello("mike");
break;
case R.id.navByType:
ARouter.getInstance().navigation(HelloService.class).sayHello("mike");
break;
那么為什么需要區(qū)分兩種類型帕胆?因為在Java中接口是可以有多個實現(xiàn)的朝捆,通過ByType的方式可能難以拿到想要的多種實現(xiàn),這時候就可以通過ByName的方式獲取真實想要的服務(wù)懒豹。所以其實大多數(shù)情況是通過ByType的芙盘,如果有多實現(xiàn)的時候就需要使用ByName驯用。
服務(wù)發(fā)現(xiàn)的使用就是以上。
2.發(fā)現(xiàn)Fragment
Fragment獲取實例的使用也是很簡單儒老,一行代碼搞定蝴乔,和跳轉(zhuǎn)的寫法很基本就是一樣的。
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
Fragment發(fā)現(xiàn)的使用就是以上驮樊。
3.發(fā)現(xiàn)服務(wù)的源碼分析
接下來我們分析下發(fā)現(xiàn)服務(wù)的邏輯過程薇正,小伙伴們有沒有疑問,在上面服務(wù)的使用時囚衔,HelloService.class是一個接口挖腰,怎么去獲取到他的實現(xiàn)類的?
public interface HelloService extends IProvider {
void sayHello(String name);
}
ByName的發(fā)現(xiàn)比較簡單佳魔,很Activity的跳轉(zhuǎn)很像曙聂。我們先來看看ByType的發(fā)現(xiàn)過程晦炊。
ARouter.getInstance().navigation(HelloService.class)
先跟到_ARouter的navigation(Class<? extends T> service)
中鞠鲜。
protected <T> T navigation(Class<? extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
// Compatible 1.0.5 compiler sdk.
if (null == postcard) { // No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
上面代碼主要就是兩步,第一構(gòu)造postcard断国,第二LogisticsCenter跳轉(zhuǎn)
1.構(gòu)造postcard
首先還是熟悉的先構(gòu)造postcard贤姆,只不過這里時直接在navigation
中進行構(gòu)造,之前activity或者url跳轉(zhuǎn)都是通過build構(gòu)造稳衬,意思都差不多霞捡。我們進去LogisticsCenter.buildProvider
看看”【危看到LogisticsCenter
就可以猜到這里邏輯是要和APT技術(shù)生成的Java類打交道碧信,可以參考ARouter解析一:基本使用及頁面注冊源碼解析。
public static Postcard buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);
if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}
我們在源碼中打個斷點看看meta
是什么東東街夭∨椴辏可以看到meta
中有個destination
屬性可以拿到具體的實現(xiàn)類HelloServiceImpl
。
上面的meta
是直接從倉庫Warehouse
中獲取服務(wù)板丽,那么倉庫的providersIndex
是從哪來的呈枉?其實就是在獲取ARouter實例的時候加載進來的,在LogisticsCenter .init()
埃碱,這里為了方便分析猖辫,對代碼做了手腳。
for (String className : classFileNames) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
Java文件名就是ARouter$$Providers$$app
砚殿,這個類就是在編譯器使用APT技術(shù)自動生成的啃憎。有木有很激動?似炎?荧飞?其實就是一個map凡人,其中就有我們上面使用到的HelloService
,對應(yīng)的注冊類就是HelloServiceImpl .class
叹阔。
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
}
}
所以我們在倉庫中就可以根據(jù)Name-HelloService
發(fā)現(xiàn)服務(wù)的具體實現(xiàn)挠轴。之后就很好理解了,可以從meta
中拿到path-/service/hello
,group-service
構(gòu)造postcard耳幢。
2.LogisticsCenter跳轉(zhuǎn)
拿到postcard之后就是跳轉(zhuǎn)到目標(biāo)服務(wù)了岸晦,這個和activity跳轉(zhuǎn)是一樣的。我們到老朋友LogisticsCenter.completion(postcard)
中看下睛藻。
public synchronized static void completion(Postcard 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() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // Reload
}
}
這里還是從倉庫中去找服務(wù)启上,一開始肯定是沒有因為還沒有加載。
我們之前分享提到過ARouter是分組管理的店印,按需加載冈在。所以這里到節(jié)點的map中找到service的分組,然后加載這個分組按摘。
加載service分組后包券,就可以拿到我們需要的HelloService
了。
public class ARouter$$Group$$service implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
atlas.put("/service/json", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
atlas.put("/service/single", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
}
}
看到這里小伙伴們有沒有感到疑惑炫贤?其實也算是框架的一個需要改進的地方溅固。前面在provider中已經(jīng)拿到HelloService
具體實現(xiàn)類的路徑,這里又加載service
分組,然后再找到具體實現(xiàn)類兰珍,做了反復(fù)沒必要的工作了侍郭。不過這并不影響框架的牛逼性哈。
再接下來其實就是通過反射構(gòu)造HelloService
實例掠河。
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must be implememt IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
}
將服務(wù)實例設(shè)置給postcard亮元,之后可以在navigation中通過postcard的方法getProvider()
得到。
發(fā)現(xiàn)服務(wù)的源碼就是以上,和activity的發(fā)現(xiàn)差別就是服務(wù)需要通過反射構(gòu)造實例返回唠摹。
4.發(fā)現(xiàn)Fragment的源碼分析
接著看下發(fā)現(xiàn)Fragment的源碼,使用上和activity是一樣的爆捞。build
也是構(gòu)造postcard實例。
ARouter.getInstance().build("/test/fragment")
我們看下build的操作,也activity也是一致的跃闹,代碼比較簡單嵌削,這里就不多做解釋。
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
有了postcard接著就是到LogisticsCenter.completion(postcard)
中進行具體的跳轉(zhuǎn)了望艺。首先還是找到映射關(guān)系的類苛秕,可以看到倒數(shù)第二個就是我們需要的fragment,接著就是加載這個節(jié)點找默,將信息補充到postcard中艇劫。
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test",
new java.util.HashMap<String, Integer>(){{put("pac", 9); put("obj", 10); put("name", 8); put("boy", 0);
put("age", 3); put("url", 8); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2",
"test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", null, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
有了完整信息的postcard就可以拿到fragment了,跳轉(zhuǎn)邏輯在_ARouter的_navigation中,也是通過反射拿到Fragment的實例惩激,注意Fragment實例中需要有默認的構(gòu)造函數(shù)店煞。通過setArguments給fragment參數(shù)蟹演。
private 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 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;
}
從上面也可以看出,目前ARouter還沒實現(xiàn)另外三個組件 broadcast
,content provider
,service
的路由功能顷蟀,期待后續(xù)更新哈酒请。
發(fā)現(xiàn)Fragment的源碼就是以上。
5.總結(jié)
在activity跳轉(zhuǎn)的基礎(chǔ)上我們今天分享了service(這里不是指四大組件的Service鸣个,和后端開發(fā)的接口有點類似)羞反,fragment路由的使用和源碼分析,大部分邏輯是類似的囤萤,主要區(qū)別就是這里需要通過反射拿到實例昼窗,activity則是拿到路徑后進行跳轉(zhuǎn)。再有就是前面加載service時會有反復(fù)加載的過程涛舍,這應(yīng)該是沒有必要的澄惊。
今天的發(fā)現(xiàn)服務(wù)和Fragment車就開到這,小伙伴們可以下車嘍富雅,不要忘記點個贊哦掸驱!
謝謝!
歡迎關(guān)注公眾號:JueCode