Since componentization is the trend of Android development, even if you don’t need the volume of your project, you should learn something about it, at least through componentization you can have a deeper understanding of some principles of design patterns.

Componentization is to decouple the modules with better business independence and realize independent operation and compilation, so that collaborative development can be achieved, development efficiency can be improved, and the risk of introducing bugs to other modules due to code modification can be reduced.

However, since components are completely decoupled, how should they communicate? Traditionally, they communicate with each other through intents. What about routing

And then the main character, Arouter.

It is an open source component of Alibaba, which is used for communication between components in componentized projects to realize decoupling.

I’m going to talk a little bit about how to configure it, talk about the pits that you encounter in the configuration, and then talk about how to use it.

In addition to some basic configuration and use, of course, there are some advanced use methods, routing communication has a comprehensive summary.

1. Configure the Arouter route

In build.gradle defaultConfig

javaCompileOptions {
    annotationProcessorOptions {
        arguments = [AROUTER_MODULE_NAME: project.getName()]
    }
}
Copy the code

If not configured, the following error is reported:

java.lang.RuntimeException: ARouter::Compiler >>> No module name, for more information
Copy the code

Also rely on:

annotationProcessor 'com. Alibaba: arouter - compiler: 1.5.1'
Copy the code

Otherwise, it cannot be used. In addition to the above two configurations, the actual interface configuration is as follows:

api('com. Alibaba: arouter - API: 1.5.1'){
    exclude module: 'support-v4'
}
Copy the code

According to their own needs, check some duplicate libraries, my project V4 repeated, so to exclude.

It’s important to know which dependencies can be placed in the public library and which ones can’t, and Arouter is a really nice thing to do, but Arouter -API can be placed in the public library, using the API, so you don’t have to rely on each module, whereas these two must depend on each module, otherwise you get an error.

2. Pit in Arouter configuration

  • Pit 1: As long as you want to use Arouter’s component, you must rely on it in the component
annotationProcessor 'com. Alibaba: arouter - compiler: 1.5.1'
Copy the code

Otherwise, an error will be reported.

  • Pit 2: clear the cache, I had a problem with the configuration at the beginning, the configuration was successful, but it was still not match, I wasted half a day, so I cleared the cache, oh my god.

  • You need to add Android. enableJetifier=true in gradle.properties when you encounter the following error

ARouter::Compiler An exception is encountered, [null]
Copy the code
  • Pit 4:

The layout file names in each component must be different. Otherwise, the layout file will be considered the same.

3.Arouter basic use

Now that the basic configuration is finished, are you looking forward to using it, wondering what role it plays in the componentization, to meet our componentization needs?

We need to initialize Arouter. The website says that the earlier the better, because initialization is time-consuming, so we usually initialize it in the application.

if (isDebug()) {// These two lines must be written before init, otherwise these configurations will be invalid during init
    ARouter.openLog();// Prints logs
    ARouter.openDebug();// Enable debug mode (if running in InstantRun mode, you must enable debug mode! The online version needs to be closed, otherwise there are security risks)
}
ARouter.init(mApplication); // As early as possible, it is recommended to initialize in the Application
Copy the code
  • Route Usage

The use method is quite simple, through the way of annotation, annotation keyword is Route, specific use is as follows:

First, you need to register the activity, fragment, or service you want to start, specify a unique path, if duplicate, not match error.

@Route(path = ArouterPath.COMPONENT_USER_ABOUT_ACTIVITY)
public class About extends Activity {
……
}
Copy the code

After you register, you have your address. People can access you through your path. How do you access it?

Step 2: Access through the registered path

ARouter.getInstance()
    .build(ArouterPath.COMPONENT_USER_ABOUT_ACTIVITY)
    .navigation();
Copy the code

Module to module communication, not only between the interface to jump, but also to jump, some necessary parameters to the past, Arouter routing is also supported.

So let’s see what we can do with these parameters. Depending on the type of the argument, use a different with method.

ARouter.getInstance().build(ArouterPath.COMPONENT_USER_ABOUT_ACTIVITY)
            .withLong("key1".666L)
            .withString("key3"."888")
            .withObject("key4".new Test("Jack"."Rose"))
            .navigation();
Copy the code

You may be wondering if there are too few parameters to support, and how is that possible? Look at the documentation provided on the website to see how much support is available.

// Build standard routing requests
ARouter.getInstance().build("/home/main").navigation();

// Build standard routing requests and specify groups
ARouter.getInstance().build("/home/main"."ap").navigation();

// Build standard routing requests and parse them directly through the Uri
Uri uri;
ARouter.getInstance().build(uri).navigation();

// Build the standard routing request, startActivityForResult
// The first parameter of navigation must be the Activity and the second parameter must be the RequestCode
ARouter.getInstance().build("/home/main"."ap").navigation(this.5);

// Pass the Bundle directly
Bundle params = new Bundle();
ARouter.getInstance()
    .build("/home/main")
    .with(params)
    .navigation();

/ / specify the Flag
ARouter.getInstance()
    .build("/home/main")
    .withFlags();
    .navigation();

/ / get fragments
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
                    
// Object pass
ARouter.getInstance()
    .withObject("key".new TestObj("Jack"."Rose"))
    .navigation();

// If you don't have enough interfaces, you can just take the Bundle and assign it
ARouter.getInstance()
        .build("/home/main")
        .getExtra();

// Transfer animation (normal way)
ARouter.getInstance()
    .build("/test/activity2")
    .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
    .navigation(this);

