Android组件化开发启航

2019-03-14  本文已影响0人  caoliang1021

1.背景介绍

  在移动端项目功能不断完善和丰富的过程中我们一直在寻找一种可以高效开发且复用率高的开发模式,特别是多应用同步开发、管理。
  在开发过程中你是否遇到需要发布影子工程?新建项目是否需要耗费大量时间将原有基类、工具类、第三方通用类库逐个copy进新项目中?在项目不断迭代功能越来越丰富的同时是否发现项目编译时间不断增加?组件化开发就可以很好的解决上述这些问题,它实际是将一个完整的项目划分为若干个模块,过程类似搭积木,一个一个拼接,你可以单独使用其中任意一个,也可以用多个拼接出各种形状,达到高效开发,最大复用。

在了解组件化开发之前我们需要先了解组件和模块这两个概念

2.系统架构图

系统架构图.png
项目结构.png

项目由主项目app,商城模块mall-module,会员模块member-module,公共模块common-module各个基类(BaseActivity/BaseApplication/BaseAdapter等)、工具类、网络、图片等一些公共常用的第三方sdk:

3.实践

apply plugin: 'com.alibaba.arouter'

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        classpath "com.alibaba:arouter-register:1.0.2"
    }
}

allprojects {
    repositories {
        google()
        jcenter { url "http://jcenter.bintray.com/" }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

项目gradle.properties全局配置文件:

#IS_MAIN_APP true主项目为应用 mall-module为library false mall-module为应用可单独启动
IS_MAIN_APP=true
#AS 编译配置
COMPILE_SDK_VERSION=28
BUILDTOOLS_VERSION=28.0.3
MIN_SDK_VERSION=15
TARGET_SDK_VERSION=25
#版本号
APP_VERSION=1
APP_VERSION_CODE=1.0.1
if (IS_MAIN_APP.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {

    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
    buildToolsVersion BUILDTOOLS_VERSION

    defaultConfig {
        if (IS_MAIN_APP.toBoolean()) {
            applicationId "com.fenlibao.arouter"
        }
        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
        versionCode Integer.parseInt(APP_VERSION)
        versionName APP_VERSION_CODE
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            if (IS_MAIN_APP.toBoolean()) {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            }
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
    compile project(':common-module')//公共方法 基类 放在该项目中
    if (IS_MAIN_APP.toBoolean()) {
        compile project(':mall-module')//商城模块
        compile project(':member-module')//会员模块
    }

}

if (IS_MALL_APP.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
    buildToolsVersion BUILDTOOLS_VERSION
    defaultConfig {
        if (IS_MALL_APP.toBoolean()) {
            applicationId "com.fenlibao.mall"
        }
        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
        versionCode Integer.parseInt(APP_VERSION)
        versionName APP_VERSION_CODE
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        resourcePrefix "mall_"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            if (IS_MALL_APP.toBoolean()) {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            }
        }
    }
}

dependencies {
    compile project(':common-module')
    compile 'com.android.support.constraint:constraint-layout:1.1.3'
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
}

apply plugin: 'com.android.library'
android {
    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
    buildToolsVersion BUILDTOOLS_VERSION
    defaultConfig {
        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
        versionCode Integer.parseInt(APP_VERSION)
        versionName APP_VERSION_CODE
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ AROUTER_MODULE_NAME : project.getName() ]
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:28.0.0'
    compile 'com.android.support:design:28.0.0'
    compile 'com.android.support.constraint:constraint-layout:1.1.3'
    testCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:1.0.2'
    androidTestCompile('com.android.support.test.espresso:espresso-core:3.0.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
//该项目中suppourt包为25.2.0版本比较低 顾做排除处理
    compile('com.alibaba:arouter-api:1.4.1') {
        exclude group: 'com.android.support'
    }
    //Rxjava2
    compile 'io.reactivex.rxjava2:rxjava:2.2.6'
    compile 'io.reactivex.rxjava2:rxandroid:2.1.0'
    //Retrofit2
    compile 'com.squareup.retrofit2:retrofit:2.4.0'
    compile 'com.squareup.retrofit2:converter-gson:2.4.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    //Okhttp-interceptor
    compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
}

4.注意事项

  1. 通过反射方式
try {
    Class clazz = Class.forName("com.fenlibao.member.MainActivity");
    Intent intent = new Intent(_activity, clazz);
    startActivity(intent);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
  1. 使用ARouter注解方式,实现映射关系自动注册
/**
 * ARouter在MyApplication中初始化
 */
public class MyApplication extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        init();
    }

    private void init() {
        if (isDebug()) {
            ARouter.openLog();
            ARouter.openDebug();
        }
        ARouter.init(this);
    }

    public boolean isDebug() {
        return getApplicationInfo() != null && (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        ARouter.getInstance().destroy();
    }
}

/**
 * 在常量类中定义静态字符串常量
 */
public static final String MEMBER_1_URL_MAIN = "/member/MainActivity";
/**
 * 在对应module Activity中使用@Route标签进行绑定
 */
@Route(path = RoutePath.MALL_1_URL_MAIN)
public class MainActivity extends BaseActivity {
}
/**
 * 在需要跳转至该页面build方法中传入对应Activity字符串常量,即可实现跳转
 */
ARouter.getInstance().build(RoutePath.MEMBER_1_URL_MAIN).navigation();
  1. 在common-module中定义BaseApplication继承Application。
**
 * 项目Application基类主项Application需集成此类
 */
public class BaseApplication extends Application {
    /**
     * 系统上下文
     */
    private static BaseApplication mAppContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mAppContext = this;
    }
    /**
     * 获取系统上下文单例
     */
    public static BaseApplication getInstance() {
        return mAppContext;
    }
}

  1. 在app项目中MyApplication(上部分代码示例中可以查看)继承BaseApplication,同时设置到AndroidManifest.xml application标签下name属性中。
  2. 在app及module中都可通过BaseApplication.getInstance();方法即可获取应用上下文对象,如下:
BaseApplication application = BaseApplication.getInstance();
##IS_MAIN_APP true主项目为应用,mall-module为library;false mall-module为应用可单独启动
IS_MAIN_APP=true
#app build.gradle
android {
    sourceSets {
            main {
                if (IS_MAIN_APP.toBoolean()) {
                    manifest.srcFile 'src/main/release/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/debug/AndroidManifest.xml'
                }
            }
        }
}
#mall-module build.gradle
android {
    sourceSets {
            main {
                if (!IS_MAIN_APP.toBoolean()) {
                    manifest.srcFile 'src/main/release/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/debug/AndroidManifest.xml'
                }
            }
        }
}
  1. 在module项目中build.gradle配置文件中设置
resourcePrefix "mall_"

通过给模块设置不同的前缀,避免资源文件重名覆盖问题。
2.良好的命名(资源文件/类文件)习惯,需以module前缀开头,例如mall-module布局资源文件,采用mall_activity_home,方式避免资源文件重名覆盖问题,如果资源文件不按resourcePrefix "mall_" 定义前缀命名会有错误提示,例如以如下方式命名布局资源文件 activity_mall_home.xm:


错误提示.png

5.总结

  以上就是组件化开发的详细步骤和相关注意事项,如果你面前是一个庞大的工程,建议你使用该方案,以最小的代价尽快开始实施组件化。如果你现在负责的是一个开发初期的项目,代码量还不大,那么也建议尽快进行组件化的规划,不要给未来的自己增加徒劳的工作量。
项目示例Github地址:https://github.com/guixia/AndroidModule

上一篇 下一篇

猜你喜欢

热点阅读