可能是最好理解的ARouter源碼分析

ARouter是什么

ARouter是阿里巴巴開源的Android平臺(tái)中對(duì)頁(yè)面、服務(wù)提供路由功能的中間件,提倡的是簡(jiǎn)單且夠用。
Github: https://github.com/alibaba/ARouter
介紹: https://yq.aliyun.com/articles/71687

簡(jiǎn)單使用示例

Activtiy跳轉(zhuǎn)

ARouter.getInstance().build("/test/activity2").withString("name", "老王").navigation();

獲取服務(wù)功能

ARouter.getInstance().navigation(HelloService.class).sayHello("mike");

((HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation()).sayHello("mike");

主要代碼結(jié)構(gòu)

  • arouter-api: 上層主要代碼,包括入口類ARouter,主要邏輯代碼類LogisticsCenter,相關(guān)輔助類ClassUtils等

  • arouter-annotation: ARouter中主要支持的annotation(包括Autowired, Route, Interceptor)的定義,以及RouteMeta等基礎(chǔ)model bean的定義

  • arouter-compiler: ARouter中annotation對(duì)應(yīng)的annotation processor代碼(注解處理器:讓這些注解代碼起作用,Arouter中主要是生成相關(guān)代碼,關(guān)于annotation processor,詳細(xì)了解可參考 Java注解處理器, 這篇譯文介紹的非常完整。

  • arouter-gradle-plugin: 一個(gè)gradle插件,目的是在arouter中插入相關(guān)注冊(cè)代碼(代替在Init時(shí)掃描dex文件獲取到所有route相關(guān)類)

  • app: Demo代碼,包括Activity跳轉(zhuǎn),面向接口服務(wù)使用,攔截器的使用等詳細(xì)使用示例

核心源碼分析

ARouter.getInstance().build("/test/activity2").withString("name", "老王").navigation();

從最常用的跳轉(zhuǎn)開始分析,基本可了解到ARouter的運(yùn)轉(zhuǎn)原理。這行完成跳轉(zhuǎn)的代碼最終效果是攜帶參數(shù)跳轉(zhuǎn)到對(duì)應(yīng)的Activity,在Android層面來(lái)說(shuō)最后一定是通過(guò)調(diào)用startActivity或是startActivityForResult來(lái)完成跳轉(zhuǎn)。

分為幾步來(lái)看:

  1. ARouter調(diào)用Build生成Postcard,過(guò)程是怎樣的
  2. Postcard是什么
  3. Postcard調(diào)用navigation是怎樣執(zhí)行到startActivity的

生成Postcard過(guò)程

打開ARouter類,發(fā)現(xiàn)基本都是調(diào)用_ARouter類的方法。

ARouter類非常簡(jiǎn)單,只是通過(guò)Facade pattern包裝了_ARouter類的相關(guān)方法,方便調(diào)用和閱讀。

_ARouter類真正實(shí)現(xiàn)了相關(guān)入口功能,包括初始化和銷毀等方法,另外主要包括生成Postcard以及通過(guò)Postcard完成navigation的代碼 (這里先介紹生成Postcard生成部分,_ARouter詳細(xì)介紹見下方)

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));
        }
    }

這過(guò)程主要完成了2個(gè)步驟:

  1. 查找是否存在PathReplaceService,如果有調(diào)用PathReplaceService的forString方法獲取新的path (后面介紹完Service功能會(huì)更好理解這部分)
  2. 從path中解析出group值,調(diào)用帶有g(shù)roup參數(shù)的build方法
    protected Postcard build(String path, String group) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = 
                ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return new Postcard(path, group);
        }
    }

在這個(gè)方法中就可以看到Postcard的構(gòu)造。

Postcard是什么

首先Postcard繼承RouteMeta,RouteMeta中存儲(chǔ)的是關(guān)于route的一些基礎(chǔ)信息,只定位于存儲(chǔ)route基礎(chǔ)信息。

public class RouteMeta {
    private RouteType type;         // Type of route
    private Element rawType;        // Raw type of route
    private Class<?> destination;   // Destination
    private String path;            // Path of route
    private String group;           // Group of route
    private int priority = -1;      // The smaller the number, the higher the priority
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  // Param type
    private String name;

    private Map<String, Autowired> injectConfig;  // Cache inject config.
  
