Why learn open source frameworks

1. Learn excellent open source framework design ideas 2. Learn excellent open source framework implementation technology 3. Actual handwriting excellent open source framework to deep understanding

The early single layered model

  • Problem 1: No matter what the subcontracting does, as the project grows, the project loses the sense of hierarchy, and the successors rush to take over
  • Problem two: The package name constraint is too weak, a little careless, different business packages will directly call each other, the code is high coupling
  • Problem three: Multi-party development in version management, prone to code coverage conflicts and other problems

All the code is written in different packages within the APP module

1. What is componentization and why is it needed

Meaning of componentization: not interdependent, can interact with each other, arbitrary combination, highly decoupled, free disassembly, free assembly, reuse, stratification independent

At this point: APP is no longer the boss, and the sub-modules are the younger brother

After componentization, all modules are level. Some people say that app module is a shell in componentization. This is why it is called APP shell project

Students think, if there is no componentization, or single:

  • 1. How much work is needed to completely cut down the order sub-module?
  • 2. How to add a submodule (wallet) to the project? .

2. Automatically deploy and configure the integration environment or component environment.

Environment switch and run independently

  • During the development of the project, we must optimize our Gradle files, such as extracting common content
  • During the development of a project, Gradle should be used to configure everything related to the “official environment” and “test environment”

Componentization points to consider:

The principle of

Phone Module and Android Library:

Gradle configuration

1. Disconnect from public administration: manager.gradle

// Groovy language object oriented if for

/ / expanded fast
ext {

    / / now Gradle
    // Formal environment and test environment
    isRelease = false

    // Official and test environment server URL configuration
    url = [
            "debug"  : "https://192.188.22.99/debug"."release": "https://192.188.22.99/release"
    ]

    // Create Map store, key and value are custom
    androidID = [
            compileSdkVersion : 30.buildToolsVersion : "30.0.1".applicationId : "com.derry.derry".minSdkVersion : 16.targetSdkVersion : 30.versionCode : 1.versionName : "1.0".testInstrumentationRunner: "androidx.test.runner.AndroidJUnitRunner"
    ]

    // Create Map store, key and value are custom
    appID = [
            app: "com.derry.modularproject".login: "com.derry.login".register: "com.derry.register"
    ]

    // 300 lines MAP key value
    dependenciesID = [
            "appcompat"       : "Androidx. Appcompat: appcompat: 1.2.0"."constraintlayout": "Androidx. Constraintlayout: constraintlayout: 2.0.1."."material"        : "Com. Google. Android. Material: material: 1.1.0." "."vectordrawable"  : "Androidx. Vectordrawable: vectordrawable: 1.1.0."."fragment"        : "Androidx. Navigation: navigation - fragments: 2.2.2." "."ui"              : "Androidx. Navigation: navigation - UI: 2.2.2." "."extensions"      : "Androidx. Lifecycle: lifecycle - extensions: 2.2.0." "]},Copy the code

2. Build. Gradle of the project

// The build.gradle file in the root directory is imported as a common copy
apply from : 'manager.gradle'

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 4.0.1." "

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
Copy the code

3.app.gradle

apply plugin: 'com.android.application'

println("Derry ---> app Student hao 1")

println "Derry ---> app Student hao 2"

// Complete mode performance
def androidID = rootProject.ext.androidID

