1. What are modularization, componentization and plug-in

Iteration as the accumulation of the business, products, we write engineering will be more and more big, also more and more bloated, more difficult to maintain, it have a kind of method, can make everyone responsible for their own business module, use do each module directly assembled together, so that the code is more flexible and mutual coupling are lower, Reuse can also be greater. So the idea of modularity comes into play.

To put it simply, modularity is the breaking up of a program according to its function into independent modules so that each module contains only what is relevant to its function. Module we are relatively familiar with, for example, the login function can be a module, the search function can be a module, the car transmitter can also be a module.

From individual understanding of course, modularity is a kind of thought only, it is big change small, separate management, how to carry out concretely in actual project, there are two kinds of plans at present, one is componentization change, one is plug-in change

I found a very graphic picture on the Internet

  • Componentization scheme is to combine several independent sub-modules into a whole to reduce the coupling between modules. These sub-modules can run independently under certain conditions. The main module does not fail because of the absence of any submodules. Components can be flexibly built among them.

    Similar to building blocks, assembly and combination, easy to maintain

  • Plug-in scheme is: a program auxiliary or extension function module, plug-in is not necessary for the program, but it can give the program some additional functions.

    For example, today’s applications rely more on some third-party libraries, such as map SDK, sharing SDK, payment SDK, etc., which leads to the installation package becoming bigger and bigger. Depending on these SDKS alone, the installation package may increase the size of 10-20m.

    When new features are needed, the entire installation package has to be updated. Peruse the above definition again, you know its purpose and role, that is, some additional functions, when needed, can be flexibly added, dynamic loading. The main solution of plug-in is to reduce the size of the application, do not install the extension function, when the corresponding function needs to load the corresponding module

2. The difference between plugins and

The difference is easy to understand based on what they are used for: componentization does not have the ability to dynamically add or modify components at run time, but pluginization does

3. Componentized practice plan

Speaking of componentized practice program, only a small poem describes, through all kinds of forums, see the end of time, the original most suitable program ah, beside

In a word: Various options, nor the lack of a lot of writing good, but with the commercial development is given priority to, the following introduce one of the most appropriate, that’s a ARouter alibaba out, it is simple and easy to use, it supports multiple module project, it is stronger, it supports intercepting logic, and many other advantages, will write next ali the framework of the development in the future, which is convenient to use. If you’re interested, you can wait for my next blog post about how it works in practice.

4. Start pumping

1. First, look at engineering

Is an e-commerce, there are three components, one is the home page, one is the shopping cart, one is the personal center, three independent modules

2. Do some prep

Since each module should be able to be debugged separately, we first define the switch of each module to set whether the module should be debugged separately

  1. In the build.gradle project directory, define three variables
buildscript { 
    ext.kotlin_version = '1.3.31'

    ext {
        isRunHome = true // true: The Home module can be run separately
        isRunPersonalcenter = true
        isRunShopingcar = true
    }
Copy the code
  1. In a submodule, such as the Home module, set build.gradle
if(isRunHome.toBoolean()){ // 1. Determine whether to run isRunHome independently based on the preset isRunHome
    apply plugin: 'com.android.application'
}else {
    apply plugin: 'com.android.library'
}

android {
    android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"

    defaultConfig {

        if(isRunHome.toBoolean()){ // 2. Add applicationId if it works

            applicationId "com.bj.home"
        }
Copy the code
  1. Set its build.gradle in the main module (app module)
dependencies {

    if(! isRunHome.toBoolean()){// 1. If you want to run it independently, the main project does not load it
        implementation project(path: ':home')
    }
    implementation project(path: ':personalcenter')
    implementation project(path: ':shoppingcar')
Copy the code

That’s what happens when you compile

4. Of course, there is still a step to set up the Androidmanifest.xml file, because generally speaking, an APP only has one start page, when the component is debutted separately also needs a start page, so we need to set up two files. In this way

AndroidManifest, like ApplicationId, can be configured in build.gradle, so we can also dynamically modify the values of Boolean variables defined when dynamically configuring component project types. We need to modify the build.gradle file for submodules such as home.

android {
...
  
    sourceSets {

        main {
            // 1. Use different Androidmanifest.xml files for separate and integrated debugging
            // We can also configure different Java source code, different resource files, etc according to different projects
            if(isRunHome.toBoolean()) {

                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            } else{
                manifest.srcFile 'src/main/AndroidManifest.xml'}}}}Copy the code

Build. Gradle file (root directory) : build.gradle file (root directory) : build.gradle You can’t use class references to pass data between components.




3. Integrate Ali’s routing framework ARouter

The solution is to integrate alibaba’s routing framework ARouter, a framework designed to help componentalize Android apps — supporting routing, communication and decoupling between modules

3.1 Adding a Dependency

1. Add a dependency on ARouter to each module, of course, create a base module by itself, add the dependency to base, other modules can reference it.

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName: project.getName()]
            }
        }
    }
Copy the code
dependencies {
    compile 'com. Alibaba: arouter - API: 1.2.1.1'
    annotationProcessor 'com. Alibaba: arouter - compiler: 1.1.2.1'. }Copy the code

Ok, configuration done

3.2 Initializing the SDK

We’re going to initialize it in our custom MyApplication

 @Override
    public void onCreate(a) {
        super.onCreate();

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

            // Prints logs
            ARouter.openLog();

            // 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.openDebug();
        }
        // Initialize ARouter
        ARouter.init(this);
    }

    private boolean isDebug(a) {

        return BuildConfig.DEBUG;
    }
