Android modular

On why to carry out Android modular development, the Internet has also spoken rotten, but summed up, generally can be summed up as: in the early stage of App development, the amount of code is not large, the amount of business is relatively small, an App as a separate module for development, often not a big problem. However, with the increase of business, the code becomes more and more complex, the code coupling between each module becomes more and more serious, and the structure becomes more and more bloated. Modifying a code requires compiling the whole project, which leads to very time-consuming. At this time, the best way is to carry out modular splitting.

Benefits of modularity

To sum up the current modularity, there are generally some benefits as follows:

  • Common functions, no repeated development, modification, code reuse is stronger;
  • Independent compilation and running, improve compilation speed, also improve development efficiency;
  • More conducive to team development, different people can be independently responsible for different modules;
  • Independent modules can adopt different technical architectures, try new technical solutions, such as adopting a new network framework, or even switching to Kotlin to develop the App.

Problems that need to be solved

To modularize a project into components, the following issues need to be addressed:

  • Page hopping between modules;
  • Event communication between modules;
  • Intermodule service invocation;
  • Independent operation of modules;
  • Intermodule page jump route interception (e.g., login)

For all of the above problems, you can use ARouter to solve them.

ARouter framework

The ARouter framework is defined as a framework for componentizing Android apps. It supports routing, communication, and decoupling between modules.

  • Supports direct parsing of the standard URL to jump, and automatically inject parameters into the target page
  • Supports multi-module projects
  • Support to add multiple interceptors, custom interception sequence
  • Dependency injection is supported and can be used as a standalone dependency injection framework
  • Support InstantRun
  • Support for MultiDex(Google solution)
  • Mapping relationships are classified by group, managed at multiple levels, and initialized as required
  • Users can specify global and local degrade policies
  • Components such as pages, interceptors, and services are automatically registered with the framework
  • Supports a variety of ways to configure transition animation
  • Obtaining Fragments
  • Full support for Kotlin and blending (see other #5 at the end of this article for configuration)
  • Support third-party App hardening (automatic registration using arouter- Register)
  • Generates routing documents
  • Provides IDE plug-ins with convenient associated paths and target classes

Basic usage

Add dependencies and configurations

Before using ARouter, you need to add the corresponding dependencies, with the following steps:

android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: Project.getname ()]}}}} dependencies {// compile with compiler'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'. } // Old gradle plugin (< 2.2), you can use apt plugin'other # 4'// Kotlin configuration reference at the end of the article'other # 5'
Copy the code

Add annotations

For ARouter to identify a Route, the @route annotation identifies the specific path. Such as:

// The path must have at least two levels, /xx/ xx@route (path =)"/test/activity")
public class YourActivity extend Activity {
    ...
}
Copy the code

To initialize the SDK

if(isDebug()) {// These two lines must be written before init, otherwise these configurations will be invalid during init arouter.openlog (); // Prints the log arouter.openDebug (); // Enable debug mode (if running in InstantRun mode, debug must be enabled! } ARouter. Init (mApplication); // As early as possible, it is recommended to initialize in ApplicationCopy the code

Initiating a routing operation

Routing operations are operations that trigger routes, for example:

// 1. A simple in-app jump (through the URL to jump in'Advanced usage') ARouter. GetInstance (). Build ("/test/activity").navigation(); Arouter.getinstance ().build()"/test/1")
            .withLong("key1", 666L)
            .withString("key3"."888")
            .withObject("key4", new Test("Jack"."Rose"))
            .navigation();
Copy the code

If you want to accept pass-processed data, you can use the following method:

@route (path = mainRoutePath. MAIN_ACTIVITY) public class MainActivity extends BaseActivity {/** * Receives parameters */ @Autowired(name ="name")
    public String name;
    @Autowired(name = "age") public int age; . }Copy the code

The interceptor

// A more classic application is to handle login events during a jump so that there is no need to repeat the login check  on the target page. // Interceptors will be executed between jumps, multiple interceptors will be executedin order of priority
@Interceptor(priority = 8, name = "test interceptor")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        ...
        // No problem! hand over control to the framework
        callback.onContinue(postcard);  
        
        // Interrupt routing process
        // callback.onInterrupt(new RuntimeException("Something exception"));      

        // The above two types need to call at least one of them, otherwise it will not continue routing
    }

    @Override
    public void init(Context context) {
        // Interceptor initialization, this method will be called when sdk is initialized, it will only be called once
    }
}
Copy the code

Demotion strategy

The demotion strategy ARouter provides has two main ways. One is through callback. One way is to provide service interfaces. This method calls the onLost method of the NavCallback interface if the jump fails. ARouter provides the following callback mode functions:

ARouter.getInstance().build(MainRoutePath.MAIN_ACTIVITY).navigation(this, new NavCallback() {
     @Override
     public void onFound(Postcard postcard) {
          Log.d("ARouter"."Found it.");
     }

     @Override
     public void onLost(Postcard postcard) {
          Log.d("ARouter".I can't find it.);
     }

     @Override
     public void onArrival(Postcard postcard) {
          Log.d("ARouter"."Jump over.");
     }

     @Override
      public void onInterrupt(Postcard postcard) {
           Log.d("ARouter"."Intercepted."); }});Copy the code

Global degradation of the service interface – the way the service interface is mainly handled internally, the exposed interface is very friendly.

ARouter.getInstance().build("/test/test").navigation();

Copy the code

This degrades into a degradable service interface, and onLost is returned. Such as:

@Route(path = RoutePath.DEGRADE)
public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        ARouter.getInstance().build(RoutePath.DEGRADE_TIP).greenChannel().navigation();
    }

    @Override
    public void init(Context context) {

    }
}
Copy the code

confusion

To avoid packaging errors, use the keep flag for the following.

-keep public class com.alibaba.android.arouter.routes.**{*; } -keep public class com.alibaba.android.arouter.facade.**{*; } -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*; }If byType is used, add the following rule to protect the interface
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

If you use single-class injection, that is, you do not define the interface to implement the IProvider, you need to add the following rules to protect the implementation
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
Copy the code

ARouter Helper,

Search for ARouter Helper in the Android Studio plugin market, or download the ARouter – idea-Plugin zip installation package listed at the top of the document to install it manually. You can find a navigation icon at the beginning of the jump code and click on it to jump to the target class that identifies the path in the code.

ARouter modular example

Next, a demo will show you how to develop modularity with ARouter. The overall architecture of the demo modularity is shown below.

  • App: The host module of the project is just an empty shell, which depends on other modules and becomes the entrance of the project architecture.

  • Baselibrary: a project’s base class library, where each submodule relies on shared common classes and resources, preventing multiple implementations of common functions in different modules;

  • Module_route: library that centrally manages the routes of all modules;

  • Module_main: flash screen, login page, home page, etc.

  • Module_home: home page module;

  • Module_video: video module;

  • Module_mine: My module;

Switch between dependent mode and independent mode

One advantage of using modular development is that individual modules can be developed at the same time and run independently without relying on the host app. In other words, each module is a separate app, which can be relied on when the project is released. Business modules are not allowed to depend on each other, but need to rely on the base class library. In addition, the apK generated by a single module is smaller in size, faster in compile time, and much more efficient in development. At the same time, it can also be tested independently. To achieve this effect, some configuration of the project is required.

Gradle. Configuration properties

A switch needs to be set in the main gradle.properties project to control the compilation mode of the Module, for example:

isModule=false
Copy the code

When isModule is false, it can be used as a dependent library and can only start the project with the host APP. When the module is selected to run, the red X is in front of other modules, indicating that it cannot run.

When isModule is true, it runs as a separate module, selecting one of the specific modules to run directly.

Module Manifest file

To complete the dependency mode and standalone mode switch, the module manifest file needs to be configured with two, one as a standalone project manifest file and one as a library manifest file, for example, the module_main module.

Gradle configuration

To complete the switch, you also need to configure the Build. gradle file for the Module, as shown below:

Host APP Configuration

Next, add a module dependency to the host app’s build.gradle as follows:

dependencies {
    if(! isModule.toBoolean()) { implementation project(':module_home')
        implementation project(':module_video')
        implementation project(':module_main')
        implementation project(':module_mine')
        implementation project(':module_router')}}Copy the code

ARouter Helper

Q&A

Question 1

ARouter::Compiler >>> No module name, for more information, look at gradle log. Check all modules that your project depends on, including modules that depend on your module. In order to be able to compile separately, you need to add a name to each module.

defaultConfig {
    
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ AROUTER_MODULE_NAME : project.getName() ]
            }
        }
}

Copy the code

Question 2

ARouter.getInstance().inject(this); There is a singletask startup mode for an activity, call arouter.getInstance ().inject(this) in onNewIntent; The intent in onNewIntent is not the same as the intent in onCreate. The intent in onNewIntent is the same as the intent in onCreate. So you need to call the setIntent method in the onNewIntent method, and then you get the parameters.

Question 3

ARouter there’s no route matched

Different modules must have different primary paths; otherwise, the primary paths in a moudle will become invalid.

Attached: sample download address