1 Component Introduction

1.1 What is componentization

Service components are not horizontally dependent and can run independently.

1.2 Componentization significance

Business components do not depend on each other, but can interact with each other. They can be combined, disassembled and reused freely. They are highly decoupled and layered independently.

Componentize Gradle configuration

  1. Unified management of dependencies.
  2. Extract the Base Gradle file and write the common configuration of Gradle to base.
  3. Dynamically switch library and Application in the component’s build.gradle and define the applicationId in the Application component.
  4. Configure the component to run independently of the AndroidManifest file, independent runtime, packaging to exclude the file.
  5. Dependencies on business components, using runtimeOnly, for code isolation.
  6. Gradle file added resourcePrefix to implement resource isolation, resource image manually added prefix.

Implementation, runtimeOnly, API differences

  • Implementation is not open to the outside world, but this Module depends on.
  • RuntimeOnly is dependent at runtime
  • The API can pass dependencies, and other projects can rely on the API’s JAR packages.

3 Component Module initialization

When a component runs independently, it has a separate manifest, and of course you can specify the Application class to initialize it. When components are merged, there can only be one Application and it exists in the host App. How should components be initialized? Solve the problem with reflection (dynamic proxy mode) :

  1. Library-base defines the component lifecycle management class, registers the full path of the component class name that needs to be initialized, and defines the singleton class ModuleLifecycleConfig. The singleton class ModuleLifecycleConfig traverses the component initialization path registered in the component lifecycle management class, and calls the initialization method dynamically through reflection.
  2. Define the IModuleInit interface, which is implemented by the component that needs to be initialized, and do the initialization in the implementation class.
  3. Call the relevant method that calls ModuleLifecycleConfig in the host module’s Application.

Add debug (module run separately) code

Modules that need to run independently add debugging activities, AndroidManifest, and so on.

5 Communication between components

There is absolutely no coupling between components, but in real development there will definitely be business intersections, and communication between components will be achieved through Arouter.

6. Arouter

6.1. APT

APT scans and processes annotations in code at compile time, and then outputs Java files based on the annotations.

Common annotation frameworks:

  • Eventbus: concatenates the code and writes the file to the BufferWriter (IO).

  • Butterknife

  • Arouter: JavaPoet and auto-service are used

    • JavaPoet: Square’s open source Java code generation framework provides an API for generating Java files. Apis for creating methods, creating classes, adding methods to classes, creating packages, and adding classes to packages.
    • Auto-service: it provides a simple way to register APT, avoiding the original tedious registration steps
  • DataBing

6.2 ARouter’s technical proposal

ARouter provides two SDKS, one for two different phases.

  1. Arouter_api: Is run-time oriented and provides an API to implement routing functionality.
  2. Arouter_compiler: Works at compile time and contains Annotation processing classes that are generated by Java’s Annotation Processor Tool according to the defined Processor.

Arouter_annotation: contains the annotation class and the bean that carries the data

6.2.1 Page Registration

Process:

  1. The annotated class file is scanned through the annotation handler
  2. Sorted by different types of source files: ARouter is a framework that provides a lot of functionality, not only for jumping, but also for decoupling between modules. In addition, ARouter provides a lot of functionality, such as the automatic registration of interceptors mentioned earlier, but all components in ARouter are automatically registered
  3. Generate mapping files according to a fixed naming format
  4. The mapping file is loaded with a fixed package name during initializationThe first three steps occur at compile time, and the last step is run time.

6.2.2 Load: Group management and load on demand

ARouter does not load registered pages all at once, which has a significant impact on performance. Arouter allows multiple groups under a module, all of which will eventually be managed by a root node. As shown in the image above, assuming that there are four modules, each module has a root node, below each root node can manage the group nodes in the whole module, each group node contains all the pages under this group, that is to say, can according to certain business rules or naming conventions to part of the page aggregation as a group, Each group is the first segment in the path, and each module has an Interceptor node, which is the Interceptor node. In addition, each module also has a provider node that controls the interception inversion.

ARouter will load all root nodes at once, not any Group nodes, when initialized. The three circles below show the loading of ARouter when it is initialized.

When a page under a group is accessed for the first time, all the pages of the entire group are loaded. This is called on-demand loading for ARouter.

6.2.3 interceptor

6.2.4 InstantRun compatible

6.3 Implementation of dependency injection

6.4 Arouter source code analysis

6.4.1 compile-time

6.4.2.1 arouter – the compiler

Arouter-compiler finds the annotation defined in arouter-Annotation declared by the auto-configured Processor, and then uses Javapoet to generate auxiliary injection code according to the configuration of the annotation.

Arouter-compiler has three processors:

  • AutowiredProcessor — Handles @autowired
  • InterceptorProcessor — Handles @interceptor
  • RouteProcessor — Handles @autowired, @route
6.4.2.1.1 RouteProcessor