    ...
    
}

Postcard類則還包括了"明信片"的"寄出"(navigation方法),"是否支持綠色通道"(isGreenChannel方法),以及支持"寄出效果"(withTransition方法)等具體的功能。

public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // Data to transform
    private int flags = -1;         // Flags of route
    private int timeout = 300;      // Navigation timeout, TimeUnit.Second
    private IProvider provider;     // It will be set value, if this postcard was provider.
    private boolean greenChannel;
    private SerializationService serializationService;

    // Animation
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim = -1;
    private int exitAnim = -1;
  
    ...
      
    public void navigation(Activity mContext, int requestCode, NavigationCallback callback){
        ARouter.getInstance().navigation(mContext, this, requestCode, callback);
    }
  
    ...
      
    public Postcard withString(@Nullable String key, @Nullable String value) {
        mBundle.putString(key, value);
        return this;
    }
  
    ...
      
    public Postcard withTransition(int enterAnim, int exitAnim) {
        this.enterAnim = enterAnim;
        this.exitAnim = exitAnim;
        return this;
    }
  
    ...
}

回到前面的介紹,在_ARouter的的build方法中通過(guò)path和group參數(shù)構(gòu)造出了一張可以"寄出"的"明信片"(new Postcard)。

Postcard.navigation到startActivity

接下來(lái)就是調(diào)用Postcard的navigation方法

public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
    ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}

可以看到其實(shí)是調(diào)用了ARouter的navigation, 內(nèi)部調(diào)用了_ARouter的navigation。接下來(lái)看看navigation的主要過(guò)程是怎樣的。

protected Object navigation(final Context context, final Postcard postcard, final int
    requestCode, final NavigationCallback callback) {
    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());

        if (debuggable()) {
            // Show friendly tips for user.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]",
                            Toast.LENGTH_LONG).show();
                }
            });
        }

        if (null != callback) {
            callback.onLost(postcard);
        } else {    // No callback for this invoke, then we use the global degrade service.
            DegradeService degradeService = 
                    ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }

    if (null != callback) {
        callback.onFound(postcard);
    }

    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor 
            // cost too mush time made ANR.
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            /**
             * Continue process
             *
             * @param postcard route meta
             */
            @Override
            public void onContinue(Postcard postcard) {
                _navigation(context, postcard, requestCode, callback);
            }

            /**
             * Interrupt process, pipeline will be destory when this method called.
             *
             * @param exception Reson of interrupt.
             */
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }

                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + 
                        exception.getMessage());
            }
        });
    } else {
        return _navigation(context, postcard, requestCode, callback);
    }

    return null;
}

這個(gè)方法有點(diǎn)長(zhǎng),但主要包括3步流程。

  1. 調(diào)用LogisticsCenter.completion

    主要是為Postcard找到對(duì)應(yīng)router,并且用router中信息填充Postcard對(duì)象。

    如果該方法拋出NoRouteFoundException,則調(diào)用對(duì)應(yīng)Callback的onLost,如果沒(méi)有配置Callback則嘗試獲取是否存在DegradeService,如果存在調(diào)用DegradeService的onLost方法。(DegradeService的實(shí)現(xiàn)可以理解為兜底方案,比如native頁(yè)面沒(méi)有找到,用相應(yīng)H5頁(yè)面替代展示)

    LogisticsCenter類的詳細(xì)介紹見下方。

  2. 調(diào)用Callback的onFound方法,然后判斷是否可以走“綠色通道”(即不支持?jǐn)r截)。

    不能走“綠色通道”的都需要經(jīng)過(guò)攔截器攔截(后面會(huì)介紹攔截器原理),攔截器會(huì)返回繼續(xù)和中斷2種結(jié)果,繼續(xù)則會(huì)繼續(xù)執(zhí)行調(diào)用navigation方法(相當(dāng)于在跳轉(zhuǎn)前做了點(diǎn)額外動(dòng)作);

    如果走了“綠色通道”的則直接調(diào)用_navigation方法。

  3. _navigation方法中則可以看到,如果 postcard.getType() 是activity則調(diào)用startActivity或startActivityForResult,且支持動(dòng)畫和啟動(dòng)flags的設(shè)置,接著調(diào)用callback.onArrival。
    如果種類是provider則提供提供provider對(duì)象,如果是Fragment則生成對(duì)象且賦值參數(shù)。

