Android decoupling (3) APT-based decoupling

Android decouples module dependencies through APT

1. What is APT?

APT (Annotation Process Tool) is an annotation processing tool that can scan and process annotations during compilation and generate corresponding Java code. APT is a feature of Java, but it is also widely used in Android development.
The advantages of APT are:

  • The correctness of the code can be checked during compilation to avoid errors at runtime
  • Can reduce the amount of handwritten code, improve development efficiency and readability
  • Can achieve decoupling between modules, reduce coupling and dependencies

The disadvantages of APT are:

  • Requires additional configuration and use of annotation processors
  • Generated code may not be easy to understand and maintain
  • Not suitable for dynamically changing data

2. Application scenarios of APT in Android

APT has many application scenarios in Android, such as:

  • ButterKnife: Use Annotations to Bind Views and Events to Simplify UI Development
  • Dagger2: Use annotations to implement dependency injection and decoupling between modules
  • EventBus: Use annotations to register and send events to simplify communication between components
  • Retrofit: Use annotations to define network request interfaces to simplify network development
  • ARouter: Use annotations to define routing tables to achieve jumps between modules

This article will focus on how APT achieves decoupling between modules in Android modular development.

3. How APT achieves decoupling between modules

In Android modular development, the following problems are usually encountered:

  • Modules cannot directly reference each other's classes or resources, otherwise circular dependencies or conflicts will result
  • Communication protocols need to be defined through interfaces or abstract classes between modules, but this will increase the amount of code and complexity
  • Modules need to call each other's methods or services through reflection or dynamic proxy, but this will affect performance and security

In order to solve these problems, we can use APT to generate an intermediate class as a cross-module forwarding layer. The specific steps are as follows:

  1. Define a common library (common) as a basic module, providing some basic functions and common interfaces.
  2. Define an annotation library (annotation) as a submodule of the common library, and define some custom annotations in it.
  3. Define a compiler library (compiler) as a submodule of the common library, and define a custom annotation processor in it.
  4. Introduce public libraries in business modules (moduleA, moduleB, etc.), and use custom annotations to mark classes or methods that need to be called or provide services across modules.
  5. During compilation, the custom annotation processor will scan classes or methods marked with custom annotations in all business modules, and generate corresponding intermediate classes according to the rules.
  6. During operation, business modules can call services or methods provided by other business modules through intermediate classes.

Below we illustrate this process with a simple example.

3.1 Common library common

The common library common provides the following functions:

  • A ServiceManager class is defined to manage all cross-module provided or invoked

Android decouples module dependencies through APT

1. What is APT?

APT (Annotation Process Tool) is an annotation processing tool that can scan and process annotations during compilation and generate corresponding Java code. APT is a feature of Java, but it is also widely used in Android development. The advantages of APT are: - The correctness of the code can be checked during compilation to avoid errors at runtime - It can reduce the amount of handwritten code, improve development efficiency and readability - It can achieve decoupling between modules, reducing coupling and dependencies Disadvantages of relational APT are: - Requires additional configuration and use of annotation processors - Generated code may not be easy to understand and maintain - Not suitable for dynamically changing data

2. Application scenarios of APT in Android

APT has many application scenarios in Android, such as: - ButterKnife: Use annotations to bind views and events to simplify UI development - Dagger2: Use annotations to implement dependency injection and achieve decoupling between modules - EventBus: Use annotations to register and send events, Simplify communication between components - Retrofit: Use annotations to define network request interfaces to simplify network development - ARouter: Use annotations to define routing tables to achieve jumps between modules This article will focus on how APT implements solutions between modules in Android modular development couple.

3. How APT achieves decoupling between modules

In Android modular development, the following problems are usually encountered: - Modules cannot directly refer to each other's classes or resources, otherwise it will cause circular dependencies or conflicts - Modules need to define communication protocols through interfaces or abstract classes, but This will increase the amount of code and complexity - modules need to call each other's methods or services through reflection or dynamic proxy, but this will affect performance and security In order to solve these problems, we can use APT to generate a cross-module forwarding layer intermediate class.
The specific steps are as follows:
1. Define a common library (common) as a basic module to provide some basic functions and common interfaces.
2. Define an annotation library (annotation) as a submodule of the public library, and define some custom annotations in it.
3. Define a compiler library (compiler) as a submodule of the public library, and define a custom annotation processor in it.
4. Introduce public libraries in business modules (moduleA, moduleB, etc.), and use custom annotations to mark classes or methods that need to be called or provide services across modules.
5. During compilation, the custom annotation processor will scan all business modules for classes or methods marked with custom annotations, and generate corresponding intermediate classes according to the rules.
6. During operation, business modules can call services or methods provided by other business modules through intermediate classes. Below we illustrate this process with a simple example.