// Transfer animation (API16+)
ActivityOptionsCompat compat = ActivityOptionsCompat.
    makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2.0.0);

/ / ps. MakeSceneTransitionAnimation using Shared elements, need to pass into the current Activity in the navigation method

ARouter.getInstance()
    .build("/test/activity2")
    .withOptionsCompat(compat)
    .navigation();
        
// Use the green channel (skip all interceptors)
ARouter.getInstance().build("/home/main").greenChannel().navigation();

// Print logs using your own logging tool
ARouter.setLogger();

// Use your own thread pool
ARouter.setExecutor();
Copy the code

Not only does it support general arguments, but it also supports bundles, data serialization, jump animations, and even fetching an object.

The path format also needs a special mention:

There are two levels of path: group/name. Group is the category of your module, and name is the identifier of your fragment/activity. You can use the class name or not, but it is best to represent them by name.

For example, the path: /login/login_activity, represents the login activity under the login module.

For those of you who are careful, you might say, WELL, I can pass a value through the outer, how do I get that value on the receiving end?

Do you have the previous method, will not feel a little low, but some students are code refactoring, change up the workload is too large, do not want to change line, of course, all support, such as time to change is not late.

But since we’re using routing, let’s use routing to get the value.

Arouter.getinstance ().inject(this) is also required in onCreate, otherwise it will not receive the value.

Let’s look at an example: Sender:

ARouter.getInstance()
            .build(ArouterPath.AROUTER_PATH_LOGIN)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .withString("data"."test")
            .navigation();
Copy the code

Receiver parameters:

@Route(path = ArouterPath.AROUTER_PATH_LOGIN)
public class LoginActivity extends AppCompatActivity {
    @Autowired(name = "data")
    String param;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);// Must register, otherwise param will not get it
        setContentView(R.layout.login_ly);
        Toast.makeText(getApplicationContext(), "param:+"+ param, Toast.LENGTH_LONG).show(); }}Copy the code

You see is not more convenient than the previous scheme, hurry up to action, which parameters to support, the following is the official document:

Declare a field for each parameter and annotate it with @autowired
// You can't pass Parcelable data in the URL. You can pass Parcelable objects through the ARouter API
@Route(path = "/test/activity")
public class Test1Activity extends Activity {
    @Autowired
    public String name;
    @Autowired
    int age;
    
    // Map the different parameters in the URL by name
    @Autowired(name = "girl") 
    boolean boy;
    
    // Support parsing custom objects, using JSON pass in URL
    @Autowired
    TestObj obj;      
    
    // Pass the implementation of List and Map using withObject
    // Serializable interface implementation class (ArrayList/HashMap)
    // The place where the object is received cannot be marked with the specific implementation class type
    // Only List or Map should be marked, otherwise the type in serialization will be affected
    Other similar cases need to be treated in the same way
    @Autowired
    List<TestObj> list;
    @Autowired
    Map<String, List<TestObj>> map;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ARouter.getInstance().inject(this);

    // ARouter will automatically assign a value to the field
    Log.d("param", name + age + boy); }}Copy the code

You may also have a pain point: you want to use a service from another module. What should you do? I’m not going to rely on it, and then the sense of componentization is gone, but you might say, well, I put it in the common library, and that will solve the problem.

This could be a solution, but perhaps considering that the service is strongly related to the module, putting it in the Common violates the principle of the design pattern. Arouter also provides a solution to this problem. See how to use the IProvider.

4. The use of IProvider

Here’s an example of how to use it: We have two modules, Module1 and Module2, and Module1 wants to use Module2’s services. Their common library is commonLib.

1. Create an ILoginProvider in commonlib and inherit the IProvider.

public interface ILoginProvider extends IProvider {
    void userService(a);
}
Copy the code

2. Create a new class that inherits ILoginProvider from the module you need to call the service. That is, implement ILoginProvider in Module2 and register path with @route for other module calls.

@Route(path = ArouterPath.AROUTER_PATH_SERVICE)
public class ILoginProviderImp2 implements ILoginProvider {
    private Context mContext;

    @Override
    public void userService(a) {
        Toast.makeText(mContext, "Perform the services of this module", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void init(Context context) { mContext = context; }}Copy the code

3. How to use it in Module1?

  • Register ILoginProvider via Autowired, which is an interface class, not an implementation class.
  • Arouter.getinstance ().inject(this);
  • Use the service.
public class UserCenterActivity extends AppCompatActivity {

    private ArrayList<Fragment> mFragmentArrayList = new ArrayList<>();

    @Autowired(name = ArouterPath.AROUTER_PATH_SERVICE)
    ILoginProvider mILoginProvider;// Is an interface class, not an implementation class

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.center_activity);
    
        ARouter.getInstance().inject(this);
        mILoginProvider.init(getApplicationContext());
        mILoginProvider.userService();// Use module2 services}}Copy the code

5. The use of IInterceptor

Login is easy to do APP scenarios, but there is a pain point is, not logged in, need to jump to the login interface, with IInterceptor can easily fix.

Create a new global IInterceptor, where navigation will intercept it. You can intercept it based on the address, or if you are not logged in, jump directly to it.

@Interceptor(priority = 2)
public class LoginInterceptorImpl implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        String path = postcard.getPath();
        
        if (ArouterPath.AROUTER_PATH_LOGIN.equals(path)) {
            callback.onInterrupt(null);
        } else{ callback.onContinue(postcard); }}@Override
    public void init(Context context) {}}Copy the code

It’s really fun to use and worth having.

Reference:

Arouter website