Welcome to Star/Issue, project address: github.com/Dovar66/DRo…

Written in the beginning

Componentization is suitable for medium to large projects that require collaboration among multiple people. It is not recommended if the project is one person and the developer has not practiced componentization.

Advantages of componentization

  1. Business isolation allows each business module to focus on its own business implementation and not on other business modules.
  2. Separate debugging, each module can run independently, convenient development and debugging.
  3. Component reusability: For different apps with overlapping services, components can be directly used for assembly.
  4. Suitable for AOP.
  5. You can set the code modification permissions of team members in a more granular manner.

DRouter: An easy-to-use componentized solution that supports a multi-process architecture

Download the demo

DRouter is currently being used for electrocution news

DRouter provides three main features: interface routing, action routing, and event bus to make it easier for Android developers to componentize their projects. Especially for multi-process applications, the introduction of DRouter allows developers to communicate across processes without having to know about AIDL.Copy the code

Characteristics of framework

* Perfect support for multiple processes and no need for users to go to bindService or custom AIDL. * page routing: Supports defining urls for activities, * Support for cross-process API calls (action routing). * Support for cross-process event bus. * AOP based boot Module initialization and automatic registration of pages, interceptors, and providers.Copy the code

How to configure

1. Add the JitPack repository to build.gradle in the project root directory:

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}
Copy the code

2. Add dependencies to BaseModule:

API 'com. Making. Dovar66. DRouter: the router - API: 1.0.8'Copy the code

3. Add annotations handler dependencies to other components that need DRouter:

AnnotationProcessor 'com. Making. Dovar66. DRouter: the router - the compiler: 1.0.8' annotations at the same time in these components defaultConfig configuration parameters, specifies the component only:  defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } }Copy the code

4. Multi-process configuration:

* If your project requires multiprocess wide area routing, have your Application implement the IMultiProcess interface. Wide area routing is disabled by default and will only be enabled if this interface is implemented. * In the build. Gradle file of the App Module, the plugin RouterPlugin must be referenced after the apply Plugin: 'com.android.application'. 'com.android. Application 'apply plugin: "com.dovar.router. Plugin "// Must apply plugin: After '. Com. Android application, Otherwise I couldn't find AppExtension buildscript {repositories {Google () maven {url "https://plugins.gradle.org/m2/"}} dependencies { Classpath "gradle. Plugin. RouterPlugin: plugin: 2.0.0"}}Copy the code

How to use

The initialization is done in application.oncreate ()

DRouter.init(app);
Copy the code

Create component initialization entry (optional)

Subclass BaseAppInit in the component and add the Module annotation: @Module public class AInit extends BaseAppInit { @Override public void onCreate() { super.onCreate(); }} The annotation handler registers AInit with DRouter, and when DRouter initializes, OnCreate () of the registered BaseAppInit subclass is called. BaseAppInit provides an Application instance that is used in component engineering to get the global Application context.Copy the code

Page routing

Add the Route annotation to the interceptor to set the interceptor: @Route(path = "/b/main", interceptor = BInterceptor.class) public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.module_b_activity_main); }} Then use DRouter in the project to jump to the page: DRouter. Navigator ("/b/main").navigateto (mContext);Copy the code

Action Routing (API calls)