3.1 Common library common public library

common provides the following functions:

  • A ServiceManager class is defined to manage all cross-module provided or invoked

3.2 annotation library annotation

The annotation library annotation defines the following annotations:

  • @Service: Used to mark classes that need to provide services across modules, the name and description of the service can be specified in the annotation
  • @Method: Used to mark methods that need to be called across modules. The name and parameter type of the method can be specified in the annotation
  • @Param: used to mark the parameters of the method, the name and type of the parameter can be specified in the annotation

For example:

// 定义一个跨模块提供服务的类
@Service(name = "user", desc = "用户服务")
public class UserService {
    
    

    // 定义一个跨模块调用的方法
    @Method(name = "login", params = {
    
    @Param(name = "username", type = String.class), @Param(name = "password", type = String.class)})
    public boolean login(String username, String password) {
    
    
        // 省略登录逻辑
        return true;
    }
}


3.3 compiler library compiler

The compiler library compiler defines a custom annotation processor ServiceProcessor, which inherits the AbstractProcessor class and overrides the following methods:

  • init: Initialize the annotation processor and get some tool objects, such as Elements, Types, Filer, etc.
  • getSupportedAnnotationTypes: Returns a collection of annotation types that support processing, such as Service, Method, Param, etc.
  • process: process elements (Element) marked with supported annotation types, and generate corresponding intermediate classes according to the rules

For example:

// 自定义注解处理器
public class ServiceProcessor extends AbstractProcessor {
    
    

    // 初始化工具类对象
    private Elements elementUtils;
    private Types typeUtils;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
    
    
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
        typeUtils = processingEnv.getTypeUtils();
        filer = processingEnv.getFiler();
    }

    // 返回支持处理的注解类型集合
    @Override
    public Set<String> getSupportedAnnotationTypes() {
    
    
        Set<String> types = new LinkedHashSet<>();
        types.add(Service.class.getCanonicalName());
        types.add(Method.class.getCanonicalName());
        types.add(Param.class.getCanonicalName());
        return types;
    }


3.2 Annotation library

annotation The annotation library annotation defines the following annotations:

  • @Service: Used to mark classes that need to provide services across modules, the name and description of the service can be specified in the annotation
  • @Method: Used to mark methods that need to be called across modules. The name and parameter type of the method can be specified in the annotation
  • @Param: used to mark the parameters of the method, the name and type of the parameter can be specified in the annotation
    For example:
// 定义一个跨模块提供服务的类 
@Service(name = "user", desc = "用户服务") public class UserService {
    
    
 // 定义一个跨模块调用的方法 
 @Method(name = "login", params = {
    
    
 @Param(name = "username", type = String.class), 
 @Param(name = "password", type = String.class)
 }
 ) 
 public boolean login(String username, String password) {
    
     
 // 省略登录逻辑
  return true; 
  } 
 } 

3.3 Compiler library compiler The compiler library compiler defines a custom annotation processor ServiceProcessor, which inherits the AbstractProcessor class and overrides the following methods:

  • init: Initialize the annotation processor and get some tool objects, such as Elements, Types, Filer, etc.
  • getSupportedAnnotationTypes: Returns a collection of annotation types that support processing, such as Service, Method, Param, etc.
  • process: Process elements (Element) marked with supported annotation types, and generate corresponding intermediate classes according to the rules.
    For example:
 // 自定义注解处理器 
public class ServiceProcessor extends AbstractProcessor {
    
    
 // 初始化工具类对象
  private Elements elementUtils;
  private Types typeUtils; 
  private Filer filer; 
  @Override public synchronized void init(ProcessingEnvironment processingEnv) {
    
    
   super.init(processingEnv); 
   elementUtils = processingEnv.getElementUtils(); 
   typeUtils = processingEnv.getTypeUtils(); 
   filer = processingEnv.getFiler(); 
   } 
   // 返回支持处理的注解类型集合 
   @Override public Set<String> getSupportedAnnotationTypes() {
    
    
    Set<String> types = new LinkedHashSet<>(); 
    types.add(Service.class.getCanonicalName()); 
    types.add(Method.class.getCanonicalName()); 
    types.add(Param.class.getCanonicalName()); return types;
     }
}

3.4 runtime library runtime

The runtime library runtime defines a ServiceManager class, which is responsible for managing all service classes and methods provided across modules, and loading generated intermediate classes. It provides the following methods:

  • init: Initialize ServiceManager, scan all registered service classes, and load the corresponding intermediate classes
  • register: To register a service class, you need to pass in the fully qualified name of the service class
  • invoke: To call a service method, you need to pass in the service name, method name and parameter list

For example:

// 初始化 ServiceManager
ServiceManager.init();

// 注册一个服务类
ServiceManager.register("com.example.UserService");

