android module Decoupling Modularization Overview of Recommendations

  • 2021-09-20 21:22:36
  • OfStack

Reason

In mobile development, with the continuous decline of projects, the requirements become more and more complex. The project is getting bigger and bigger. Then the development of module at this time is an inevitable choice. On the road of final componentization, we might as well take a single 1 project such as the Stone Age, and then the next simple split project is divided into several moudle, which is the Bronze Age.

Simple division of Bronze Age module

Evolution

Due to the split of multiple module from a complex single project, the initial isolation of code and resources has been achieved, or the developers of requirements modules have begun to focus on the development of their own requirements modules module. However, as some requirements are related and need to be called each other. So here comes the problem, in AXXX module


api project(':BXXX')

And in BXXX module


api project(':AXXX')

At this point, there is interdependence. First of all, the compiler will be able to do it. However, there will be Circular dependency, circular interdependence, which is absolutely not allowed.

In order to solve the above problems, AXXX module and BXXX module need to provide external service capability support, encapsulation and abstraction. Where the interface/protocol needs to be exposed to the outside world, the interface will be abstracted. Put these interfaces independently in BaseXXXX module, so that AXXX module and BXXX module are all removed separately


api project(':BaseXXXX')

The interdependent call communication between AXXX module and BXXX module is solved by BaseXXXX intermediate module communication.

Preliminary solution

In order to build a communication bridge between AXXX module and BXXX module in BaseXXXX module, a communication identification interface can be defined in BaseXXXX module:


/**
 *
 *  Span module Communicative   Identification  interface Interface 
 */
public interface IModuleApi {
}

Then the communication is mainly through ModuleApiHelper


public class ModuleApiHelper {

  private static Map<Class<? extends IModuleApi>,IModuleApi> moduleApiMap = new HashMap<>();
  private static Map<Class<? extends IModuleApi>,List<IModuleApi>> moduleApiListMap = new HashMap<>();

  /**
   *  Span module  Register in  IKWModuleApi Interface, and implementation 
   *  It can usually be found in   Others module Medium   Register the implementation of this interface, and use the module Medium getModuleApi Get the interface implementation 
   *  In this way, it is used module  No   Must rely on compile Others module It's over 
   * @param clazz
   * @param iModuleApi
   */
  public static void register(Class<? extends IModuleApi> clazz, IModuleApi iModuleApi){
    if (null != iModuleApi && null != clazz){
      moduleApiMap.put(clazz, iModuleApi);
    }
  }

  public static void unregister(Class<? extends IModuleApi> clazz){
    if (moduleApiMap.containsKey(clazz)){
      moduleApiMap.remove(clazz);
    }
  }

  public static void register2List(Class<? extends IModuleApi> clazz, IModuleApi iModuleApi){
    if (null != iModuleApi && null != clazz){
      if (moduleApiListMap.containsKey(clazz)){
        List<IModuleApi> iModuleApis = moduleApiListMap.get(clazz);
        iModuleApis.add(iModuleApi);
      }else{
        List<IModuleApi> iModuleApis = new ArrayList<>();
        iModuleApis.add(iModuleApi);
        moduleApiListMap.put(clazz, iModuleApis);
      }
    }
  }

  public static void unregister2List(Class<? extends IModuleApi> clazz){
    if (moduleApiListMap.containsKey(clazz)){
      moduleApiListMap.remove(clazz);
    }
  }

  public static void unregisterAll(Class<? extends IModuleApi> clazz){
    unregister(clazz);
    unregister2List(clazz);
  }

  public static <T extends IModuleApi> List<T> getModuleListApi(Class<T> clazz){
    if (null != clazz){
      if (moduleApiListMap.containsKey(clazz)){
        List<IModuleApi> iModuleApis = moduleApiListMap.get(clazz);
        return (List<T>) iModuleApis;
      }else{
        return null;
      }
    }else{
      return null;
    }
  }

  /**
   *  Gets the registered bound IKWModuleApi  Realization 
   * @param clazz
   * @param <T>
   * @return
   */
  public static <T extends IModuleApi> T getModuleApi(Class<T> clazz){
    if (null != clazz){
      if (moduleApiMap.containsKey(clazz)){
        return (T) moduleApiMap.get(clazz);
      }else{
        return null;
      }
    }else{
      return null;
    }
  }
}

Thus, for example, in AXXX module, the original AServiceData class is as follows:


public class AServiceData {
  public String getSomeData(){
    return "this is some data";
  }

  public void sayHello(){
    System.out.println("hello");
  }
}

Transform into


public interface IAServiceData extends IModuleApi {
  String getSomeData();
  void sayHello();
}

public class AServiceData implements IAServiceData{

  @Override
  public String getSomeData(){
    return "this is some data";
  }

  @Override
  public void sayHello(){
    System.out.println("hello");
  }
}

The IAServiceData interface is defined in BaseXXXX module. Then AXXX module performs the corresponding service of register


public class AModuleService {

  public void init(){
    ModuleApiHelper.register(IAServiceData.class,new AServiceData());
  }
}

The IAServiceData service can be registered by calling the init method of AModuleService. Then immediately down, getXXX in BXXX module to get the service can call the corresponding method.

Any method that requires this service can be called as follows:


IAServiceData iaServiceData = ModuleApiHelper.getModuleApi(IAServiceData.class);

Attention

1 > register registration time, the sooner the better, 1 generally recommended in each module has a similar application onCreate registration best.

2 > The interface between IModuleApi and ModuleApiHelper, and each extends inheriting the IModuleApi interface, needs to be placed in the intermediate communication BaseXXX Module. Each module that needs communication can go to compile/api BaseXXX Module.

Problem

In order to ensure the effective registration of IModuleApi interface, the sooner the better. In this way, as the project becomes more and more complex, there are more and more places to communicate. The ModuleApiHelper of Unified 1 will be registered in more and more places, and there will be more problems.

1 > Registering the Map container takes up an increasing amount of memory.
2 > register is registered in different places, some of which are placed in onCreate of Application of each module, and some of which may be placed in other classes.
3 > Jump from ui page to BxxActivity page from AXXX module is not supported.
4 > Applications in multiple processes are not supported.

In order to solve the above problems, ARoute of age of steam was introduced.

ARoute of Age of Steam

Because of the scope, ARoute will not be described in detail in the general overview, and ARoute will be analyzed in the next time. Generally speaking, in multi-module communication,:

1 > Solves the jump problem of ui page.
2 > The problem of register is carried out as needed, and register is obtained by static annotation, so register is easy to maintain, for example.

But it still can't solve the application in multi-process.

Andromeda in the Age of Electrical Appliances

Andromeda solves the communication process between multi-process and cross-process ipc, and also supports single-process communication...


Related articles: