Arouter is a componentialized routing framework for Android. The functions involved include activities, fragments, jump strip parameters, custom services, custom interceptors, interception sink, and redirect urls. If you’ve used Arouter, you’ve only used Arouter’s jump and its parameter-based features, such as custom services, interceptors, global degradation policies, and redirection. Here’s how to use them.

directory

  • Basic dependence
  • Initialize the
  • Add annotations
  • A routing
  • Add confusion
  • Automatic loading of routing tables using Gradle
  • Use the IDE plug-in to navigate to the target class

Used to introduce

1. Base dependencies

1.1. Java version dependencies

Add the following code to the module that uses Arouter:

android {

    defaultConfig {

.

        javaCompileOptions {

            annotationProcessorOptions {

//arouter specifies the module name needed for compiling

                arguments = [AROUTER_MODULE_NAME: project.getName()]

            }

        }

    }

}



dependencies { 

.

    implementation 'com. Alibaba: arouter - API: 1.5.1'

    annotationProcessor 'com. Alibaba: arouter - compiler: 1.5.1'

}

Copy the code

The common practice here is to put the arouter-API dependencies in the module of the base service, because since componentization is used, all modules must depend on the arouter-API library. The arouter-Compiler dependency needs to be placed in each module.

1.2. Kotlin version dependencies
plugins {

.

    id 'kotlin-kapt'

}



dependencies {

.

    implementation 'com. Alibaba: arouter - API: 1.5.1'

    kapt 'com. Alibaba: arouter - compiler: 1.5.1'

}

Copy the code

Note that the plugin is the new androidStudio plugin, but kotlin is not the same as The Java plugin. Everything else is the same.

2. The initialization

This is easy, just initialize it in Application:

if(isDebug()) {// These two lines must be written before init, otherwise these configurations will not be valid during init

ARouter.openLog(); // Prints logs

ARouter.openDebug(); // Enable debug mode (if running in InstantRun mode, debug must be enabled! The online version needs to be closed, otherwise there is a security risk.

}

ARouter.init(mApplication); // As early as possible, it is recommended to initialize in Application

Copy the code

In order to optimize the startup of Arouter, the initialization of Arouter took more than 4 seconds. As a result, the initialization of Arouter was put on the welcome page. As a result, the online version found a pit. You’ll notice that Arouter’s initialization is marked false, so you’ll need to put Arouter’s initialization in the Application. The Application will initialize the Arouter process even if the process has been killed, so the initialization will still be done in the Application. However, the automatic loading of Gradle plugin routes is implemented, as described below.

3. Add annotations

3.1 @ the Route annotation

The Route annotation applies to classes that carry the path. Here are some examples of how the Route annotation can be used:

  • The Route annotation adds a Route to the Activity
@Route(path = "/login/loginActivity")

class LoginActivity : AppCompatActivity() {

.

}

Copy the code
  • The Route annotation adds a global serialization method
@Route(path = "/yourservicegroupname/json")

class JsonServiceImpl : SerializationService {

    lateinit var gson: Gson

override fun <T : Any? > json2Object(input: String? , clazz: Class<T>?) : T {

        return gson.fromJson(input, clazz)

    }



    override fun init(context: Context?) {

        gson = Gson()

    }



override fun object2Json(instance: Any?) : String {

        return gson.toJson(instance)

    }



override fun <T : Any? > parseObject(input: String? , clazz: Type?) : T {

        return gson.fromJson(input, clazz)

    }



}

Copy the code

The Route annotation is used here to define how SerializationService is serialized, which is used when withObject is used, as we’ll see later.

  • The Route annotation defines the global degrade policy
@Route(path = "/yourservicegroupname/DegradeServiceImpl")

class DegradeServiceImpl : DegradeService {

    override fun onLost(context: Context, postcard: Postcard) {

        Log.d("DegradeServiceImpl"."No route address found:${postcard.path}")

    }



    override fun init(context: Context?) {

    }



}

Copy the code

The Route annotation is used to define the global degradation policy, that is, when the routing table is not found, the corresponding processing.

  • The Route annotation implements the service
interface HelloService:IProvider{

    fun sayHello(name:String):String

}



// Implement the interface

@Route(path = "/common/hello", name = Test Services)

