Tencent interview: How do components communicate? Reveal ARouter source code parsing

This column focuses on sharing the knowledge of large-scale Bat interview, and it will be updated continuously in the future. If you like it, please click a follow

Interviewer: ever using a modular, modular communication, how to do ARouter a useful Psychoanalysis: componentization general often got in architecture, modular content is more, with the development of our usual MVC application of monomer, componentization is operations team, job seekers need to design complex components of communication and interaction: From the origin of componentization, advantages and disadvantages, finally leads to componentization disadvantage, component communication

1 componentization

1.1 Original intention of componentization
  • As the APP version continues to iterate and new functions continue to be added, the business will become more and more complex and the maintenance cost will be high.
  • The business coupling degree is high, the code is becoming more and more bloated, and it is difficult for the team to cooperate and develop.
  • In Android projects, the computer will be very slow when compiling the code, and because of the serious coupling of the code in a single project, every modification of the code has to be recompiled and packaged tests, which makes it very time-consuming.
  • Easy unit testing, changing a single business module without focusing on other modules being affected.
1.2 What is componentization

Componentization is to divide an app into multiple modules, as shown in the figure below. Each Module is a component (or can be a basic library for components to rely on). During the development process, we can debug some components separately, and components do not need to depend on each other, but can call each other. When the final release is made, all components are lib dependent by the main App project and packaged into an APK.

1.3 Advantages of componentization
  • Componentalization is the separation of common modules, unified management, to improve reuse, split the page into smaller granularity of components, components in addition to UI implementation, also contains the data layer and logic layer.
  • Each project can compile independently, speed up compilation, and package independently.
  • Modifications made within each project do not affect other projects.
  • The business library project can be quickly broken down and integrated into other apps.
  • The business module with frequent iteration adopts the component mode. The business line research and development can not interfere with each other, improve collaboration efficiency, control product quality and strengthen stability.
  • In parallel development, team members only focus on small modules of their own development, which reduces coupling and facilitates later maintenance.

2 Componentized communication

2.1 Componentized communication

Componentization is not directly dependent on each other, and it cannot be done if component A wants to call component B’s methods. Many developers abandon componentization because of the complexity of communication between componentization

Components communicate in the following ways:

1. Local broadcast

Local broadcast, LoacalBroadcastRecevier. It is more used to communicate with components specified by different systems within the same application. The advantages lie in that the broadcast sent will only be transmitted within its own APP and will not be leaked to other apps. Other Apps cannot send broadcast to their own APP and need not be interfered by other apps. Local broadcasting is like intercom communication, with low cost and high efficiency. However, it has a disadvantage that the communication mechanism of both is entirely entrusted to the system, so we cannot intervene in any step of transmission and cannot control it. Generally, it is not used in a high proportion in the process of component communication.

2. Inter-process AIDL

Interprocess AIDL. Besides, AIDL communication is also system-level communication with Binder mechanism at the bottom. Although Android provides templates for us to realize, it is often difficult for users to understand, complicated interaction, and often not suitable for application in componentized communication.

3. Anonymous memory sharing

Anonymous memory sharing. For example, Sharedpreferences, in multi-threaded scenarios, are often thread unsafe, which is more about storing information that changes little, such as configuration information in the component, and so on

4. Intent Bundle

Intent Bundle delivery. Include explicit and implicit, explicit transfer need to clear the path of the package name, component and component is often need to rely on each other, the deviation of componentization SOP (the principle of separation of concerns), if go recessive, package name path cannot be repeated, not only need to define a set of rules, there is only one package name path error, screen is a bit of trouble, This approach tends to be more appropriate for internal communication between components than for external interaction with other components.

2.2 One of the current mainstream practices is to introduce a third party, such as the Base Module in the figure.




3 ARouter component communication framework

ARouter is a middleware that provides routing functions for pages and services in Alibaba’s open source Android platform. It advocates simplicity and is sufficient. Mainly used for componentized communication

GitHub:github.com/alibaba/ARo…

preface

        Intent intent = new Intent(mContext, XxxActivity.class);
        intent.putExtra("key"."value");
        startActivity(intent);
        
        Intent intent = new Intent(mContext, XxxActivity.class);
        intent.putExtra("key"."value");
        startActivityForResult(intent, 666);
Copy the code

The above code, in Android development, the most common and most commonly used function is page jump, we often need to face the need to jump from the browser or other apps to their own App page, but even a simple page jump, as time goes by, will also encounter some problems:

  • Centralized URL management: Androidmanifest.xml is loaded with IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter IntentFilter Multiple paths need to be solved frequently, such as overlapping paths, excessive activities exported, and security risks
  • Poor configurability: Manifest is limited to XML, cumbersome to write, complex to configure, and has few things to customize
  • Not interfering in the jump process: Jump directly with Intent, the developer can not interfere in the jump process, some aspects of the implementation is difficult, such as login, buried point is very common logic, it is not reasonable to determine in each child page, after all, the activity has been instantiated
  • No explicit dependencies across modules: In App small size, we will do to the App level, according to the business split into sub modules, completely decoupling between, through the packaging process control function of the App, so convenient to deal with big team collaboration, logic without interfering with each other, this time can only rely on implicit Intent to jump, write trouble, success is difficult to control.

