preface

I highly recommend reading this article on Android componentization best Practices first

This article is a summary of thinking after reading practice, more inclined to practice steps.

Componentized development architecture

Componentized development is basically this architecture, with the key distinction between base and Base_Custom component content.

There are two concepts involved here: modularization and componentization. The two concepts are essentially the same in terms of reducing code coupling. The difference lies in content.

Componentized development steps

Step 1: Create the component

File — >New — >New Moudle — >Phone & Tablet Moudle

After creation, it is a project, similar to app, which will create related resource files by default. “Business Module Preference”

File – > New – > New Moudle – > Android Library

After creation is a moudle “Component preference”

Step 2: Code

In the process of coding, we need to pay attention to the aggregation of resources and code, or follow the principle of doing your own thing and reduce the coupling.

  • resources

    • The image resource Drawable, which needs to be stored in its own directory, like the common return key, is stored in the base component;

    • Color value color, do not share, not much, write in their own component;

    • The dimension DP, which is basically unchanged, can be placed in the base component;

    • The style style, which refers to the Activity theme, can be defined in the base component and inherited in the component, thus ensuring both the overall app style and specialization.

    • In order to ensure sufficient isolation of resources within the component, we can standardize the name, modular name, such external calls, less prone to error.

      For example, the login module IC_login_phone login_phone_hint_TEXT…

      To be strict, you can configure the named prefix in the build.gradle file so that if you do not code in this format, it will float red. For XML files only, the images need to be properly named themselves.

      android {
          resourcePrefix "login_"// Other configuration... }Copy the code
  • code

    • Common base classes, utility classes, etc. are placed in the base components
    • To prevent external direct references to classes within a component, this can be used when integrating into a projectruntimeOnly
    dependencies {
        ...
        runtimeOnly project(':moudle_shop')}Copy the code

Step 3: Debug the configuration

After the code is written, it needs to be debugged separately or integrated into the APP, so two modes need to be configured.

  • creategradle.propertiesFile that defines properties to distinguish between the two modes
/ / such asfalseRepresents integration debugging,trueDebug isSingleRun =false
Copy the code
  • Configure it in the build.gradle file

    • Configuring project Types
    if(issinglerun.toboolean ()) {// Debug alone should be the project type apply plugin:'com.android.application'
    } else{// Integrate debugging as library integrated into app plugin:'com.android.library'} android{ ... }...Copy the code
    • configurationapplicationId
    Android {defaultConfig {// applicationId needs to be configured for separate testsif (isSingleRun.toBoolean()) {
                applicationId "com.xx.moudle_xx"}}}Copy the code
    • configurationAndroidManifest.xml

    Separate tests require a launch page entry, so the manifest file also needs to be configured

    android{
          sourceSets {
            main {
                if(issinglerun.toboolean ()) {// Create a new manifest folder, store the manifest file, set the start page entry manifest.srcfile'src/main/manifest/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'}}}}Copy the code
    • configurationApplcation

    Each component needs to be initialized. Common components (such as network frames and image frames) can be initialized in the base component. How to initialize some of their own modules? Integration tests do not initialize the component’s Application.

    Here we can define BaseApp 4 in the base component. The dynamic configuration of the component Application

  • At this point, the debugging configuration is complete. The purpose of the configuration is to facilitate the testing of components when they are independently developed and integrated into the app. Test the configuration by setting the isSingleRun property in the gradle.properties file to true.

Step 4: Debug test

  • Set isSingleRun to true, and you can debug separately, just like the app project.

  • Set isSingleRun to false and integrate into your app to integrate your tests.

    Integration testing has a question: How do I jump to an Activity in a component? Need to use Alibaba open source ARouter

    It is recommended to read Android componentization Best Practices iv. Interface hopping between components.

    ARouter Configuration steps:

    Configure build.gradle in the base component

    android {
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [ moduleName : project.getName() ]
                }
            }
        }
    }
    
    dependencies {
        api 'com. Alibaba: arouter - API: 1.3.1'// Arouter - Compiler's annotation dependency requires that all modules that use arouter add a dependency on annotationProcessor'com. Alibaba: arouter - compiler: 1.1.4'
    }
    Copy the code

    Component Configuration

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

    Initialize in app main program Application

    // This break must be written, otherwise it will not take effect, I thought I did not need to print the log, but it took a long time to find the bugif(buildconfig.debug) {// These two lines must be written before init, otherwise these configurations will not be valid during init arouter.openlog (); // Prints the log arouter.openDebug (); // Enable debug mode (if running in InstantRun mode, debug must be enabled! } ARouter. Init (this);Copy the code

    The component in which the Activity is started configures the path, which must start with “/” and have at least two levels

    @Route(path = "/test/test")
    public class TestActivity extends BaseActivity {}
    Copy the code

    The main program starts the Activity in the component

    ARouter.getInstance().build("/test/test").navigation();
    Copy the code
  • This is the end of individual debugging and simple integration debugging, and the next step is more about integration debugging, how to ensure that components in the case of a high degree of convergence are available to each other.

Data transfer between components

The whole idea is to define interfaces in the underlying components through which data is passed between components, completely decoupled.

Thank you

Best practices for Android componentization