class HelloServiceImpl : HelloService {

    override fun sayHello(name: String): String {

        Log.d("HelloServiceImpl"."hello, $name")

        return "hello, $name"

    }



    override fun init(context: Context) {}

}

Copy the code

This is an example of how to provide a service through the Route annotation. How to receive a service through the Route annotation will be covered in another annotation below.

3.2 @ Interceptor annotation

This Interceptor annotation is very powerful. It intercepts your route, allowing you to control when it passes and when it doesn’t. For example, I have a requirement that WHEN I jump to share, I want to check whether I have logged in or not. If I have not logged in, I will do the login operation. If you had to determine whether you had logged in at every route point before, it would be tedious. With route interceptors, you don’t have to determine where to jump.

@Interceptor(priority = 8, name = "Login interception")

class LoginInterceptor : IInterceptor {

    override fun process(postcard: Postcard, callback: InterceptorCallback) {

        val path = postcard.path

        if (path == "/share/shareActivity") {

            val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()

            if (TextUtils.isEmpty(userInfo)) {

                callback.onInterrupt(Throwable("Not logged in yet. Log in."))

            } else {

                callback.onContinue(postcard)

            }

        }else{

            callback.onContinue(postcard)

        }

    }



    override fun init(context: Context?) {

// Interceptor initialization will call this method when the SDK initializes, but only once

        Log.d("LoginInterceptor"."LoginInterceptor initialized")

    }



}

Copy the code

Interceptors can define priorities, and if there are more than one interceptor, the interceptors are executed in sequence.

3.3 the @autowired annotation

Autowired annotations are defined on the properties of the target page and are usually used to define the values received by the target page, as well as the receiving server mentioned above:

3.3.1Autowired annotations receive values
@Autowired(name = "username")

lateinit var username: String

@Autowired

lateinit var testBean: TestBean

@Autowired(name ="listBean" )

lateinit var listBean: List<TestBean>

Copy the code

The above defines the reception of values for basic types, as well as the reception of custom beans and collections

3.3.2Autowired annotations receive services
@Autowired

lateinit var helloService: HelloService

@Autowired(name = "/common/hello")

lateinit var helloService1: HelloService

lateinit var helloService2: HelloService

lateinit var helloService3: HelloService

Copy the code

As you can see, helloService can point directly to HelloServiceImpl. If helloService has multiple services, the Autowired annotation needs to specify the name routing attribute, which service instance it is. HelloService2 and helloService3 don’t use the @AutoWired annotation to define the source of the service. Don’t worry, the source of the service will be provided below:

helloService2 =

    ARouter.getInstance().build("/common/hello").navigation() as HelloService

helloService3 = ARouter.getInstance().navigation(HelloService::class.java)



// Use the service

helloService.sayHello("helloService")

helloService1.sayHello("helloService1")

helloService2.sayHello("helloService2")

helloService3.sayHello("helloService3")

Copy the code

HelloService2 specifies the route address via build, helloService3 specifies the class of HelloService via navigation, and HelloServiceImpl specifies the service.

The above @autowired annotation uses ARouter.getInstance().inject(this) in class initialization, otherwise the @Autowired annotation will not be implemented

3.3 Pre-processing Service

By implementing PretreatmentService interface, for example, IF I want to interfere with the login before sharing, if there is no login, I will determine the logic by myself:

@Route(path = "/yourservicegroupname/pretreatmentService")

class PretreatmentServiceImpl : PretreatmentService {

    override fun onPretreatment(context: Context, postcard: Postcard): Boolean {

        if (postcard.path == "/share/ShareActivity") {

            val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()

            if (TextUtils.isEmpty(userInfo)) {

                Toast.makeText(ArouterApplication.application, "You're not logged in yet.", Toast.LENGTH_SHORT).show()

                return false// Preprocess the jump. If you need to process the jump yourself, this method returnsfalseCan be

            }

        }

        return true

    }



    override fun init(context: Context) {}

}

Copy the code

In this example, the interceptor function is the same as the preprocessor function, but the preprocessor function predates the interceptor, and we will analyze the specific differences when we analyze the source code.

3.4 Redefining URL Forward

Redirect to the URL

// Implement the PathReplaceService interface and add an arbitrary annotation to the Path content

