Novice guide to avoiding pits: Detailed explanation of Android component development

learning target:

Proficiency in component development, routing configuration


Learning Content:

**Before using componentized development, you must first clarify the overall project framework, divide the modules and business (focus), and a good start will have a good result. **After the module is clearly divided, start configuring the Module.

As shown in the figure, we have to complete the following functions:

1. Click on the mall to enter ShoppingModule

2. Click Login to enter LoginModule

3. Click the red area of ​​the bill to display the bill list (Fragment in other Modules)

(shareModule is a public module)

Created as follows according to business requirements:

Add under the App's gradle.properties file to control whether the module runs independently.

#配置某个组件是否可以独立运行
isShoppingRunAlone = true
isLoginRunALone = true

Then configure App build.gradle.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
 
android {
    compileSdkVersion 30
    buildToolsVersion "29.0.3"
 
    defaultConfig {
        applicationId "com.example.moduledemo"
        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'
        }
    }
}
 
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
 
    //导入公共模块
    implementation project(':ShareModule')
 
    // 根据gradle中的配置来决定是否引用module
    if (!isLoginRunALone.toBoolean()){
        implementation project(':LoginModule')
    }
    if (!isShoppingRunAlone.toBoolean()){
        implementation project(':ShoppingModule')
    }
 
}

Continue to configure the build.gradle file of other Modules.

if (isShoppingRunAlone.toBoolean()){
    apply plugin: 'com.android.application'
}else {
    apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
 
android {
    compileSdkVersion 30
    buildToolsVersion "29.0.3"
 
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
 
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }
 
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets{
        main{
            // 在独立运行或者作为Libarary调试时,使用不同的AndroidManifest.xml文件
            if (isShoppingRunAlone.toBoolean()){
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            }else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
}
 
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    //导入公共模块
    implementation project(':ShareModule')
 
}

Use different Manifest files in different operating modes.

You need to create a new manifest folder in the Main directory of the corresponding module (otherwise, you will not find the Manifest file if you run it alone).

The manifest file that runs separately is set as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.loginmodule">
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Incorporate into the main Module runtime manifest file settings as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.shoppingmodule">
 
    <application>
        <activity android:name=".ShoppingActivity"/>
    </application>
</manifest>

After all the configuration is complete, you can modify the value of the variable in gradle.properties, compile to check whether the configuration is correct, and whether the manifest file is replaced. Run to see if it is normal.

Next, start configuring routing.

Many people are wondering why they need to use routing jumps if they can directly get the activity of the sub-module after referencing the Module. This is because the component development is to make a single Module compile independently. If the main Module references the class name of the sub-Module, the main Module will compile abnormally when the sub-Module runs alone.

We need to know that a project cannot have only one sub-module. How to use routing when our other sub-modules want to jump to each other? So we need to configure the routing in ShareModule. In the previous configuration, we imported ShareModule into each Module.

first step

We create a jump template corresponding to Module

import android.content.Context;
import android.os.Bundle;
 
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
 
public interface ILoginService {
    void launch(Context ctx, String targetClass);
 
}
import android.content.Context;
import android.os.Bundle;
 
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
 
public interface IShoppingService {
    void launch(Context ctx, String string);
    Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle);
}

Second step

Realize jump logic and value transfer operations in the corresponding moudle

package com.example.loginmodule;
 
import android.content.Context;
import android.content.Intent;
 
import com.example.sharemodule.ILoginService;
 
 
public class LoginService implements ILoginService {
    @Override
    public void launch(Context ctx, String targetClass) {
        Intent intent = new Intent(ctx, LoginActivity.class);
        ctx.startActivity(intent);
    }
}
package com.example.shoppingmodule;
 
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
 
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
 
import com.example.sharemodule.IShoppingService;
 
 
public class ShoppingService implements IShoppingService {
 
    @Override
    public void launch(Context ctx, String string) {
        Intent intent = new Intent(ctx, ShoppingActivity.class);
        ctx.startActivity(intent);
    }
 