Create the appropriate AbsProvider subclass and add the Provider annotation, then register the Action in the class: @Provider(key = "a") public class AProvider extends AbsProvider { @Override protected void registerActions() { registerAction("test1", new Action() { @Override public RouterResponse invoke(@NonNull Bundle params, Object extra) {toast.makeText (appContext, "popup ", toast.length_short).show(); return null; }}); registerAction("test2", new Action() { @Override public RouterResponse invoke(@NonNull Bundle params, Object extra) { if (extra instanceof Context) { Toast.makeText((Context) extra, params.getString("content"), Toast.LENGTH_SHORT).show(); } return null; }}); Router ("a","test1").route(); Drouter.router ("a","test2").withString("content"," test2").extra(context).route(); // Call DRouter. MultiRouter ("a","test1").route(process); Note that when invoked across processes, the target Action will be executed in Binder threads. You can specify that the Action will be executed in the UI thread by setting runOnUiThread. DRouter.multiRouter("a","test1").runOnUiThread().route(process);Copy the code

Event bus

Subscribe to the event
There are two ways to subscribe: 1. Lifecycle awareness, no need to manually unsubscribe:  DRouter.subscribe(this, ServiceKey.EVENT_A, New EventCallback() {@override public void onEvent(Bundle e) {toast.maketext (mainActivity.this, "/b/main/ receive event A", Toast.LENGTH_SHORT).show(); }}); 2. Need to manually unsubscribe:  Observer<Bundle> mObserver = DRouter.subscribeForever("event_a", New EventCallback() {@override public void onEvent(Bundle e) {toast.maketext (mainActivity.this, "/b/main/ receive event A", Toast.LENGTH_SHORT).show(); }});Copy the code
Publish events (in any thread)(After enabling multi-process configuration, events are distributed to all processes)
Bundle bundle = new Bundle(); Bundle. putString(" Content ", "event "); bundle.putString("content"," event "); DRouter.publish(ServiceKey.EVENT_A, bundle);Copy the code
Unsubscribe events (timely unsubscribe is required when subscribing via subscribeForever())
 DRouter.unsubscribe("event_a", mObserver);
Copy the code

Confuse configuration

-keep class com.dovar.router.generate.** { *; }
Copy the code

DRouter’s componentization implementation principle

Interface routing

You can define a path for an Activity, and then jump to the Activity through the path. You can set a jump interceptor.Copy the code

Action routing

The service provider registers the Action with DRouter to expose the service to other components and processes. Note: Parameters passed across process calls need to be serialized, otherwise they will be filtered out by DRouter.Copy the code

Event bus

Componentized project architecture diagram

About APP shell project

Manage packaging configuration. Set component references. Manage obfuscation rules in a centralized manner, and no obfuscation files are configured in each component.Copy the code

About the application component layer

Business center, which contains all business components.Copy the code

About the public service layer

Manage cross-component calls and common resources, see common_service in the project for details. Why put an additional common services layer between the infrastructure layer and the application component layer? * encapsulation of basic framework layer API function calls, convenient to change the demand of the third-party libraries in the future, believe that many programmers have experienced replacement of third-party libraries (especially the foundation library) pain, if the project does not have in the package but a direct reference to the third party API, wait until you want to change and then you will find there are too many need to modify the code. Store common resources and code, exposing it to upper-level business use, while avoiding the sinking of these resources into the infrastructure layer, thereby reducing unnecessary updates to the infrastructure layer. In a multi-player collaborative project, the infrastructure must be stable, so we want as few commits as possible to point to the infrastructure layer.Copy the code

About the base frame layer

Generic function modules unrelated to business, such as network requests, picture loading, generic custom controls, etc.Copy the code

How to configure separate Module debugging after componentization?

Set up a working shell project, such as app in the example. Then configure component dependencies in the shell project and comment out other component dependencies if you want to debug one component separately.Copy the code

Componentized resource file processing

1. Androidmanifest.xml merge:

Each module has its own AndroidManifest file, which will eventually be merged into one during APP compilation.

We can under the module configuration for the Application of the build/intermediates/manifests path find AndroidManifest file, after the synthesis contrast before and after the compilation of differences can be roughly analyze merger rules and conflict rules.

Android :label=”@string/app_name”; androidManifest.xml; androidmanifest.xml; Resources from the current ApplicationModule are preferentially fetched.

2. R file:

The id in the R file in libModule is no longer a static constant, it is a static variable, so you can't use switch.. case.. Syntax operation resource IDCopy the code

3. Other Resources:

1. To prevent resources with the same name, you are advised to add unique characters to the names of resources in each module. For example, in module-live, all resources are prefixed with "mlive_", mlive_icon_close.png apply Plugin: 'com.android.library' android { compileSdkVersion 27 defaultConfig { minSdkVersion 15 targetSdkVersion 27 ... } resourcePrefix "module_a_" // Can be used to limit the resource name prefix} 2. You are advised to store some resources such as style, common string, shared image, and drawable in common_service. Resources belonging to different modules should be stored in their own modules.Copy the code

Componentized Git deployment

Note: You can still use the version management scheme of your existing project, so skip this section.Copy the code

Our project will only corresponds to a Git repository, so all the members of the development can edit all of the code in the project and submit the changes, but as a project manager, we want members to submit himself to the responsible business code, avoid the members in the development process accidentally changed the bugs by other members of the code. With componentization, we can reconfigure code modification permissions based on member development responsibilities.

Assume that during the development process, the basic framework layer remains the same. A is responsible for Module_A, B is responsible for Module_B, c is responsible for module_C and packaging. A, B, and C can modify common_service. Therefore, we will separate module_A/MODULE_B/MODULE_C/COMMON_Service into sub-warehouses. Module_a warehouse only allows modification permission to A, MODULE_B warehouse only allows modification permission to B, and module_C warehouse only allows modification permission to C. This results in the following permission distribution:

There are three Git deployment options:

* Direct deployment of multiple sub-warehouses under the main project Directly imports all sub-warehouses under the main project. Advantages: No need to learn new Git commands. Disadvantages: The operation of downloading the main project is complicated, you need to clone the main project and then clone all the sub-projects under the main project (you can complete the one-click clone through the configuration script). Advantages: It is easier to use than subtree and has no Git clone problems with the first method. Disadvantages: Submodule way cannot amend the pair in the main project project pushed to the subproject warehouse. * git subtree/how to use (https://blog.csdn.net/Dovar_66/article/details/83185288) advantages: Changes made to subprojects in the main project can be pushed to the subproject repository. Disadvantages: Git commands are complicated to use, and members have high learning costs.Copy the code

The first one I’m using right now. An AS plug-in is attached, which may come in handy: one-click current branches of all repositories under Git Pull

How to carry on component transformation step by step?

Complete componentization is not something that can be done in a day or two, and there are always new requirements waiting to be developed on our projects. Iteration almost guarantees that we can't suspend the requirements for componentization. Therefore, version iteration and component-based splitting need to be carried out simultaneously. Here are my suggestions: 1. Start preparing: * Add the APP shell project, please refer to the APP project in this project. * Use the current application project of your project as the common service layer, of course, since the components have not been split, * For a new component (represented by module_search), IT is recommended to start with a relatively simple or familiar business module, such as the search module of my own company project, which only has search function and rarely interacts with other modules, so I choose to start with it. Module_search dependencies on common_service.(Implementation dependencies) 3. To introduce DRouter: refer to the DRouter instructions above. 4. Separate the common service layer from the infrastructure layer (optional) : If your project did not separate the business layer from the infrastructure layer before, it is recommended that you now separate the infrastructure from common_Service. Step by step component separation: (This process can be as short as a few days, as long as a few months, the larger the project is, the heavier the coupling is, the longer the time is. It is recommended to proceed slowly) * We already added module_search, so now we want to remove the search module code from common_service and put it into the search component, which can still reference the common service layer code directly. But only through public service layer routing use the search function. * new requirements in the development of the main line, a new resource file, preferably in the corresponding component engineering resource file before can temporarily keep in public service layer, waiting for the follow-up by each component engineering claim, the backlog of as little as possible in the public service layer. Permissions, Android's four major components, and unique third-party library references should all be declared in the corresponding component Module, not sunk into the common services layer, let alone the infrastructure layer. (After the first component is successfully split and launched to market, if the feedback is good, then it is time to move on to the other components.) Split the second component, the third component... The granularity of component split depends on your business module and team responsibilities, so split as needed.Copy the code

My other projects: classmate, your system Toast may need fixing!