android {
    compileSdkVersion androidID.compileSdkVersion
    buildToolsVersion androidID.buildToolsVersion

    defaultConfig {
        applicationId appID.app
        minSdkVersion androidID.minSdkVersion
        targetSdkVersion androidID.targetSdkVersion
        versionCode androidID.versionCode
        versionName androidID.versionName

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        // Make my Java code work
        // Tags for Java code leaks, flags, formal and test environments
        // For componentization and integration
        buildConfigField("boolean"."isRelease", String.valueOf(isRelease))
    }

    buildTypes {
        debug {
            buildConfigField("String"."debug"."\"${url.debug}\"")
        }
        release {
            buildConfigField("String"."release"."\"${url.release}\"")

            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs".include: ["*.jar"])

    / / 300 rows
   / * implementation "androidx appcompat: appcompat: 1.2.0" implementation "androidx. Constraintlayout: constraintlayout: 2.0.1." " Implementation "com. Google. Android. Material: material: 1.1.0" implementation "Androidx. Vectordrawable: vectordrawable: 1.1.0" implementation "androidx. Navigation: navigation - fragments: 2.2.2." " Implementation "androidx navigation: navigation - UI: 2.2.2" implementation "Androidx. Lifecycle: lifecycle - extensions: 2.2.0" * /

    // You can do it in one line
    dependenciesID.each {k,v -> implementation v}

    testImplementation 'junit: junit: 4.12'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.2'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.3.0'

    if (isRelease) {
        // Attach the app shell
        implementation project(':login')
        implementation project(':register')}else {
        // The login register cannot be attached, because the login register can run independently}}Copy the code

4. Library module build.gradle

apply plugin: 'com.android.library'

println "Derry ---> lib Student hao 2"

android {
    compileSdkVersion androidID.compileSdkVersion
    buildToolsVersion androidID.buildToolsVersion

    defaultConfig {
        minSdkVersion androidID.minSdkVersion
        targetSdkVersion androidID.targetSdkVersion
        versionCode androidID.versionCode
        versionName androidID.versionName

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs".include: ["*.jar"])

    / * implementation "androidx appcompat: appcompat: 1.2.0" implementation "androidx. Constraintlayout: constraintlayout: 2.0.1." " Implementation "com. Google. Android. Material: material: 1.1.0" implementation "Androidx. Vectordrawable: vectordrawable: 1.1.0" implementation "androidx. Navigation: navigation - fragments: 2.2.2." " Implementation "androidx navigation: navigation - UI: 2.2.2" implementation "Androidx. Lifecycle: lifecycle - extensions: 2.2.0" * /

    // You can do it in one line
    dependenciesID.each {k,v -> implementation v}

    testImplementation 'junit: junit: 4.12'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.2'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.3.0'

}
Copy the code

5. Build. Gradle for a service module

// apply plugin: 'com.android.application'
if (isRelease) { // For release versions, each module cannot run independently
    apply plugin: 'com.android.library' // The formal environment library cannot run independently
} else {
    apply plugin: 'com.android.application' // The test environment application runs independently
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    defaultConfig {
        // applicationId "" // There are appids that can run independently

        if(! isRelease) {// To run independently, you must have an appID
            applicationId appID.login // Componentized mode can run independently to have applicationId
        }

        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            if(! isRelease) {// In componentized mode, run Debug separately
                manifest.srcFile 'src/main/debug/AndroidManifest.xml' / / to take effect
            } else { // In a formal environment
                // Integrated mode, the whole project package APK
                manifest.srcFile 'src/main/AndroidManifest.xml' // Let the manifest file in our previous default path work again

                java {
                    // Files in the debug directory do not need to be merged into the main project
                    exclude "**/debug/**"
                }
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs".include: ["*.jar"])
    implementation 'androidx. Appcompat: appcompat: 1.2.0'
    implementation 'androidx. Constraintlayout: constraintlayout: 2.0.4'
    testImplementation 'junit: junit: 4.12'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.2'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.3.0'

}
Copy the code

3. Interaction mode of sub-modules in componentization (non-Arouter version).

Means of communication between components

The disadvantages of using EventBus are that the EventBean is expensive to maintain and difficult to manage:

The second method uses the broadcasting method, the disadvantage is: it is not easy to manage, all sent out uniformly

The drawback is that there is too much configuration XML writing in androidmanifest.xml

Method 4 using class loading mode, the disadvantage is that it is easy to write wrong package name class name, fewer disadvantages (we try to write this way)

 // Todo mode a class load
// Class load jump can be successful. High maintenance costs and prone to human error
try {
        Class targetClass = Class.forName("com.xiangxue.personal.Personal_MainActivity");
        Intent intent = new Intent(this, targetClass);
        intent.putExtra("name"."derry");
        startActivity(intent);
} catch (ClassNotFoundException e) {
        e.printStackTrace();
}
Copy the code

The disadvantage of using a global Map is that a lot of objects need to be registered.

// personal/Personal_MainActivity getMap
// Todo mode 2 global MapClass<? > targetActivity = RecordPathManager.startTargetActivity("personal"."Personal_MainActivity");
startActivity(new Intent(this, targetActivity));
Copy the code
public class RecordPathManager {

    Group: app,order,personal ** order: * OrderMainActivity1 * OrderMainActivity2 * OrderMainActivity3 */
    private static Map<String, List<PathBean>> maps = new HashMap<>();

    /** * Add path information to global Map **@paramGroupName groupName, for example, "personal" *@paramPathName specifies the pathName, for example, "Personal_MainActivity" *@paramClazz class objects, such as personal_mainactivity.class */
    public static void addGroupInfo(String groupName, String pathName, Class
        clazz) {
        List<PathBean> list = maps.get(groupName);

        if (null == list) {
            list = new ArrayList<>();
            list.add(new PathBean(pathName, clazz));
            // Store in the warehouse
            maps.put(groupName, list);
        } else {
            // Store in the warehouse
            maps.put(groupName, list);
        }
        // maps.put(groupName, list);
    }

    /** * return "Class to jump" * by telling me the group name and pathname@paramGroupName groupName oder *@paramPathName pathName OrderMainActivity1 *@returnJump to the target class object */
    public staticClass<? > startTargetActivity(String groupName, String pathName) { List<PathBean> list = maps.get(groupName);if (list == null) {
            Log.d(Config.TAG, "StartTargetActivity this group name gets information that is not registered...");
            return null;
        }
        // Traverse to match the "PathBean" object
        for (PathBean pathBean : list) {
            if (pathName.equalsIgnoreCase(pathBean.getPath())) {
                returnpathBean.getClazz(); }}return null; }}Copy the code

Register before you get

The difference between analysis componentization and modular: www.jianshu.com/p/cac0beae8…

4.APT (Annotation Processing Tool)

What is APT?

It detects the annotations in the source code file and automatically generates the code according to the annotations. If you want the customized Annotation processor to run normally, you must use the APT tool to process it. It can also be understood that a program can only execute a custom annotation interpreter during compilation by declaring the APT tool.

Popular understanding: according to the rules, help us generate code, generate class files

Important elements used in APT

PackageElement represents a PackageElement. ExecutableElement represents a method, constructor, or initializer (static or instance) of a class or interface. TypeElement represents a class or interface program element. Provides access to information about types and their members. VariableElement represents a field, enum constant, method or constructor parameter, local variable, or exception parameter

APT uses apis

The property name instructions
getEnclosedElements() Returns the child elements directly contained by the element
getEnclosingElement() Returns the parent element that contains the element, as opposed to the previous method
getKind() Returns the type of element and determines which element it is
getModifiers() Get the modifier keyword, such as public static final
getSimpleName() Gets the name, without the package name
getQualifiedName() Gets the full name, or, if it is a class, the full package name path
getParameters() Gets the parameter elements of the method, each of which is a VariableElement
getReturnType() Gets the return value of the method element
getConstantValue() If a property variable is decorated with final, you can use this method to get its value

APT technology

Make Java files APT JavaPoet make Java files APT

Which open source projects are useful in the traditional way? A: Look at the EventBus source code to see the traditional approach: advantages (the programming flow goes on) disadvantages (no OOP ideas come into play

5. Advanced usage JavaPoet

What is JavaPoet?

  • JavaPoet is an open source Java code generation framework from Square that provides Java Api generation

  • This framework is very functional and is the Java object-oriented OOP syntax we are used to

  • You can easily use it to generate code from annotations

  • In this way, code generation is automated

  • Can let us use a more concise and elegant way to replace the tedious redundant work

Project home page and source: github.com/square/java…

JavaPoet related

Class object instructions
MethodSpec Represents a constructor or method declaration
TypeSpec Represents a class, interface, or enumeration declaration
FieldSpec Represents a member variable, a field declaration
JavaFile A Java file containing a top-level class
ParameterSpec Used to create parameters
AnnotationSpec To create annotations
ClassName Used to wrap a class
TypeName Type, such as TypeName.VOID is used when adding return value types

S A string, such as: S character string, for example:
S, “hello”

T Classes, interfaces, such as: T class, interface, such as:
T, MainActivity

Advanced usage JavaPoet

What exactly is JavaPoet? Oop thinking: Advantages (add OOP thinking) Disadvantages (unaccustomed, in reverse order)

Is JavaPoet really better than traditional methods? Not so, if complex code generation, but inefficient development trends, OOP thinking, real mastery of JavaPoet, fondly

6. Componentized project deployment, ARouter principle

The build of the compiler. Gradle

Compiler is a Java project

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs'.include: ['*.jar'])

    // The service behind it can listen to whether you are compiling.....
    // AS3.4.1 + Gradle 5.1.1 + auto-service:1.0-rc4
    compileOnly'com. Google. Auto. Services: auto - service: 1.0 rc4'
    annotationProcessor'com. Google. Auto. Services: auto - service: 1.0 rc4'

    // Help us generate Java code through class calls [JavaPoet]
    implementation "Com. Squareup: javapoet: 1.9.0."

    // Rely on annotations
    implementation project(":arouter-annotations")



}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"
Copy the code

Annotations ARouter

@Target(TYPE) / / class
@Retention(CLASS) // compile time XUtil== Runtime
public @interface ARouter {

    String path(a);

    String group(a) default "";

}
Copy the code

The generated code

package com.derry.compiler;

import com.derry.arouter_annotations.ARouter;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

@AutoService(Processor.class) // Enable the service
@SupportedAnnotationTypes({"com.derry.arouter_annotations.ARouter"}) / / comment
@SupportedSourceVersion(SourceVersion.RELEASE_7) // Version of the environment


// Receive the parameters from android project
@SupportedOptions("student")

public class ARouterProcessor extends AbstractProcessor {

    // The utility class that operates on Element (classes, functions, attributes, actually elements)
    private Elements elementTool;

    // Utility class for type(class information), which contains utility methods for manipulating TypeMirror
    private Types typeTool;

    // Message Displays log information
    private Messager messager;

    // File generators, class resources, etc., are the files that need Filer to be generated
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        elementTool = processingEnvironment.getElementUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        String value = processingEnvironment.getOptions().get("student");
        // This code has been poisoned
        // I can use diagnostic.kind. ERROR if I want to throw an exception in the annotation handler
        messager.printMessage(Diagnostic.Kind.NOTE, "> > > > > > > > > > > > > > > > > > > > > >"+value);
    }

    // Service: work at compile time
    // pit: Subfunctions will not work if they are not used anywhere
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // This code has been poisoned
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>> Derry run...");

        if (set.isEmpty()) {
            return false; / / do nothing
        }

        / / cycle?
        // Get "class node information" annotated by ARouter
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
        for (Element element : elements) { // for 3 // 1 element == MainActivity 2 element == MainActivity2

            /** package com.example. Helloworld; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!" ); }} * /
            // Java everything is an object
            // C everything is a pointer
            MethodSpec mainMethod = methodSpec.methodBuilder ("main").modifier (Modifier.PUBLIC) Modifier.STATIC) .returns(void.class) .addParameter(String[].class, AddStatement ("$t.ut.println ($S)", System. Class, "Hello, JavaPoet!") ) .build(); Class TypeSpec testClass = TypeSpec. ClassBuilder ("DerryTest").addMethod(mainMethod).addModifiers(Modifier. Modifier.FINAL) .build(); JavaFile packagef = javafile.builder ("com.xiangxue.test", testClass).build(); // Generate file try {packagef.writeto (filer); } catch (IOException e) { e.printStackTrace(); PrintMessage (diagno.kind. NOTE, "failed to generate Test file, exception :" + LLDB etMessage()); } * /

            / / package information
            String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();

            // Get the simple class name, for example, MainActivity MainActivity2 MainActivity3
            String className = element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "Classes annotated by @aretuer are:" + className);

            // String className = element.getSimpleName().toString();

            // Target: Name of the file to be generated MainActivity$$$$$$$$$ARouter
            String finalClassName = className + "$$$$$$$$$ARouter";

            / * * template:  public class MainActivity3$$$$$$$$$ARouter { public static Class findTargetClass(String path) { return path.equals("/app/MainActivity3") ? MainActivity3.class : null; }} * /

            ARouter aRouter = element.getAnnotation(ARouter.class);

            / / 1. Methods
            MethodSpec findTargetClass = MethodSpec.methodBuilder("findTargetClass")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(Class.class)
                    .addParameter(String.class, "path")
                    Return path.equals("/app/MainActivity3")? MainActivity3.class : null;

                    // JavaPoet packaging transformation is required
                    .addStatement("return path.equals($S) ? $T.class : null",
                            aRouter.path(),
                            ClassName.get((TypeElement) element))
                    .build();

            / / 2. Class
            TypeSpec myClass = TypeSpec.classBuilder(finalClassName)
                    .addMethod(findTargetClass)
                    .addModifiers(Modifier.PUBLIC)
                    .build();

            / / 3. Package
            JavaFile packagef = JavaFile.builder(packageName, myClass).build();

            // start generating
            try {
                packagef.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
                messager.printMessage(Diagnostic.Kind.NOTE, "Generated" + finalClassName + "File failed, exception :"+ e.getMessage()); }}return true; // It's done}}Copy the code

The build of the app. Gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    defaultConfig {
        applicationId "com.derry.new_modular_javapoet"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        // Pass parameters
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [student: 'hello ni hao student javapoet']
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs".include: ["*.jar"])
    implementation 'androidx. Appcompat: appcompat: 1.2.0'
    implementation 'androidx. Constraintlayout: constraintlayout: 2.0.4'
    implementation project(path: ':arouter-annotations')
    implementation project(path: ':arouter-annotations')
    testImplementation 'junit: junit: 4.12'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.2'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.3.0'

    // Rely on annotations
    implementation project(":arouter-annotations")

    // Rely on the annotation handler for the annotation handler to work
    annotationProcessor project(":compiler")}Copy the code