ARouter.getInstance().build("/test/activity2").withString("name", "老王").navigation();

至此,已梳理完上方代碼完成Activity跳轉(zhuǎn)的主要流程。

獲取接口對(duì)象

((HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation()).sayHello("mike");

大體流程和上面完成Activity跳轉(zhuǎn)流程代碼類似,主要區(qū)別在于,

  1. LogisticsCenter.completion中給Postcard填充type時(shí),對(duì)應(yīng)的是RouteType.PROVIDER,并且在Warehouse中找到對(duì)應(yīng)的Provider賦值給Postcard。
  2. 在ARouter的_navigation方法中,發(fā)現(xiàn)Postcard類型是RouteType.PROVIDER則直接返回對(duì)應(yīng)Provider。

ARouter中Annotation的使用

Annotation在ARouter中有著非常重要作用,Annotation基本原理這里不做詳談。
ARouter中主要的Annotation有3個(gè):

Autowired
Route
Interceptor

對(duì)應(yīng)的Annotation定義代碼位置如下圖所示,位于arouter-annotation module中。

annotation代碼位置.jpg

Autowired

Autowired主要完成界面跳轉(zhuǎn)過(guò)程中,Intent參數(shù)的自動(dòng)填充。

先看看Autowired是如何使用的,再看看Autowired是如何做到的。

如何使用

@Route(path = "/test/activity1", name = "測(cè)試用 Activity")
public class Test1Activity extends AppCompatActivity {

    @Autowired(desc = "姓名")
    String name = "jack";

    @Autowired
    int age = 10;

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

        ARouter.getInstance().inject(this);
      
        ...
          
        //inject方法被調(diào)用后,被Autowired注解的變量則已從Intent中取出對(duì)應(yīng)參數(shù)賦值
    }
}

如何實(shí)現(xiàn)

在arouter-compiler module中,查看對(duì)應(yīng)的AutowiredProcessor代碼,這是一個(gè)用來(lái)處理Autowired注解的注解處理器,主要目的是生成Java代碼,生成怎樣的Java代碼(如何生成的,可基于注解處理器原理詳細(xì)查看AutowiredProcessor),如下:

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    Test1Activity substitute = (Test1Activity)target;
    substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : 
            substitute.getIntent().getExtras().getString("name", substitute.name);
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
    substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
    
    ...
  }

代碼路徑:


autowired路徑.jpg

梳理下Autowired注解整個(gè)流程:

在Activity的變量上標(biāo)注,注解處理器會(huì)在編譯階段掃描有被Autowired注解標(biāo)注的變量,根據(jù)這個(gè)類和變量的情況生成一份Java代碼,其中最主要會(huì)有一個(gè)inject方法,完成對(duì)相關(guān)變量的解析賦值,然后在Activity的onCreate靠前位置調(diào)用對(duì)應(yīng)inject方法即可。這個(gè)注解主要目的在于省去了手動(dòng)編寫解析Intent參數(shù)的代碼。
(但為什么代碼是調(diào)用 ARouter.getInstance().inject(this); 后面會(huì)有說(shuō)明)

Route

Route注解標(biāo)注的類(頁(yè)面或服務(wù)類),可通過(guò)path跳轉(zhuǎn)到對(duì)應(yīng)界面或是獲取到具體服務(wù)。

如何使用

@Route(path = "/test/activity1", name = "測(cè)試用 Activity")
public class Test1Activity extends AppCompatActivity {
  ...
}

@Route(path = "/yourservicegroupname/hello")
public class HelloServiceImpl implements HelloService {
  ...
}

如何實(shí)現(xiàn)

在arouter-compiler module中,查看對(duì)應(yīng)的RouteProcessor代碼,用來(lái)處理Route注解的注解處理器,主要完成以下幾部分的生成代碼工作(主要為RouteProcessor中的parseRoutes方法):

  1. 獲取到所有的被注解Route的類,生成對(duì)應(yīng)的RouteMeta,分組放到groupMap中,key為group name, value為支持排序放入的的Set<RouteMeta>中。

  2. 遍歷groupMap中所有的Set<RouteMeta>的所有RouteMeta (所以是個(gè)雙層for循環(huán))生成對(duì)應(yīng)代碼。

  for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
      String groupName = entry.getKey();
       ...
      Set<RouteMeta> groupData = entry.getValue();
      for (RouteMeta routeMeta : groupData) {
        ...
      }
    ...
  }