// 调用一个服务方法
boolean result = (boolean) ServiceManager.invoke("user", "login", "admin", "123456");


3.5 Example of use

Suppose we have two modules: app and user. The app module depends on the user module, and the user module provides a UserService class for implementing user-related functions. We want to call the UserService's login method in the app module, but don't want to reference the user module directly.

We can use APT to decouple the dependencies of these two modules, the specific steps are as follows:

  1. Define the UserService class in the user module and mark the class and the login method with @Service and @Method annotations.
  2. Add annotation, compiler and runtime library dependencies in app module, and configure annotationProcessor in build.gradle.
  3. Register the UserService class in the app module and use the ServiceManager to call the login method.

The specific code is as follows:

// user 模块中定义 UserService 类
@Service(name = "user", desc = "用户服务")
public class UserService {
    
    

    @Method(name = "login", params = {
    
    @Param(name = "username", type = String.class), @Param(name = "password", type = String.class)})
    public boolean login(String username, String password) {
    
    
        // 省略登录逻辑
        return true;
    }
}

// app 模块中添加依赖和配置
dependencies {
    
    
    implementation project(':user')
    implementation 'com.kymjs:annotation:1.0'
    annotationProcessor 'com.kymjs:compiler:1.0'
    implementation 'com.kymjs:runtime:1.0'
}

// app 模块中注册并调用 UserService 类
public class MainActivity extends AppCompatActivity {
    
    

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

        // 初始化 ServiceManager
        ServiceManager.init();

        // 注册 UserService 类
        ServiceManager.register("com.example.UserService");

        // 调用 login 方法
        boolean result = (boolean) ServiceManager.invoke("user", "login", "admin", "123456");

        // 显示结果
        Toast.makeText(this, result ? "登录成功" : "登录失败", Toast.LENGTH_SHORT).show();
    }
}


In this way, the function of decoupling module dependencies through APT is realized.

3.4 runtime library runtime

The runtime library runtime defines a ServiceManager class, which is responsible for managing all service classes and methods provided across modules, and loading generated intermediate classes. It provides the following methods: - init: Initialize ServiceManager, scan all registered service classes, and load the corresponding intermediate class - register: register a service class, you need to pass in the fully qualified name of the service class - invoke: call a service method , you need to pass in the service name, method name and parameter list
. For example:

// 初始化 
ServiceManager ServiceManager.init(); 
// 注册一个服务类 
ServiceManager.register("com.example.UserService");
 // 调用一个服务方法 
 boolean result = (boolean) ServiceManager.invoke("user", "login", "admin", "123456"); 

3.5 Example of use

Suppose we have two modules: app and user. The app module depends on the user module, and the user module provides a UserService class for implementing user-related functions. We want to call the UserService's login method in the app module, but don't want to reference the user module directly. We can use APT to decouple the dependencies of these two modules. The specific steps are as follows:
1. Define the UserService class in the user module, and use @Service and @Method annotations to mark this class and the login method.
2. Add annotation, compiler and runtime library dependencies in the app module, and configure annotationProcessor in build.gradle.
3. Register the UserService class in the app module and call the login method using the ServiceManager.
The specific code is as follows:

 // user 模块中定义 UserService 类 @Service(name = "user", desc = "用户服务") public class UserService {
    
    
  @Method(name = "login", params = {
    
    @Param(name = "username", type = String.class), 
  @Param(name = "password", type = String.class)
  }) public boolean login(String username, String password) {
    
    
   // 省略登录逻辑 return true; 
   } 
   } 
   // app 模块中添加依赖和配置 
   dependencies {
    
    
    implementation project(':user')
    implementation 'com.kymjs:annotation:1.0' 
    annotationProcessor 'com.kymjs:compiler:1.0' 
    implementation 'com.kymjs:runtime:1.0'
     } 
     // app 模块中注册并调用 UserService 类 
     public class MainActivity extends AppCompatActivity {
    
     
     @Override protected void onCreate(Bundle savedInstanceState) {
    
    
      super.onCreate(savedInstanceState); 
      setContentView(R.layout.activity_main); 
      // 初始化 
      ServiceManager ServiceManager.init();
       // 注册 UserService 类 
       ServiceManager.register("com.example.UserService"); 
       // 调用 login 方法
        boolean result = (boolean) ServiceManager.invoke("user", "login", "admin", "123456"); 
        // 显示结果 
        Toast.makeText(this, result ? "登录成功" : "登录失败", Toast.LENGTH_SHORT).show();
         }
        }

In this way, the function of decoupling module dependencies through APT is realized.

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master the various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Welcome everyone to support with one click and three links. If you need the information in the article, you can directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

PS: There is also a ChatGPT robot in the group, which can answer your work or technical questions
picture

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/132088208