This is the second day of my participation in the August More text Challenge. For details, see:August is more challenging

preface

If A and B have no dependencies and want to communicate with each other, the basic principle is to use A binder, socket, file, provider, EventBus, Arouter, etc., to communicate with each other. Either he’s looking for attention, or he’s giving up therapy.

Let’s assume that we have: The app module has a MainActivity, and the Login Module wants to call the MainActivity. According to the above, It can only be implemented through common or other public dependencies of common dependencies.

1 Use common

  • 1 define a Map

    >
    ,class>
  • 2 In the App Module, add the mainActivity. class to the map:
map.put("main",MainActivity.class)
Copy the code
  • 3 In the Login Module, get the MainActivity. Class from map.get(“main”) and create an Intent to launch the MainActivity
Class<? extends Activity> mainActivityClass= map.get("main")
startActivity(new Intent(this,mainActivityClass))
Copy the code

The core is two things: injection and acquisition. Namely: you want to let others use himself into public warehouse (injection), and provide a certificate (where the certificate is the string “main”), which is the key, I want to take the credentials to withdraw from public warehouse (access), a public warehouse is can visit each other, that is must depend on each other.

Next, let’s look at the implementation of Arouter.

2 is done by Arouter.

We do the same thing with Arouter, except it’s a lot simpler, a lot easier, and a lot more stupid. It does both injection and capture, and it adds a lot of optimizations, but it’s the same thing, so let’s see.

Simple to use

Add dependencies to gradle file:

If Java, add:

API 'com.alibaba: aroutter-compiler :1.2.2' android {defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } } }Copy the code

For kotlin, add:

Alibaba: aroutter-api :1.5.0' kapt 'com.alibaba: aroutter-compiler :1.2.2' android {defaultConfig {kapt {arguments  { arg("AROUTER_MODULE_NAME", project.getName()) } } } }Copy the code

The difference between Java and Kotlin is that Kotlin uses the kapt keyword, everything else is the same.

2 is initialized in the onCreate() of Application

// Initialize (inject) arouter.init (this);Copy the code

3 Define the path, which is the credential

@route (path = "/app/activity_main") @route (path = "/app/activity_main")Copy the code

4 Start the corresponding Activity based on the PATH, that is, obtain

ARouter.getInstance().build(path).navigation(); // Obtain and start the Activity based on the credential pathCopy the code

Arouter.init (this) is the injection process, and the key is the path defined by @route (path); Then call navigation() with the path to fetch and start the corresponding Activity.

This is just a simple example of using Arouter, but it’s not an introduction. The focus of this article is on the principle. For more details, visit the Arouter website.

So, how does the injection of Arouter do, and how does the fetch get, see the following.

The principle of analyzing

1 Compile time to do – generate intermediate code

Apt technology: Apt technology is to set up a set of code templates, and then during class loading, dynamically generate a.java file based on the specified code template. This file can be accessed directly at run time, see here for more information.

The diagram below:

So, when we add Arouter’s dependency to the gralde, Then at compile time will be in the corresponding module/build/generated/source/kapt/debug/next generation “com. Alibaba. Android. Arouter. Routes” directory, arouter generated code in here, Such as:

/ / this IRouteRoot, see the name "ARouter $$$$Root app", including "ARouter $$Root" is a prefix, "app" is the group name, also is the path to "/" inside space of the first string, and then through the "$$" connection, / / the full name of the class it is "com. Alibaba. Android. Arouter. Routes. Arouter $$$$Root app" public class arouter $$$$Root app implements IRouteRoot {// the parameter is a map and the value type is IRouteGroup @override public void loadInto(map <String, Class<? Extends IRouteGroup>> routes) {// "app" extends Route(path = "/app/activity_main") { Put ("app", ARouter$$Group$$app.class); } // This is an IRouteGroup. Similarly, Public class ARouter$$Group$$app {@override public void loadInto(Map<String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String; RouteMeta> Atlas) {// "app/activity_main" is the path we specified via @route, after which RouteMeta stores the type of component to launch, And the corresponding.class file // the argument to this routemeta.build () is important, Put ("/app/activity_main", routemeta.build (routetype.activity, mainActivity.class, "/app/activity_main", "app", null, -1, -2147483648)); }} // The routemeta.build () method is useful after the argument // type is: Routetype. ACTIVITY, // destination is mainActivity. class, // path is "/app/activity_main", Public static RouteMeta build(RouteType type) public static RouteMeta build(RouteType type) Class<? > destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) { return new RouteMeta(type, null, destination, null, path, group, paramsType, priority, extra); }Copy the code

Note that the above code is all generated in build() in the App Module. That is, the code is completely transparent and unreachable to the Login Module.

What’s more, we found that all the generated.Java files have a common prefix “ARouter”, such as “ARouter”, such as “ARouterRoot”. And because they are below the Arouter generated directory, so their complete class names have a prefix: “com. Alibaba. Android. Arouter. Routes. Arouter $$”. All right, so let’s say we’ve compiled it and we launch our app.

2 Runtime things to do – injection

So now that we’ve compiled it, we just hit Run to launch the app, and now we’re at the runtime, At this point we have/build/generated/source/kapt/debug/grown became “com. Alibaba. Android. Arouter. Routes” directory, and there is also a heap of arouter generated code.

The next sequence of code is executed, running into the Application’s onCreate(), so the initialization is performed:

// initialize, which calls the pile of code generated at compile time, Arout.init (this) protected static synchronized Boolean init(Application Application) {// We save mContext, and we say mContext = application; // Initialize LogisticsCenter. Init (mContext, executor); hasInit = true; mHandler = new Handler(Looper.getMainLooper()); return true; }Copy the code

We followed the code and found the final tune:

// Executor is a built-in thread pool LogisticsCenter. Init (mContext, executor);Copy the code

Next we look at the code, which removes the log and some of the minor logic:

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { mContext = context; executor = tpe; try { Set<String> routerMap; / / if it is debugable () or update the app version of the if (ARouter. Debuggable () | | PackageUtils. IsNewVersion (context)) {/ / then retrieves all of the class, so, If your Arouter has a route not found, update the version number or debug Arouter. / / here will get all the "com. Alibaba. Android. Arouter. Routes" the class name of the class files in the directory. routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE); // Cache it in SharedPreferences for next time. if (! routerMap.isEmpty()) { context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply(); } // Cache the version number of the app in SharedPreferences for next time. PackageUtils.updateVersion(context); } else {// If the version number is not updated and debug is not enabled, All the class is retrieved from the cache cache before routerMap = new HashSet < > (context. GetSharedPreferences (AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>())); } // Iterate over all the class names you just got, and call their loadInto() method in reflection. Then the generated classes in the App Module will have their loadInto() called and injected into the argument. for (String className : RouterMap) {/ / joining together is in fact the string ". Com. Alibaba. Android arouter. Routes. Arouter $$Root ", isn't that the compiler generated when "arouter $$$$Root app" prefix? If (classname.startswith (ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { Map.put ("app", ARouter$$Group$$app.class), and this key/value pair is placed in Warehouse. GroupsIndex. ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); / / this is "com. Alibaba. Android. Arouter. Routes. Arouter $$Interceptors"} else if (className. StartsWith (ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) { ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); / / this is "com. Alibaba. Android. Arouter. Routes. Arouter $$will"} else if (className. StartsWith (ROUTE_ROOT_PAKCAGE + + DOT  SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) { ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } } } catch (Exception e) { throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]"); }}Copy the code

According to the above, we know:

  • 1 for all “com. Alibaba. Android. Arouter. Routes” directory under the name of the class, this step is a cache operation.
  • 2 iterates over all the acquired class names and calls their loadInto(map) method.
  • LoadInto (map) will store all the (key,group.class) classes in Warehouse. GroupsIndex

Let’s look at the code for Warehouse:

Class Warehouse {// Cache route and metas; // Cache route and metas; static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); static Map<String, RouteMeta> routes = new HashMap<>(); // Cache provider static Map<Class, IProvider> providers = new HashMap<>(); static Map<String, RouteMeta> providersIndex = new HashMap<>(); // Cache interceptor static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]"); static List<IInterceptor> interceptors = new ArrayList<>(); static void clear() { routes.clear(); groupsIndex.clear(); providers.clear(); providersIndex.clear(); interceptors.clear(); interceptorsIndex.clear(); }}Copy the code

Let’s look at the access to all “com. Alibaba. Android. Arouter. Routes” class files in the directory path of logic, the key!

/ / we incoming packageName "com. Alibaba. Android. Arouter. Routes" public static Set < String > getFileNameByPackageName (Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException { final Set<String> classNames = new HashSet<>(); List<String> Paths = getSourcePaths(context); final CountDownLatch parserCtl = new CountDownLatch(paths.size()); For (final String path: paths) { DefaultPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { DexFile dexfile = null; If (path.endswith (EXTRACTED_SUFFIX)) {// If it is a.zip file, LoadDex = dexfile.loaddex (path, path + ".tmp", 0); } else {dexfile = new dexfile (path); } // Iterate over the elements under dexfile Enumeration<String> dexEntries = dexfile.entries(); while (dexEntries.hasMoreElements()) { String className = dexEntries.nextElement(); / / if based on com. Alibaba. Android. Arouter. "routes" at the beginning, just add the if (className. StartsWith (packageName)) {classNames. Add (className); } } } catch (Throwable ignore) { Log.e("ARouter", "Scan map file in dex files made error.", ignore); } finally { if (null ! = dexfile) { try { dexfile.close(); } catch (Throwable ignore) { } } parserCtl.countDown(); }}}); } parserCtl.await(); // Return classNames; }Copy the code

The logic of the above code is simple:

  • 1 Obtain the paths of all Dex files and create a DexFile
  • 2 traversal DexFile, and all to “com. Alibaba. Android. Arouter. Routes” file to add in the beginning, and then return.

Based on the above chapters, we know, ARouter files are generated at compile time. Com. Alibaba android. ARouter. “routes” as the prefix, so the results of this function is to get all ARouter file generated at compile time.

Then let’s see how to get all the DexFile paths:

public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException { ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0); File sourceApk = new File(applicationInfo.sourceDir); List<String> sourcePaths = new ArrayList<>(); / / add the apk the default path, which can be understood as the path of the apk sourcePaths. Add (applicationInfo. SourceDir); // EXTRACTED_NAME_EXT = ".classes", So this is something like "test.classes" String extractedFilePrefix = sourceapk.getName () + EXTRACTED_NAME_EXT; // If MultiDex is enabled, then traverse the path to each dex file if (! IsVMMultidexCapable () {int totalDexNumber = getMultiDexPreferences(context).getint (KEY_DEX_NUMBER, 1); File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME); For (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; SecondaryNumber++) {//EXTRACTED_SUFFIX is ".zip", So fileName will look like test.classes2.zip, Test.classes3. zip String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX; File extractedFile = new File(dexDir, fileName); If (extractedFile isFile ()) {/ / add path sourcePaths. Add (extractedFile. GetAbsolutePath ()); } else { throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'"); }}} // return return sourcePaths; }Copy the code

The logic of the above code is very simple, is to get the path of all Dex files corresponding to the app, in fact, Android code packaged out is a pile of Dex files, can be regarded as a collection of.class files. So when we went from writing code to packaging it, it was :.java ->.class ->.dex. Now we’re going to go the other way, from.dex ->.class, and of course we just need to get.class.

Ok, let’s review the process again:

  • 1 In the Application onCreate() we call arouter.init (this).
  • 2 then call ClassUtils. GetFileNameByPackageNam () to obtain all com. Alibaba. Android. Arouter. “routes” dex file path in the directory.
  • 3 Then iterate through the dex files to get the full class names of all the calss files.
  • 4 Then iterate over all the class names, get the class prefix, and call their loadInto(map) method through reflection. This is an injection process, and it is injected into the member variable of the parameter Warehouse.
  • 5 including Arouter generated at compile time “. Com. Alibaba. Android Arouter. Routes. Arouter Root. ARouterRoot. ARouterRoot. ARouterRoot app “class, Its corresponding code :<“app”,ARouterapp” class, The corresponding code :<“app”,ARouterGroup$$app.class> is added to the Warehouse.

Ok, so now we’re done with the injection process, to put it simply: App package name -> get.dex -> get.class -> find the corresponding.class -> reflect call method -> store in the Warehouse, this process is injection, Warehouse is the Warehouse, stored in the need key and.class, ok, Let’s look at the acquisition process.

3 When the call to do – get

The code to call is simple:

"/app/activity_main" arouter.getInstance ().build(path).navigation();Copy the code

Arouter.getinstance () is a singleton. Let’s look at the build(path) function:

Public Postcard build(String path) {return _arouter.getInstance ().build(path); } The Postcard build(String path) {if (textutils.isEmpty (path)) {throw new HandlerException(consts.tag) + "Parameter is invalid!" ); } else { //... // Here, extractGroup(path) is to get the group name, path is "/app/activity_main", App return build(path, extractGroup(path)); Private String extractGroup(String path) {// Verify that the path is valid, for example, if it doesn't start with "/", Is an error if (TextUtils. IsEmpty (path) | |! path.startsWith("/")) { throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!" ); String defaultGroup = path.substring(1, path.indexof ("/", 1)); if (TextUtils.isEmpty(defaultGroup)) { throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!" ); } else { return defaultGroup; } } catch (Exception e) { logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage()); return null; } // Build, We already know that the parameters are ("/app/activity_main","app") and that the Postcard build(String Path, String group) { if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!" ); } else { // ... Return new Postcard(Path, group); return new Postcard(path, group); }} Public Postcard(String path, String group) {// Call this(path, group, null, null); } public Postcard(String path, String group, Uri Uri, Bundle Bundle) {// Postcard the path in the world, Is/app/activity_main setPath (path); // Save the group, which is "app" setGroup(group); // The uri is null!! setUri(uri); This.mbundle = (null == Bundle? new Bundle() : bundle); }Copy the code

Okay, that’s it, so now we know, arouter.getInstance ().build(path); After the Postcard is created and the path and group are saved, we look at the Postcard’s navigation() function:

Public Object navigation() {// Null return navigation(null); } // Public Object navigation(Context Context) {// Overloaded call, this Context is null return navigation(Context, null); Public Object navigation(Context Context, NavigationCallback callback) {// Use this as an argument. This is the Postcard, which contains the previous path and group. return ARouter.getInstance().navigation(context, this, -1, callback); } public Object navigation(Context mContext, int requestCode, int requestCode) NavigationCallback callback) {return _arout.getInstance ().navigation(mContext, postcard, requestCode, callback); } // This is where the Object navigation is protected (final Context Context, final Int requestCode, final Int requestCode) final NavigationCallback callback) { //... Try {/ / core function 1 LogisticsCenter.com pletion (it); } catch (NoRouteFoundException ex) { if (null ! = callback) { callback.onLost(postcard); } else { DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class); if (null ! = degradeService) { degradeService.onLost(context, postcard); } } return null; } if (null ! = callback) { callback.onFound(postcard); } // Determine if the interceptor needs to be called if (! It. IsGreenChannel ()) {/ / need to call blocker interceptorService. DoInterceptions (it, new InterceptorCallback() { @Override public void onContinue(Postcard postcard) { _navigation(context, postcard, requestCode, callback); } @Override public void onInterrupt(Throwable exception) { if (null ! = callback) { callback.onInterrupt(postcard); } logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage()); }}); } else {// Do not need to call the interceptor // Core function 2 return _navigation(context, postcard, requestCode, callback); } return null; }Copy the code

Next, let’s look at the two core functions:

Public synchronized static void Completion (Postcard) {if (null == Postcard) {throw New NoRouteFoundException(TAG + "No postcard!" ); } // Get a RouteMeta from Warehouse. Routes. We only used Warehouse. GroupsIndex, so it must be null. It's based on path. RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); If (null == routeMeta) {// run here and get the class of IRouteGroup from Warehouse. GroupsIndex! Finally use our previous injection of stuff, it. GetGroup () is the "app", / / but we changed the Warehouse. The groupsIndex. Put (" app ", ARouter $$Group $$app. The class). I'll just take it out here. Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); if (null == groupMeta) { throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath()  + "], in group [" + postcard.getGroup() + "]"); } else {try {// start reflection IRouteGroup iGroupInstance = groupMeta. GetConstructor ().newinstance (); // start reflection IRouteGroup iGroupInstance = groupMeta. }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} // atlas.put("/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648)); The key is "/app/activity_main", and just like the Postcard path, it's in wareware.routes for later retrieval. iGroupInstance.loadInto(Warehouse.routes); // Discard the group, the meaning of the group is used to get the route, now it is useless, delete the save memory. Warehouse.groupsIndex.remove(postcard.getGroup()); } catch (Exception e) { throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "] "); } And return to the completion(Postcard) of the function header again; } else {// come in the second time, run here, set a bunch of properties, Build (routetype.activity, mainActivity. class, "/app/activity_main", "app", null, -1, -2147483648) postcard.setDestination(routeMeta.getDestination()); // MainActivity.class postcard.setType(routeMeta.getType()); // RouteType.ACTIVITY postcard.setPriority(routeMeta.getPriority()); // -1 postcard.setExtra(routeMeta.getExtra()); RawUri = Postcard.geturi (); // -2147483648 // If the URI is null, don't look at the URI rawUri = Postcard.geturi (); if (null ! = rawUri) { // Try to set params into bundle. Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri); Map<String, Integer> paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { for (Map.Entry<String, Integer> params : paramsType.entrySet()) { setValue(postcard, params.getValue(), params.getKey(), resultMap.get(params.getKey())); } postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{})); } postcard.withString(ARouter.RAW_URI, rawUri.toString()); {case PROVIDER: Class<? Case PROVIDER: 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(); break; case FRAGMENT: postcard.greenChannel(); default: break; }}}Copy the code

Core function 1 is done, and I ran it twice,

  • The first time, we fetched the data stored in the injection from Warehouse. GroupsIndex and loadInto() the relevant data into Warehouse. Routes.
  • The second time, we simply assigned the parameters postcard values, Destination, Type, etc.

Ok, now our Warehouse. Routes has data, and the postcard parameter has destination and type, so we execute the core function 2:

// core function 2 Private Object _navigation(Final Context Context, final Int requestCode, final Int requestCode) Final NavigationCallback callback) {// We know that the context is null, so we take the context arouter.init (context), Application Final Context currentContext = null == Context, right? mContext : context; Switch (Postcard.getType ()) {Case ACTIVITY: // Create the intent. The destination is mainActivity.class. In core function 1, we specified final Intent Intent = new Intent(currentContext, Postcard.getDestination ()); intent.putExtras(postcard.getExtras()); // Flags, we did not set it, is -1 int Flags = postcard.getFlags(); if (-1 ! = flags) { intent.setFlags(flags); } else if (! (currentContext Instanceof Activity)) {// Our context is not Activity // Add the FLAG_ACTIVITY_NEW_TASK Flag(otherwise, some versions will break if you start the Activity with application). If navigation() passes the Activity as context, Intent.setflags (Intent.flag_activitY_new_task); } // Set the Action, which we don't have. String Action = postcard.getAction(); if (! TextUtils.isEmpty(action)) { intent.setAction(action); } // Switch to the UI thread to start the Activity. runInMainThread(new Runnable() { @Override public void run() { startActivity(requestCode, currentContext, intent, postcard, callback); }}); 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; }Copy the code

The logic of the core function 2 is simple. The postcard type is used to execute the corresponding logic, and then the Activity branch is executed, and the destination, mainActit.class, is retrieved to start it.

Ok, let’s summarize:

If we go to navigation() with the path=”app/activity_main”, we’ll create the postCard of this path where group=”app”(segmented with “/”), We get the class object “Arouter GroupGroupGroupap. class” from the Werehouse groupIndex using group(i.e. “app”). Then, use reflection to create an instance, And call the loadInto() function, so it executes:

atlas.put("/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648));
Copy the code

Since then, our Werehouse routes have:

{"/app/activity_main", routemeta.build (routetype.activity, mainActivity.class, "/app/activity_main", "app"},Copy the code

Then, as the postCard type goes to switch-Case, when the case goes to routeType. ACTIVITY, the ACTIVITY starts, and we have the class of the corresponding ACTIVITY, If navigation(context) passed the context, start the Activity with that context, otherwise start the Activity with the context arouter.init (context), and if that context is not the Activity, FLAG_ACTIVITY_NEW_TASK flag is added to start the Activity.

conclusion

We tracked Arouter’s source flow along the way:

  • Use Apt technology to generate intermediate code for @route (path) annotated classes at compile time
  • 2 arouter.init (context) initializes the context, and the key is path
  • Arouter.getintstance ().build(path).navigation() ¶

Arouter is also dependent on the App and Login modules, so Arouter itself is a third-party public dependency.

So, the big idea is: If two modules at the top level are not connected to each other, they can communicate with the common dependency of the underlying module. The module here is a general term. For example, two applications can communicate with the system program, two activities can communicate with the application, and even two classes can communicate with the parent class. The two little techniques are Apt and class loading, but of course you don’t have to use them. The technique is the means, the idea is the goal, as long as the goal is achieved, nothing matters.