生成的代碼如下:

public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    ...
    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));
    ...
  }
}
  1. 生成rootMap,key為group name,value為剛才每個(gè)group對(duì)應(yīng)生成的java類的類名,根據(jù)rootMap生成對(duì)應(yīng)Java文件。
   public class ARouter$$Root$$app implements IRouteRoot {
     @Override
     public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
       routes.put("test", ARouter$$Group$$test.class);
       routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
     }
   }

詳細(xì)關(guān)于這些生成類是如何被使用的,在后面LogisticsCenter中會(huì)有詳細(xì)介紹。

Interceptor

Interceptor 主要用于在跳轉(zhuǎn)過(guò)程中插入一些功能。

如何使用

@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
    Context mContext;

    @Override
    public void process(final Postcard postcard, final InterceptorCallback callback) {
        if ("/test/activity4".equals(postcard.getPath())) {

            // 這里的彈窗僅做舉例,代碼寫法不具有可參考價(jià)值
            final AlertDialog.Builder ab = new AlertDialog.Builder(MainActivity.getThis());
            ab.setCancelable(false);
            ab.setTitle("溫馨提醒");
            ab.setMessage("想要跳轉(zhuǎn)到Test4Activity么?(觸發(fā)了\"/inter/test1\"攔截器,攔截了本次跳
                          轉(zhuǎn))");
            ab.setNegativeButton("繼續(xù)", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    callback.onContinue(postcard);
                }
            });
            ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    callback.onInterrupt(null);
                }
            });
            ab.setPositiveButton("加點(diǎn)料", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    postcard.withString("extra", "我是在攔截器中附加的參數(shù)");
                    callback.onContinue(postcard);
                }
            });

            MainLooper.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ab.create().show();
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }

這個(gè)例子是想在跳轉(zhuǎn)到 "/test/activity4" 對(duì)應(yīng)的Activity過(guò)程中彈出對(duì)話框,在用戶做出了相關(guān)動(dòng)作后再繼續(xù)跳轉(zhuǎn),點(diǎn)擊"繼續(xù)"則繼續(xù)執(zhí)行,點(diǎn)擊"算了"則取消跳轉(zhuǎn),點(diǎn)擊"加點(diǎn)料"則在跳轉(zhuǎn)過(guò)程中添加參數(shù)。

如何實(shí)現(xiàn)

上面在分析ARouter的navigation的跳轉(zhuǎn)過(guò)程中,分析_ARouter的navigation方法中有提到會(huì)去判斷是否走"綠色通道",如果沒(méi)有的話則要調(diào)用 interceptorService.doInterceptions 方法來(lái)經(jīng)過(guò)攔截器的處理。

來(lái)看看攔截器是如何處理的,主要代碼在InterceptorServiceImpl類中。主要方法有:

doInterceptions:供外部調(diào)用接口方法,會(huì)在異步線程中開啟攔截器的逐個(gè)調(diào)用,會(huì)去調(diào)用_excute方法,完成對(duì)攔截器IInterceptor的process方法的調(diào)用,通過(guò)CountDownLatch來(lái)控制是否所有攔截器都調(diào)用完成,且在超時(shí)時(shí)間內(nèi)。

_excute: 調(diào)用IInterceptor的process方法,在callback的onContinue方法中遞歸調(diào)用自己,但調(diào)整index值,使用下一個(gè)攔截器。

_ARouter介紹

ARouter類只是使用Facade patten對(duì)_ARouter類進(jìn)行了封裝,并沒(méi)有太多實(shí)際功能代碼,所以看看_ARouter中的具體實(shí)現(xiàn)代碼。

首先_ARouter是個(gè)單例類,但會(huì)在getInstance方法中判斷是否有進(jìn)行初始化(必須要先初始化)。

其次幾個(gè)主要的方法:

init: 這里面最重要是調(diào)用了LogisticsCenter.init,完成相關(guān)路由信息填充。