In order to solve the above problems, we need a routing component that can be decoupled, simple, multi-functional, highly customized, and supports interception logic: We chose ARouter of Alibaba, and took the opportunity to directly post ARouter’s Chinese description:

3.2 ARouter advantage

Learn about its advantages from ARouter Github:

Support direct parsing standard URL jump, and automatic injection of parameters to the target page support multi-module project use support to add multiple interceptors, custom interception order support dependency injection, Can be used as a dependency injection framework alone support InstantRun Support MultiDex(Google solution) mapping by group classification, multi-level management, On-demand initialization supports user-specified global degradation and local degradation policies pages, interceptors, services and other components are automatically registered to the framework support multiple ways to configure transition animation support access to Fragment support Kotlin and mix typical applications:

From the external URL mapping to the internal page, as well as parameter transfer and parsing cross-module page jump, module decoupling intercepts the jump process, processing login, buried point and other logic

Cross-module API calls do component decoupling through control inversion

Typical application scenarios

  • Mapping from external urls to internal pages, and parameter passing and parsing
  • Page hopping across modules, decoupling between modules
  • Intercept jump process, processing logics such as login and buried point
  • Cross-module API calls, decoupling between modules (in the form of registered ARouter services, calling each other through interfaces)

4. Basic functions

1. Add dependencies and configurations

apply plugin: 'com.neenbedankt.android-apt'
Copy the code
 buildscript {
     repositories {
         jcenter()
     }
     dependencies {
         classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.4'
     }
 }

 apt {
     arguments {
         moduleName project.getName();
     }
 }

 dependencies {
     apt 'com.alibaba:arouter-compiler:x.x.x'
     compile 'com.alibaba:arouter-api:x.x.x'. }Copy the code

2. Add annotations

@route (path =) @route (path =) @route (path =) @route (path ="/test/1")
 public class YourActivity extend Activity {
     ...
 }
Copy the code

3. Initialize the SDK

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

4. Initiate routing operations

// 1. A simple in-app jump (through the URL to jump in'Intermediate use') ARouter. GetInstance (). Build ("/test/1").navigation(); Arouter.getinstance ().build()"/test/1")
             .withLong("key1", 666L)
             .withString("key3"."888")
             .navigation();
Copy the code

5. Add obfuscation rules (if using Proguard)

-keep public class com.alibaba.android.arouter.routes.**{*; }Copy the code

Five, advanced usage

1. Go to the URL

// Create a new Activity to listen for Schame events // Listen for Schame events and pass them directly to ARouter // You can also do some custom gameplay, such as changing the URL // http://www.example.com/test/1 public class SchameFilterActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Uri = getIntent().getData(); Arouter.getinstance ().build(uri).navigation(); finish(); // Androidmanifest.xml reference configuration <activity android:name=".activity.SchameFilterActivity"> <! -- Schame --> <intent-filter> <data android:host="m.aliyun.com"
                 android:scheme="arouter"/>

             <action android:name="android.intent.action.VIEW"/>

             <category android:name="android.intent.category.DEFAULT"/>
             <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> <! -- App Links --> <intent-filter android:autoVerify="true">
             <action android:name="android.intent.action.VIEW"/>

             <category android:name="android.intent.category.DEFAULT"/>
             <category android:name="android.intent.category.BROWSABLE"/>

             <data
                 android:host="m.aliyun.com"
                 android:scheme="http"/>
             <data
                 android:host="m.aliyun.com"
                 android:scheme="https"/>
         </intent-filter>
 </activity>
Copy the code

2. Use ARouter to help resolve parameter types

// If you want ARouter to help parse the parameters (stored in the Bundle by type) // just add @param annotation @route (path =) to the parameters that need to be parsed"/test/1"Public class Test1Activity extends Activity {@param extends Activity {@param extends Activity {@param extends Activity; @Param private int age; @Param(name ="girl"Private Boolean boy; private Boolean boy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); name = getIntent().getStringExtra("name");
         age = getIntent().getIntExtra("age", 1); boy = getIntent().getBooleanExtra("girl".false); // Note: after using the mapping, fetch from Girl, not boy}}Copy the code

3. Enable automatic injection of ARouter parameters (experimental function, not recommended, under development protection policy)