    @Override
    public Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
        BillFragment fragment = new BillFragment();
        fragment.setArguments(bundle);
        fragmentManager.beginTransaction().replace(viewId, fragment).commit();
        return fragment;
    }
}

third step

Next we create a ServiceFactory to provide us with jump instances, and handle exceptions that may occur when running alone

package com.example.sharemodule;
 
public class ServiceFactory {
    private static final ServiceFactory instance = new ServiceFactory();
 
    private ILoginService mLoginService;
    private IShoppingService mShoppingService;
 
    private ServiceFactory(){}
 
    public static ServiceFactory getInstance() {
        return instance;
    }
 
    public ILoginService getLoginService() {
        if (mLoginService == null){
            mLoginService = new EmptyLoginService();
        }
        return mLoginService;
    }
 
    public void setLoginService(ILoginService mLoginService) {
        this.mLoginService = mLoginService;
    }
 
    public IShoppingService getSignService() {
        if (mShoppingService == null){
            mShoppingService = new EmptyShoppingService();
        }
        return mShoppingService;
    }
 
    public void setSignService(IShoppingService mSignService) {
        this.mShoppingService = mSignService;
    }
}
package com.example.mylibrarySharedLibrary;
 
import android.content.Context;
import android.os.Bundle;
 
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
 
public class EmptyLoginService implements ILoginService {
    @Override
    public void launch(Context ctx, String targetClass) {
 
    }
 
    @Override
    public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
        return null;
    }
}
package com.example.mylibrarySharedLibrary;
 
import android.content.Context;
 
public class EmptySignService implements ISignService  {
    @Override
    public void launch(Context ctx, String userId) {
 
    }
}

In this way, no exception will occur even when we run the main Moudle alone.

The code we jumped above is finished. The next step is to add to the serviceFactory.

private ILoginService mLoginService;
private IShoppingService mSignService;

Assignment

package com.example.sharemodule;
 
import android.app.Application;
 
public interface IComponentApplication {
    void initialize(Application application);
}

Provide unified initialization interface

package com.example.moduledemo;
 
import android.app.Application;
import android.util.Log;
 
import com.example.sharemodule.AppConfig;
import com.example.sharemodule.IComponentApplication;
 
 
public class MainApplication extends Application implements IComponentApplication {
    private static Application application;
 
    public static Application getApplication(){
        return application;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        initialize(this);
    }
 
    @Override
    public void initialize(Application application) {
        for (String cpnt : AppConfig.Components){
            try{
                Class<?> clz = Class.forName(cpnt);
                Object obj = clz.newInstance();
                if (obj instanceof IComponentApplication){
                    ((IComponentApplication) obj).initialize(this);
                }
            }catch (Exception e){
                Log.e("TAG", e.getMessage());
            }
        }
    }
}
package com.example.loginmodule;
 
import android.app.Application;
 
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
 
 
public class LoginApplication extends Application implements IComponentApplication {
 
    private static Application application;
 
    public static Application getApplication(){
        return application;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
 
    @Override
    public void initialize(Application app) {
        application = app;
        ServiceFactory.getInstance().setLoginService(new LoginService());
    }
}
package com.example.shoppingmodule;
 
import android.app.Application;
 
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
 
 
public class ShoppingApplication extends Application implements IComponentApplication {
    private static Application application;
 
    public static Application getApplication() {
        return application;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
 
    }
 
    @Override
    public void initialize(Application app) {
        application = app;
        ServiceFactory.getInstance().setSignService(new SignService());
    }
}
package com.example.sharemodule;
 
public class AppConfig {
    public static final String[] Components = {
            "com.example.shoppingmodule.ShoppingApplication",
            "com.example.loginmodule.LoginApplication"
    };
}

It is initialized when entering the App, and the Application instance of the sub-Module is obtained through reflection for initialization.

Final result:

This article  is included in the open source project: https://github.com/Android-Alvin/Android-LearningNotes , which contains the most fully open source project of Android components (Meituan App, Get App, Alipay App, WeChat App, Mogujie App, Youzan APP...) etc. Resources are being updated continuously...

Guess you like

Origin blog.csdn.net/weixin_43901866/article/details/112492393