Inject: 獲取AutowiredService的實(shí)體,會(huì)調(diào)用到AutowiredServiceImpl的autowire方法,在這個(gè)方法中會(huì)根據(jù)傳入?yún)?shù)Activity的Name再加上注解處理器中約定的后綴字段獲得新的類名(也就是注解處理器生成的對(duì)應(yīng)的Java文件對(duì)應(yīng)的類名),利用反射方式生成新對(duì)象,調(diào)用其inject方法。

(如上面分析的類 Test1Activity$$ARouter$$Autowired的inject方法)。

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    
    ...

    @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() 
                                            + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }
}

build: 生成對(duì)應(yīng)Postcard

navigation: 跳轉(zhuǎn)到對(duì)應(yīng)頁(yè)面或獲取對(duì)應(yīng)Service等

arouter-gradle-plugin module

這個(gè)module 是一個(gè)gradle plugin, 目的是幫助在com.alibaba.android.arouter.core.LogisticsCenter類的loadRouterMap方法中插入各個(gè)module注冊(cè)router代碼。(關(guān)于gradle plugin的機(jī)制,可通過(guò)深入理解Android之Gradle詳細(xì)了解)

/**
     * arouter-auto-register plugin will generate code inside this method
     * call this method to register all Routers, Interceptors and Providers
     * @author billy.qi <a href="mailto:qiyilike@163.com">Contact me.</a>
     * @since 2017-12-06
     */
    private static void loadRouterMap() {
        registerByPlugin = false;
        //auto generate register code by gradle plugin: arouter-auto-register
        // looks like below:
        // registerRouteRoot(new ARouter..Root..modulejava());
        // registerRouteRoot(new ARouter..Root..modulekotlin());
    }

    /**
     * method for arouter-auto-register plugin to register Routers
     * @param routeRoot IRouteRoot implementation class in the package: com.alibaba.android.arouter.core.routers
     * @author billy.qi <a href="mailto:qiyilike@163.com">Contact me.</a>
     * @since 2017-12-06
     */
    private static void registerRouteRoot(IRouteRoot routeRoot) {
        markRegisteredByPlugin();
        if (routeRoot != null) {
            routeRoot.loadInto(Warehouse.groupsIndex);
        }
    }

    /**
     * method for arouter-auto-register plugin to register Interceptors
     * @param interceptorGroup IInterceptorGroup implementation class in the package: com.alibaba.android.arouter.core.routers
     * @author billy.qi <a href="mailto:qiyilike@163.com">Contact me.</a>
     * @since 2017-12-06
     */
    private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
        markRegisteredByPlugin();
        if (interceptorGroup != null) {
            interceptorGroup.loadInto(Warehouse.interceptorsIndex);
        }
    }

在ARouter 1.4版本前,是通過(guò)掃描ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"這個(gè)packagename下獲取所有route相關(guān)類(包括分布在不同的module中的),然后遍歷分別調(diào)用對(duì)應(yīng)的loadInto方法加載到 Warehouse 的各自Index中(包括Warehouse.providersIndex,Warehouse.interceptorsIndex等),構(gòu)成不同類別的全局路由總表。
這段代碼目前仍存在,主要對(duì)應(yīng)LogisticsCenter.init方法中registerByPlugin變量為false時(shí)對(duì)應(yīng)的分支代碼。
這種方式的缺點(diǎn)在于是在運(yùn)行時(shí)去掃描dex文件中所有類找到需要的類反射來(lái)完成映射表的注冊(cè)。

從ARouter 1.4開始,引入AutoRegister機(jī)制(關(guān)于AutoRegister,可以在AutoRegister:一種更高效的組件自動(dòng)注冊(cè)方案了解詳情)?;驹硎牵涸诰幾g時(shí),掃描所有類,將符合條件的類收集起來(lái),并通過(guò)修改字節(jié)碼生成注冊(cè)代碼到指定的管理類中,從而實(shí)現(xiàn)編譯時(shí)自動(dòng)注冊(cè)的功能,不用再關(guān)心項(xiàng)目中有哪些組件類了。不會(huì)增加新的class,不需要反射,運(yùn)行時(shí)直接調(diào)用組件的構(gòu)造方法。
在ARouter中具體的實(shí)現(xiàn)如下:

  1. arouter-gradle-plugin實(shí)現(xiàn)為gradle plugin, plugin name 是 PLUGIN_NAME = "com.alibaba.arouter",定義在ScanSetting中。在app module中的build.gradle中加入 apply plugin: 'com.alibaba.arouter',表示使用該plugin。

  2. RegisterTransform中完成掃描以ROUTER_CLASS_PACKAGE_NAME = 'com/alibaba/android/arouter/routes/' 開頭的類,然后調(diào)用RegisterCodeGenerator類的方法完成向LogisticsCenter::loadRouterMap插入代碼。