/ / the first rewrite attachBaseContext method in the Application, and join ARouter. AttachBaseContext (); @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); ARouter.attachBaseContext(); } // Enable automatic injection of ARouter. EnableAutoInject (); // At this point, properties in the Activity will be automatically injected by ARouter without getIntent().getStringExtra("xxx"), etc.Copy the code

4. Declare the interceptor (intercepts the jump process and does things in the section)

A typical application is to handle login events during a jump, so there is no need to repeat login checks on the target page. Interceptors are executed between jumps, and multiple interceptors are executed in order of priority (priority = 666, name =)"Test interceptor") public class TestInterceptor implements IInterceptor { /** * The operation of this interceptor. * * @param postcard meta * @param callback cb */ @Override public void process(Postcard postcard, InterceptorCallback callback) { ... callback.onContinue(postcard); // Callback. OnInterrupt (new RuntimeException()"I think there's something unusual.")); } /** * Do your init workin this method, it well be call when processor has been load.
      *
      * @param context ctx
      */
     @Override
     public void init(Context context) {

     }
 }
Copy the code

5. Process the jump result

Arouter.getinstance ().build()"/test/1").navigation(this, new NavigationCallback() { @Override public void onFound(Postcard postcard) { ... } @Override public void onLost(Postcard postcard) { ... }});Copy the code

6. Customize the global degrade policy

// Achieve a general assist for eservice and add a Path annotation @Route(Path ="/xxx/xxx"Public Class DegradeServiceImpl implements survival eservice {/** * Router has lost. ** @param postcard Meta */ @Override public void onLost(Context context, Postcard postcard) { //do something.
       }

       /**
        * Do your init work in this method, it well be call when processor has been load.
        *
        * @param context ctx
        */
       @Override
       public void init(Context context) {

       }
     }
Copy the code

7. Declare more information for the target page

// We often need to configure some properties in the target page, for example"Do I need to log in?"The extras attribute in the Route annotation is an int. In other words, a single int has 4 bytes, or 32 bits, and can be configured with 32 switches. 32 switches can be identified by byte manipulation @route (path ="/test/1", extras = Consts.XXXX)
Copy the code

8. Manage services with ARouter (I) Expose services

Public interface IService extends IProvider {String Hello (String name); } /** ** / @route (path ="/service/1", name = Test Services)
 public class ServiceImpl implements IService {

     @Override
     public String hello(String name) {
         return "hello, " + name;
     }

     /**
      * Do your init work in this method, it well be call when processor has been load.
      *
      * @param context ctx
      */
     @Override
     public void init(Context context) {

     }
 }
Copy the code

9. Manage services with ARouter (2) Discover services

ByType IService Service = arouter.getInstance ().navigation(IService. Class); // ByType IService service = (IService) ARouter.getInstance().build("/service/1").navigation(); //  ByName
 service.hello("zz"); 2. Note: It is recommended to use ByName to get the Service. ByType is easier to write, but if there are multiple implementations, the SDK does not guarantee that you will get the implementation you wantCopy the code

10. Manage dependencies with ARouter

You can wrap your business logic or SDK with ARouter Service and initialize your SDK in the init method of the Service. Different SDKS use ARouter’s service to make calls. Each service is initialized the first time it is used. Call the init method.

In this way, we can say goodbye to all kinds of messy dependencies. As long as we can call this service, the SDK contained in this service has already been initialized, and we do not need to care about the initialization order of each SDK.

Six, more functions

1. Other Settings during initialization

ARouter.openLog(); // Enable the log arouter.printStackTrace (); // Print the thread stack when printing the logCopy the code

2. Detailed API description

// Build the standard routing request arouter.getInstance ().build("/home/main").navigation(); // Build the standard routing request and specify the grouping arouter.getInstance ().build("/home/main"."ap").navigation(); // Build a standard routing request that resolves the Uri Uri directly through the Uri; ARouter.getInstance().build(uri).navigation(); StartActivityForResult // Navigation must start with Activity and start with RequestCode arouter.getInstance ().build()"/home/main"."ap").navigation(this, 5); Params = new Bundle(); ARouter.getInstance() .build("/home/main") .with(params) .navigation(); // specify Flag ARouter. GetInstance ().build("/home/main") .withFlags(); .navigation(); Arouter.getinstance ().build();"/home/main") .getExtra(); // Use the green channel (skip all interceptors) arouter.getInstance ().build("/home/main").greenChannal().navigation();
Copy the code

The appendix

Demo apk

  • Latest version arouter-Annotation: 1.0.0 arouter-Compiler: 1.0.1 Arouter-API: 1.0.2
  • Gradle rely on
dependencies {
    apt 'com. Alibaba: arouter - compiler: 1.0.1'
    compile 'com. Alibaba: arouter - API: 1.0.2'
}
Copy the code

About me

For more information, you can click about me. I really hope to communicate with you and make progress together. Currently, I am a programmer.