Copy the code




3.3 the Activity to jump

1. Annotate the target Activity with Route (home: HomeAty)

/ * * * * * homepage module which path is the path of the jump, the path to note here is need to have at least two level, xx/xx / * * /
@Route(path = "/home/HomeAty")
public class HomeAty extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); setContentView(R.layout.aty_home); }}Copy the code

2 Page jump (APP: MainActivity)

    @Override
    public void onClick(View view) {

        switch (view.getId()){

            // Jump to the Activity page
            case R.id.btn_go_home:

                ARouter.getInstance().build("/home/HomeAty").navigation();
                break; }}Copy the code




3.4 jump ForResult

  1. Page jump and return (APP: MainActivity)
 @Override
    public void onClick(View view) {

        switch (view.getId()){

            ...

            // Jump to the Activity page and return data
            case R.id.btn_go_aty_forresult:

                ARouter.getInstance().build("/home/HomeResultAty").navigation(this.897);
                break; }}@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == 897 && resultCode == 999){

            String msg = data.getStringExtra("msg"); tv_msg.setText(msg); }}Copy the code
  1. Target Activity(Home: HomeResultAty)
@Route(path = "/home/HomeResultAty")
public class HomeResultAty extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aty_home_result);

        findViewById(R.id.btn_goback).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {

                Intent in = new Intent();
                in.putExtra("msg"."Data returned from the home module");
                setResult(999, in); finish(); }}); }}Copy the code




3.5 get fragments

  1. Get fragment(app: MainActivity)
  Fragment mFragment = (Fragment) ARouter.getInstance().build("/home/HomeFragment").navigation();
                getSupportFragmentManager().beginTransaction().replace(R.id.fl, mFragment).commit();
Copy the code

2. Add annotations to fragment (home: HomeFrag)

@Route(path = "/home/HomeFragment")
public class HomeFrag extends Fragment {... }Copy the code




3.6 In-application Jump with Parameters

  // Jump with parameters
  case R.id.btn_go_home_byArgs:

      ARouter.getInstance().build("/home/arg")
                    .withString("msg"."5")
                    .withDouble("msg2".6.0)
                    .navigation();
      break;
Copy the code

2. Target Activity(Home: HomeByArgAty)

@Route(path = "/home/arg")
public class HomeByArgAty extends Activity {

    @Autowired(name = "msg")
    String arg1;

    @Autowired
    String arg2;

    private TextView tv_msg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aty_home_arg);

        // If you use Autowired annotations, you need to add the code below
        GetIntent ().getStringExtra("")
        ARouter.getInstance().inject(this);

        tv_msg = findViewById(R.id.tv_msg);
        tv_msg.setText("Parameters passed from main project:"+arg1); }}Copy the code

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — advanced usage — — — — — — — — — — — — — — — — — — — — — — — — — — — — — –

3.7 the interceptor

ARouter also added an interceptor mode. Interceptors can be used for a variety of purposes, such as checking whether the user is logged in when routing to the target page, checking whether the user’s rights are satisfied, and routing to the appropriate login or routing interface if not. ARouter’s Interceptor is a bit of a weirdo. You only need to implement the IInterceptor interface and use the @interceptor annotation. Of course, this also has its disadvantages, that is, every time after the route, it will be intercepted by the interceptor, obviously so that the program will run less efficiently. Interceptor can be defined in several ways, such as login check Interceptor, permission check Interceptor, etc. The Interceptor priority is defined in terms of priority. The higher the priority is, the earlier the Interceptor is executed. The interceptor uses callback.oncontiune ()/ callback.oninterrupt () internally. The former indicates that the interceptor task is complete and the route continues. The latter terminates the route. Example:

1. Implement IInterceptor interface, custom interceptor, detect all jump, as long as the URI is empty intercept, can also add content in the request

@Interceptor(priority = 4)
public class LoginInterceptor implements IInterceptor {

    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {

        String uri = postcard.getExtras().getString("uri");
        if(TextUtils.isEmpty(uri)){

            Log.i("lybj"."Uri is empty, interrupt route");
            callback.onInterrupt(null);
        }else {

            Log.i("lybj"."Interceptor executing, URI not null, continue executing.");
            postcard.withString("msg"."You can add anything you want."); callback.onContinue(postcard); }}@Override
    public void init(Context context) {}}Copy the code
  1. A normal page jump
    // Interceptor test
    case R.id.btn_test_interceptor:

          ARouter.getInstance().build("/home/web")
                 .withString("uri"."file:///android_asset/schame-test.html")
                 .navigation();
        break;
Copy the code

3. Target interface

@Route(path = "/home/web")
public class WebAty extends Activity {

    private WebView wv_web;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aty_web);

        Log.i("lybj", getIntent().getStringExtra("msg"));
        String uri = getIntent().getStringExtra("uri"); wv_web = findViewById(R.id.wv_web); wv_web.loadUrl(uri); }}Copy the code




4. ARouter pit

ARouter::Compiler >>> No module name, for more information, look at Gradle log.

Add the following code to all model build.gradle references

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

Copy the code

or

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

The names of AROUTER_MODULE_NAME and moduleName are chosen for different versions. The above code must ensure that all modules are added to make the distinction

2. The resource name is the same

The layout name of the two models is the same. The main project is always in trouble when loading, so try to make sure that the resource name of each model is prefixed.

5. Code download

Click here