Routeprocessor. process iterates through all classes that declare @Router annotations, uses javaPoet interfaces to generate Java code, and first creates methods, creates classes, adds methods to classes, creates packages, and adds classes to packages.

What did RouteProcessor do

  • Group generates the ARouter RootRootRoot”moduleName” class according to @route configuration path.

    Root node of route and Provider of each module.

  • ARouter RootRootRootmoduleName. Java group as the key, injected generated ARouter GroupGroupGroupxxxx class.

    Children of each root node.

  • According to @ the Route configuration generated ARouter ProvidersProvidersProviders “groupName” and ARouter GroupGroupGroup “groupName”

  • ARouter ProvidersProvidersProvidersxxxx. Java classes for the key, injected IProvider interface implementation class

  • ARouter groupGroupGroupXXXX. Java Uses path as key to inject RouteMate instances, describe implementation classes and @autoWired parameter description

6.4.2.1.2 InterceptorProcessor

Generating code into interceptor: ARouter InterceptorsInterceptorsInterceptorsmodulename. Java

Store the interceptor Class, with priority as key, into Map<Integer, Class<? Extends IInterceptor > >.

6.4.2.1.3 InterceptorProcessor

Responsible for generating auto-injected code

6.4.2 runtime

6.4.2.1 Init

Call ARouter. Init to initialize ARouter, ARouter. Init, using flag bits, making sure the initialization code executes once, calling _ARouter.

Where Arouter is the class that exposes the interface and _ARouter is the implementation class, the design pattern used here is:

  • Decorator pattern: Decoupled, selectively exposing the interface of the implementation class, or adding functionality to the implementation class.
  • Arouter and _Arouter use singleton patterns, respectively.

Logisticscenter. init(mContext, executor) is the core method of _arouter.init. Executor is a pool of threads, and multiple interceptors and jumps are executed asynchronously.

LogisticsCenter. Init: Is to find the com. Alibaba. Android. Arouter. Routes packets of class (compile time by analyzing the comments generated code), for instance and forced into IRouteRoot, IInterceptorGroup, IProviderGroup, Then call the loadInto method:

  • IRouteRoot: Adds the class registered with the declared Route annotation to the parameter collection, namely groupsIndex.
  • IInterceptorGroup: Adds the class that registers the Interceptor annotation and implements the IInterceptor interface to the parameter set, that is, the interceptorsIndex.
  • IProviderGroup: Add the class that registers the declared Route annotation and implements the IProvider interface to the parameter set, providersIndex.

In summary, the init process loads all the annotated information into memory (root node) and completes initialization of all interceptors.

6.4.2.2 navigation

After initialization, route hops can be implemented:

ARouter.getInstance().build("/test/activity").navigation();
Copy the code

ARouter. GetInstance (). Build returns the PostCard object

It. The navigation eventually call _Arouter. Navigation, internal calls the LogisticsCenter.com pletion, if Warehouse RoteMeta was not found. The loadInto method of IRouteGrop is called again to reload the cache, and Completion is called again to set a green channel for the PROVIDER and FRAGMENT. This is a lazy loading of the group.

_arouter. navigation. PROVIDER and FRAGMENT are the green channels and execute _navigation directly.

InterceptorService is the default intercepting service configured by ARouter, which is initialized by calling _Arouter. AfterInit. The doInterceptions recursively call _excute, and all interceptors perform process. If neither intercepts, execute _Arouter._navigation.

_Arouter. _navigation:

  • Activity: Initializes the Intent, Set flags, Set Actions, the main thread starts the activity (handler implementation), and sets the animation.
  • PROVIDER: Returns the PROVIDER.
  • FRAGMENT: Returns the FRAGMENT.

6.4.3 ARouter to summarize

Compile-time arouter- Compiler is responsible for generating some of the injected code:

  • ARouter groupgroupgroupgroup-name The information about @route declared in the group is injected with the name of the groupname
  • ARouter RootRootRootapp Is injected with ARouter groupgroupgroupgroup-name
  • ARouter ProvidersProvidersProvidersapp injection IProvider interface information
  • ARouter InterceptorsInterceptorsInterceptorsapp injection IInterceptor interface information
  • The realization of Test1Activity ARouterARouterARouterAutowired @autowired automatic injection

The injected information is cached when ARouter is initialized at runtime

During navigation, lazy loading is performed based on the cache, and then the actual object is retrieved or the activity is jumped.

Automatic injection is to invoke the corresponding Test1Activity ARouterARouterARouterAutowired as an example, to copy statement @autowired fields.

6.4.4 Arouter initialization speed optimization:

Use the arouter-register plug-in.

  • Before using the plug-in:

    Init converts a dex path to a DexFile through a thread pool.

  • After use:

    Arouter automatically loads routing table information using Gradle piling to insert code at compile time. So in ARouter initialization time won’t go to find filter corresponding to com. Alibaba. Android. ARouter. The routes at the beginning of the name of the class, so as to achieve the purpose of reduce the initialization time.