通過(guò)反編譯apk出來(lái)的實(shí)際出來(lái)的代碼如下:

  private static void loadRouterMap() {
    registerByPlugin = false;
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$app");
    register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$app");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$app");
  }

這樣在編譯階段即可完成相關(guān)類的掃描工作,在ARouter初始化時(shí)只是完成路由總表的加載,省去在Dex文件中掃描類的步驟。

這里也貼下代碼README.md中給予的說(shuō)明

使用 Gradle 插件實(shí)現(xiàn)路由表的自動(dòng)加載 (可選)

    apply plugin: 'com.alibaba.arouter'

    buildscript {
        repositories {
            jcenter()
        }

        dependencies {
            // Replace with the latest version
            classpath "com.alibaba:arouter-register:?"
        }
    }

可選使用,通過(guò) ARouter 提供的注冊(cè)插件進(jìn)行路由表的自動(dòng)加載(power by AutoRegister), 默認(rèn)通過(guò)掃描 dex 的方式。
進(jìn)行加載通過(guò) gradle 插件進(jìn)行自動(dòng)注冊(cè)可以縮短初始化時(shí)間解決應(yīng)用加固導(dǎo)致無(wú)法直接訪問(wèn)dex 文件,初始化失敗的問(wèn)題,需要注意的是,該插件必須搭配 api 1.3.0 以上版本使用!

LogisticsCenter介紹

本想再詳細(xì)介紹下LogisticsCenter類,但發(fā)現(xiàn)經(jīng)過(guò)上面的一些介紹,LogisticsCenter的主要幾個(gè)方法已經(jīng)都解釋過(guò)。

loadRouteMap: 由arouter-gradle-plugin 插件module在編譯階段插入注冊(cè)Route相關(guān)代碼。

init: 調(diào)用loadRouteMap完成Warehouse中各個(gè)路由Index的加載,如果有plugin幫忙插入代碼則直接使用,如果沒(méi)有則通過(guò)運(yùn)行時(shí)掃描dex文件方式加載。

completion:前面分析navigation時(shí)有詳細(xì)介紹,在Route路由表中根據(jù)postcard中的path找到對(duì)應(yīng)的路由信息RouteMeta,利用RouteMeta中信息為Postcard賦值。

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

相關(guān)閱讀更多精彩內(nèi)容

  • 組件化被越來(lái)越多的Android項(xiàng)目采用,而作為組件化的基礎(chǔ)——路由也是重中之重。本篇文章將詳細(xì)的分析阿里巴巴開源...
    胡奚冰閱讀 15,024評(píng)論 8 32
  • ARouter探究(一) 前言 ARouter 是 Alibaba 開源的一款 Android 頁(yè)面路由框架,特別...
    Jason騎蝸??词澜?/span>閱讀 1,594評(píng)論 1 3
  • 前言 隨著項(xiàng)目業(yè)務(wù)邏輯和功能點(diǎn)日益遞增, 邏輯的耦合程度也逐漸升高, 組件化技術(shù)可以很好的解決這個(gè)問(wèn)題, 公司大佬...
    SharryChoo閱讀 1,185評(píng)論 0 9
  • 組件化被越來(lái)越多的Android項(xiàng)目采用,而作為組件化的基礎(chǔ)——路由也是重中之重。本篇文章將詳細(xì)的分析阿里巴巴開源...
    小小的coder閱讀 333評(píng)論 0 0
  • 此間花香 不在天堂 在家鄉(xiāng) 家鄉(xiāng)勝過(guò)天堂 銀鈴聲聲 聲聲在耳旁 那一眼 白馬,青衫,少年 后面跟著一條小狗 少年說(shuō)...
    三人生閱讀 75評(píng)論 0 0

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