Android組件化開發詳解

學習目標:

熟練使用組件化開發,路由配置


學習內容:

在使用組件化開發前首先要明確項目整體框架,劃分模塊及業務(重點),好的開始纔會有好的結果。模塊劃分明確後開始配置Module。

如圖我們要完成以下功能:

1.點擊商城進入ShoppingModule

2.點擊登錄進入LoginModule

3.點擊賬單紅色區域展示賬單列表(其他Module中的Fragment)

(shareModule爲公共模塊)

 根據業務需求創建如下:

在App的gradle.properties文件下添加,用於控制module是否獨立運行。

#配置某個組件是否可以獨立運行
isShoppingRunAlone = true
isLoginRunALone = true

然後配置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')
    }

}

繼續配置其他Module的build.gradle文件。

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')

}

在不同運行模式下使用不同的Manifest文件。

需要在對應module的Main目錄下新建manifest文件夾(不然單獨運行會找不到Manifest文件)。

單獨運行的manifest文件設置如下:

<?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>

併入主Module運行時manifest文件設置如下:

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

    <application>
        <activity android:name=".ShoppingActivity"/>
    </application>
</manifest>

 

全部配置完成之後,可以在gradle.properties中修改變量的值,編譯查看配置是否正確,manifest文件是否替換。運行查看是否正常。

接下來開始配置路由。

好多人心中有疑惑,在引用Module之後是可以直接獲取到子Module的Activity的爲什麼還要使用路由跳轉。是因爲組件化開發是爲了使單獨Module可以獨自編譯,運行如果主Module引用子Module的類名,當子Module單獨運行時主Module會編譯異常。

我們要知道一個項目不可能只有一個子Module,當我們其他子Module要進行相互跳轉時如何使用路由呢?所以我們要在ShareModule進行路由的配置,在之前的配置中我們將ShareModule導入了每個Module。

第一步

我們創建對應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);
}

 第二步

在對應的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;
    }
}

 第三步

接下來我們創建一個ServiceFactory,爲我們提供跳轉實例,並且處理單獨運行時可能會出現的異常

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) {

    }
}

這樣處理即使我們單獨運行主Moudle時也不會發生異常。

以上我們跳轉的代碼就寫完了接下來就是對serviceFactory中

private ILoginService mLoginService;
private IShoppingService mSignService;

進行賦值

package com.example.sharemodule;

import android.app.Application;

public interface IComponentApplication {
    void initialize(Application application);
}

提供統一初始化的接口

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"
    };
}

 進入App時進行初始化,通過反射獲取子Module的Application實例進行初始化。

最終結果:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章