Android组件化设计

作者: 吃泥巴的猫 | 来源:发表于2018-12-09 17:51 被阅读31次

    随着app迭代,以及功能需求的更新,项目中的冗余代码量增多,代码各模块间的耦合增强,不利于更新以及测试,所以开始了组建化的设计。

    android的组件化基于module的两种属性
    application属性,module可以作为独立的app运行
    apply plugin: ‘com.android.application’
    library属性,module不可独立运行,作为依赖库文件
    apply plugin: ‘com.android.library’
    通过控制module的此属性可以间module在app以及依赖库之间进行切换,此属性在build.gradle文件中。
    而且项目的gradle文件是可以公用的,所以可以新建一个config.gradle文件进行对module以及一些依赖进行配置。

    /**
     *  全局统一配置文件
     */
    ext {
        //true 每个业务Module可以单独开发
        //false 每个业务Module以lib的方式运行
        //修改之后需要Sync方可生效
        isModule = false
    
        //版本号
        versions = [
                applicationId           : "com.example.bwq.moduletest",        //应用ID
                versionCode             : 1,                    //版本号
                versionName             : "1.0.0",              //版本名称
    
                compileSdkVersion       : 28,
                buildToolsVersion       : "27.0.3",
                minSdkVersion           : 16,
                targetSdkVersion        : 28,
    
                androidSupportSdkVersion: "28.+",
                constraintLayoutVersion : "1.1.3",
                runnerVersion           : "1.0.2",
                espressoVersion         : "3.0.2",
                junitVersion            : "4.12",
    
                multidexVersion         : "1.0.2",
                butterknifeVersion      : "8.8.1",
                arouterApiVersion       : "1.4.0",
                arouterCompilerVersion  : "1.2.1",
                arouterannotationVersion: "1.0.4",
                recyclerview            : "28.+",
                cardview                : "28.+",
                //三方依赖的版本控制
        ]
        dependencies = ["appcompat_v7"        : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}",
                        "constraint_layout"   : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}",
                        "runner"              : "com.android.support.test:runner:${versions["runnerVersion"]}",
                        "espresso_core"       : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
                        "junit"               : "junit:junit:${versions["junitVersion"]}",
                        "support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
                        "design"              : "com.android.support:design:${versions["androidSupportSdkVersion"]}",
                        "support-v4"          : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}",
                        "cardview-v7"         : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}",
                        "recyclerview-v7"     : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}",
    
                        //方法数超过65535解决方法64K MultiDex分包方法
                        "multidex"            : "com.android.support:multidex:${versions["multidexVersion"]}",
    
                        //路由
                        "arouter_api"         : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
                        "arouter_compiler"    : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
                        "arouter_annotation"  : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}",
    
                        //黄油刀
                        "butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
                        "butterknife"         : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
    
                        recyclerview          : "com.android.support:recyclerview-v7:${versions["recyclerview"]}",
                        cardview              : "com.android.support:cardview-v7:${versions["cardview"]}",
                        //三方依赖的库地址
        ]
    }
    

    其中isModule为控制module为组建模式还是app模式的开关
    因此组建module的属性可通过开关来判断,在组建build.gradle文件开头设置为如下:

    if (Boolean.valueOf(rootProject.ext.isModule)) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    

    在组建化中我将app分为三层,
    第一层:app层
    app入口,初始化页面,以及主页通过在主页viewpager中添加fragment使其成为正常app的主页。
    第二层:组建层
    将项目按功能划分为各个组建,如登录组建,首页组建,我的组建等,组建间通过路由的方式跳转以及传递数据。
    第三层:基础层
    项目基础层,公用控件,公用的方法资源文件,网络请求方法,以及依赖库的封装都在这层进行实现。
    组建层以及app层的每一个module均依赖基础层,app层依赖每一个组建层以及基础层。
    基础层中将在配置文件中的依赖方法,进行依赖,基础层不需要成为app独立运行,因此无需设置application以及library的开关,依赖关键字使用api:

    apply plugin: 'com.android.library'
    apply plugin: 'com.jakewharton.butterknife'
    android {
        compileSdkVersion rootProject.ext.versions.compileSdkVersion
        buildToolsVersion rootProject.ext.versions.buildToolsVersion
    
        defaultConfig {
            minSdkVersion rootProject.ext.versions.minSdkVersion
            targetSdkVersion rootProject.ext.versions.targetSdkVersion
            versionCode rootProject.ext.versions.versionCode
            versionName rootProject.ext.versions.versionName
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            //MultiDex分包方法
            multiDexEnabled true
    
            //Arouter路由配置
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                    includeCompileClasspath = true
                }
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
    }
    
    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        //把implementation 用api代替,它是对外部公开的, 所有其他的module就不需要添加该依赖
        api rootProject.ext.dependencies["appcompat_v7"]
        api rootProject.ext.dependencies["constraint_layout"]
        api rootProject.ext.dependencies["cardview-v7"]
        api rootProject.ext.dependencies["recyclerview-v7"]
        api rootProject.ext.dependencies["support-v4"]
        api rootProject.ext.dependencies["design"]
        api rootProject.ext.dependencies["support_annotations"]
        //MultiDex分包方法
        api rootProject.ext.dependencies["multidex"]
        //黄油刀
        annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
        api rootProject.ext.dependencies["butterknife"]
        //Arouter路由
        annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
        api rootProject.ext.dependencies["arouter_api"]
        api rootProject.ext.dependencies["arouter_annotation"]
        api rootProject.ext.dependencies["recyclerview"]
        api rootProject.ext.dependencies["cardview"]
        
    }
    

    第二层中因每个组建均需要成为独立app进行测试以及开发,因此,他的manifest文件需要两套,一套为普通library的没有app入口,一套为application的有app入口的文件。
    新建module,选择phone&table module,建立标准app module,main包下新建路径manifest放置module作为app时的manifest文件


    project显示模式下main文件夹

    黄色框为新建文件夹以及新建文件:
    manifest配置为基础app配置

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.bwq.main">
    
        <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/MyAppTheme"
            android:name=".debug.MainApplication">
            <activity android:name=".debug.MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    红色框中的manifest文件为library时的

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.bwq.main">
    
        <application>
            <activity android:name=".debug.MainActivity"/>
    
        </application>
    
    </manifest>
    

    在java中新建debug包,放置一些作为独立app时的配置,如继承自基础层的Application类,如果组建入口为fragment时展示fragment的activity,之后在当前module的builde.gradle中配置

    if (Boolean.valueOf(rootProject.ext.isModule)) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    apply plugin: 'com.jakewharton.butterknife'
    android {
        compileSdkVersion rootProject.ext.versions.compileSdkVersion
        buildToolsVersion rootProject.ext.versions.buildToolsVersion
    
        defaultConfig {
            if (Boolean.valueOf(rootProject.ext.isModule))
                applicationId rootProject.ext.versions.applicationId
            minSdkVersion rootProject.ext.versions.minSdkVersion
            targetSdkVersion rootProject.ext.versions.targetSdkVersion
            versionCode rootProject.ext.versions.versionCode
            versionName rootProject.ext.versions.versionName
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            //MultiDex分包方法
            multiDexEnabled true
    
            //Arouter路由配置
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                    includeCompileClasspath = true
                }
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        sourceSets {
            //配置当前module作为app和作为library时的manifest文件以及作为library时所需要忽略的文件
            main {
                if (Boolean.valueOf(rootProject.ext.isModule)) {
                    manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                    java {
                        exclude '*debug/**'
                        exclude '*manifest/**'
                    }
                }
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        //butterKnife
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
        annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
        api project(':basis_library')
    }
    

    为了方便组建间的跳转,在基础层中BaseApplication类中初始化路由,BaseApplication为所有组建以及app的Application类的基类。

    /**
         * 初始化ARouter
         */
        public void initARouter() {
            if (BuildConfig.DEBUG) {
                ARouter.openLog();
                ARouter.openDebug();
            }
            ARouter.init(instance);
        }
    

    增加组建化后的跳转工具类

    public class ARouterUtils {
    
    
        /**
         * 根据path返回Fragment
         *
         * @param path path
         * @return fragment
         */
        public static BaseLazyFragment getLazyFragment(String path) {
            return (BaseLazyFragment) ARouter.getInstance()
                    .build(path)
                    .navigation();
        }
    
        public static BaseFragment getFragment(String path) {
            return (BaseFragment) ARouter.getInstance()
                    .build(path)
                    .navigation();
        }
    
        /**
         * 根据path返回Activity
         *
         * @param path path
         * @return Activity
         */
        public static BaseActivity getActivity(String path) {
            return (BaseActivity) ARouter.getInstance()
                    .build(path)
                    .navigation();
        }
    
        /**
         * 根据path返回FragmentActivity
         *
         * @param path path
         * @return FragmentActivity
         */
        public static BaseFragmentActivity getFragmentActivity(String path) {
            return (BaseFragmentActivity) ARouter.getInstance()
                    .build(path)
                    .navigation();
        }
    }
    

    路由跳转URL地址

    /**
     * 功能模块入口
     */
    
    public interface ARouterConfig {
        /**
         * 登录页面
         */
        String LOGIN = "/login/LoginActivity";
        /**
         * main
         */
        String MAIN = "/main/MainFragment";
        /**
         * mine
         */
        String MINE = "/mine/MineFragment";
    }
    

    app层:
    builde.gradle配置:

    apply plugin: 'com.android.application'
    apply plugin: 'com.jakewharton.butterknife'
    android {
        compileSdkVersion rootProject.ext.versions.compileSdkVersion
        buildToolsVersion rootProject.ext.versions.buildToolsVersion
        defaultConfig {
            applicationId rootProject.ext.versions.applicationId
            minSdkVersion rootProject.ext.versions.minSdkVersion
            targetSdkVersion rootProject.ext.versions.targetSdkVersion
            versionCode rootProject.ext.versions.versionCode
            versionName rootProject.ext.versions.versionName
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            //MultiDex分包方法
            multiDexEnabled true
    
            //Arouter路由配置
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                    includeCompileClasspath = true
                }
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
        annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
        api project(':basis_library')
        if (!Boolean.valueOf(rootProject.ext.isModule)) {
            api project(':main')
            api project(':login')
            api project(':mine')
        }
    }
    

    至此,组建化方案完成。

    最后说一些注意的问题,各层的依赖关键字需要使用api,不同组建间的资源文件由于在组合后可能会有冲突,各组建间的资源文件建议以组建名开头,如login_back

    相关文章

      网友评论

        本文标题:Android组件化设计

        本文链接:https://www.haomeiwen.com/subject/whftcqtx.html