@Route(path = "/yourservicegroupname/pathReplaceService") // Must be annotated

class PathReplaceServiceImpl : PathReplaceService {

/ * *

     * For normal path.

     *

     * @param path raw path

* /

    override fun forString(path: String): String {

        if (path == "/login/loginActivity") {

            return "/share/shareActivity"

        }

        returnPath // Returns the result after processing according to certain rules

    }



/ * *

     * For uri type.

     *

     * @param uri raw uri

* /

override fun forUri(uri: Uri?) : Uri? {

        returnNull // Returns the result after processing according to certain rules

    }



    override fun init(context: Context?) {

    }

}

Copy the code

Above, I changed the login route to the shared route. In the actual project, let’s see if there are any applicable scenarios.

4. Initiate a route

  • Let’s start with the simplest way:
ARouter.getInstance().build("/test/activity").navigation()

Copy the code

The postCard object is generated through the Build method, and finally the postCard’s navigation method is called.

  • Writing method of passing value:
ARouter.getInstance().build("/test/1")

            .withLong("key1", 666L)

            .withString("key3"."888")

            .withObject("key4", new Test("Jack"."Rose"))

            .navigation()

Copy the code

The withObject method is used because it defines the route class for JsonServiceImpl serialization. WithObejct can also transmit sets, maps, etc. :

ARouter.getInstance().build("/share/shareActivity").withString("username"."zhangsan")

    .withObject("testBean", TestBean("lisi", 20))

    .withObject(

        "listBean".

        listOf<TestBean>(TestBean("wanger", 20), TestBean("xiaoming", 20))

    )

    .navigation()

Copy the code

Note here that when defining the receiving list and map in the routing target class, the receiving object should not be marked with the specific implementation class type. It should only be marked with list or Map, otherwise it will affect the judgment of the type in serialization. Other similar cases need to be handled with other serialization methods. How to check out the postCard with**


  • Jump writing: jump method mainly refers tonavigationMethod is not exactly a jump method, because it is not only a jump method, for example, to generate an interceptor, service, etcnavigationMethod, which will be covered in the next section when we introduce the source codenavigationWhat are the specific effects

NavigationCallback (NavigationCallback) {NavigationCallback (NavigationCallback);

ARouter.getInstance().build("/share/shareActivity").withString("username"."zhangsan")

    .withObject("testBean", TestBean("lisi", 20))

    .withObject(

        "listBean".

        listOf<TestBean>(TestBean("wanger", 20), TestBean("xiaoming", 20))

    )

    .navigation(this, object : NavigationCallback {

        override fun onLost(postcard: Postcard?) {

        }

        override fun onFound(postcard: Postcard?) {

        }

        override fun onInterrupt(postcard: Postcard?) {

            Log.d("LoginActivity"."Not logged in yet.")

        }

        override fun onArrival(postcard: Postcard?) {

        }

    })

Copy the code

OnLost means the route was not found, onFound means the route was found, onInterrupt means the route was suspended, the default timeout time of the route is set to 300s, onArrival means the callback of the successful route jump, currently only in startActivity callback, This will be covered later in the source section.

5. Confused

There is nothing left to say about the obfuscation part, since Arouter is the annotation class that created Arouter by reflection, so most of the obfuscation needs to be added:

-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

6. Use Gradle to load the routing table automatically

It can be said that although this function is an option configuration, it has a great effect on the optimization of arouter startup. It takes more than 4 seconds for our project to initialize the SDK without using the Gradle auto-loading routing plug-in, which consumes almost no time after using this plug-in. In the next chapter, we will use the decompiler tool to see how it inserts code. Traditionally, it filters arouter annotation classes by scanning the dex file to add them to the map.

  • The specific use
// Build. Gradle for module of app

apply plugin: 'com.alibaba.arouter'

// Build. Gradle for the project

buildscript {

    repositories {

        jcenter()

    }



    dependencies {

        classpath "Com. Alibaba: arouter - register: 1.0.2"

    }

}

Copy the code

7. Use IDE plug-ins to navigate to target classes

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 start of the jump code and click on it to jump to the target class that identifies the path in the code. Currently kotlin’s icon style is not supported. Try Java jump yourself

The sample code

  • More documentation can be found here on arouter’s website