banner
jzman

jzman

Coding、思考、自觉。
github

Android Componentization Basics

PS: What you are currently insisting on may not have any effect at the moment, but it does not mean that it will not be useful in the future at some point.

When an app project reaches a certain level of complexity, componentization is essential. Componentization allows for better division of functionality. When talking about componentization, some people may think of modularization. In fact, componentization and modularization are essentially the same, both aiming to achieve code reuse and decoupling of business logic. Modularization is mainly based on business division, while componentization is mainly based on functional division. Let's explore componentization from several fundamental aspects.

  1. Inter-component navigation
  2. Dynamic creation
  3. Resource conflicts
  4. Static constants

Inter-component navigation#

In componentization, when two functional modules are not directly dependent on each other, their dependency rules are indirectly established through a base module. When performing interface navigation between components, such as Activity navigation, it is often not possible to reference an Activity from another module due to the lack of mutual dependency.

Implicit navigation#

Implicit navigation is achieved through the native Android Intent matching mechanism. It uses Actions to navigate to corresponding Activities. This way, implicit navigation can be used to achieve navigation between Activities in different modules. One thing to note is that if an Activity is moved out of its module without moving the corresponding navigation, an exception will occur if the navigation continues. Implicit Intent navigation requires verification of whether the Intent will be received. The resolveActivity() method needs to be called on the Intent object to determine if at least one application can handle the Intent. By using implicit navigation, the exported attribute can also be set to false to ensure that only the app itself can start the corresponding component.

ARouter navigation#

In Android development, modules can be seen as different networks, and the corresponding Router is the hub that connects these modules. This hub can handle parameters and other aspects of page navigation uniformly. ARouter is an open-source page navigation router released by Alibaba. It can replace implicit navigation to achieve navigation between different modules and components, as well as listening to the navigation process and passing parameters. ARouter supports both path-based navigation and URL-based navigation, and its usage is very flexible. A detailed explanation of ARouter usage will be provided in a separate article. The comparison between ARouter and traditional Android navigation methods is as follows:

  1. Explicit navigation relies on classes, while router navigation relies on specified paths.
  2. Implicit navigation is centrally managed through AndroidManifest, which makes collaborative development difficult.
  3. Native navigation is registered using AndroidManifest, while router navigation uses annotations for registration.
  4. After startActivity is called in native navigation, the navigation process is controlled by the Android system, while router navigation uses AOP aspect-oriented programming to intercept and filter the navigation process.

Dynamic creation#

The most important aspect of component-based development is to decouple various modules and components as much as possible. This naturally leads to the consideration of using the reflection mechanism in Java. Reflection allows for obtaining all information about a class at runtime, enabling dynamic manipulation of its properties and methods. When a Fragment is used as a separate component, if this Fragment component does not need to be removed, using a regular Fragment would cause the app to crash because the Fragment cannot be found. However, if the reflection method is used to create the Fragment, it will not cause the app to crash. Exceptions can be caught and relevant logic can be executed. This reduces coupling. Although reflection has some performance issues, it can indeed reduce coupling to some extent. Learning about Java reflection mechanism is necessary for component-based development.

In component-based development, each component is required to be able to run independently. In general, each component has its own initialization steps. The best scenario is when the initialization steps of several required components are basically the same. In this case, the initialization can be unified in the BaseModule. However, this scenario is relatively ideal. In most cases, the initialization of each component is different. You may think of initializing in each component's Application. However, if the initialization is done in each component's Application, there may be problems during the final compilation due to the merging of Applications. This approach is not recommended. At this point, reflection comes to mind again. Initialization files can be created in each component, and then the initialization of each component can be completed through reflection in the final Application. This way, dynamic configuration of Application in component-based development is achieved through Java reflection mechanism.

Resource conflicts#

During component-based development, if the AndroidManifest file of ModuleA specifies an Application using the android attribute, and the AndroidManifest file of the main App Module also specifies a corresponding Application using the android attribute, it is necessary to use tools="android" in the AndroidManifest file of the main App Module to resolve the conflict. The replace attribute indicates that this attribute, which is the android attribute under the tag, can be replaced during the compilation process. According to the AndroidManifest file merging rules, the specified Application in the App Module should be the one used.

Let's take an example. In a certain functional module in the project, I use SMSSDK to implement SMS verification. Since it is only used in that specific module, I only include it in that module. If other modules need to use it, SMSSDK should be included in the BaseModule. If the Application of that module is not specified when using SMSSDK, MobSDK will specify com.mob.MobApplication as the Application for that module. In this case, there will be a conflict in the android attribute of the AndroidManifest file during the overall compilation and packaging process. The solution is to use the replace attribute. The main conflicts in the merged AndroidManifest file are related to this issue. Of course, conflicts in other attributes under can also be resolved using the replace attribute. In actual development, more validation will lead to more insights.

Another point to note in component-based development is to prevent resource name conflicts that may cause errors in resource references or loss of certain resources during the final merge. When merging, resources such as strings and color values will be replaced by resources with the same name that are loaded later. The solution is to have certain naming rules for resources. You can configure "resourcePrefix "componentName"" in the build.gradle file to enforce the uniqueness of resource names. It is recommended to use the naming format "ModuleName_Function_Other" for resources in modules.

Static constants#

In component-based development, when components are merged into Lib Modules, the static variables defined in the R.java file of the Lib Module are not declared as final. This means that the corresponding constants cannot be used in the component Module. For example, switch statements cannot be used. This requires using if statements instead of switch statements in components. However, this is not a problem when the component runs independently.

Butterknife is often used in development to conveniently annotate views and view events. It uses compile-time annotation mechanism, and only constants can be used in annotations. Therefore, in component-based development, R should be replaced with R2 when using Butterknife. R2 is actually a copy of R, and the variables declared in R2 are final. Therefore, when using Butterknife in component-based development, R2 should be used instead of R in the corresponding annotations. The next article will introduce the componentization of Application.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.