美文网首页Android开发Android开发经验谈Android技术知识
Android开发高级进阶之组件化框架搭建

Android开发高级进阶之组件化框架搭建

作者: df556ada620a | 来源:发表于2019-01-12 21:36 被阅读25次

    背景

    当一个项目经过N手人开发,N个产品经理的蹂躏,N长时间的维护,此时一定存在大量代码冗余、业务耦合、项目臃肿,资源文件大把重复等等,不堪重负。当需要增加新功能或者修改之前某个功能的时候,我相信很多同仁都说只敢增加,不敢随意的去删除、修改原有的代码,因为不知道哪些有用,哪些没有用。不但增加了维护成本,也在无形中增加了APK的体积,浪费了资源。 在此背景下,就衍生除了模块化、组件化的概念。目前也已经有很多优秀的案例,我就踩在巨人的肩膀上搭建了符合组件业务的组件化框架。

    先放一个Demo地址,文章末尾也有

    一.浅谈模块

    其基本理念就是,把常用的功能、控件、基础类、第三方库、权限等公共部分抽离封装,把业务拆分成N个模块进行独立(module)的管理,而所有的业务组件都依赖于封装的基础库,业务组件之间不做依赖,这样的目的是为了让每个业务模块能单独运行。而在APP层对整个项目的模块进行组装,拼凑成一个完整的APP。借助路由(Arouter)来对各个业务组件之间的跳转,通过消息(eventbus)来做各个业务模块之间的通信。 模块化的好处:

    • 1.解耦 只要封装做得好,实际开发中会省去大量的重复代码的coding。
    • 2.结构清晰、层次明显,对后面的维护也是极其容易。
    • 3.每个业务模块可独立运行,单独提测,节省开发时间。

    二.基础搭建

    先来一张整个项目构思图

    根据项目构思图搭建的项目结构图


    下面逐一介绍每个模块的功:

    • app模块 app壳没有任何功能主要就是集成每个业务组件,最终打包成一个完整的APK app壳的gradle做如下配置,根据配置文件中的isModule字段来依赖不同的业务组件
    
    dependencise{
          //公用依赖包
        implementation project(':common_base')
        if (!Boolean.valueOf(rootProject.ext.isModule)) {
            //main模块
            implementation project(':module_main')
            implementation project(':module_market')
            implementation project(':module_wan_android')
        }
    }
    
    
    • common_base模块功能组件主要负责封装公共部分,如第三方库加载、网络请求、数据存储、自定义控件、各种工具类等。 为了防止重复依赖问题,所有的第三方库都放在该模块加载,业务模块不在做任何的第三方库依赖,只做common_base库的依赖即可。 common模块无论在什么情况下都是以library的形式存在,所有的业务组件都必须依赖于common 其结构如下:

    在commong的gradle中引入项目中使用的所有第三方库,业务组件就不用再去逐一引入

    apply plugin: 'com.android.library'
    apply plugin: 'com.jakewharton.butterknife'
    
    ...
    
    dependencies {
        // 在项目中的libs中的所有的.jar结尾的文件,都是依赖
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        //把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"]
    
        //eventbus 发布/订阅事件总线
        api rootProject.ext.dependencies["eventbus"]
    
        //网络
        api rootProject.ext.dependencies["novate"]
    
        //日志
        api rootProject.ext.dependencies["logger"]
    
        //fastJson
        api rootProject.ext.dependencies["fastjson"]
    
        //沉浸栏
        api rootProject.ext.dependencies["barlibrary"]
    
        //banner
        api rootProject.ext.dependencies["banner"]
    
        //图片加载
        api rootProject.ext.dependencies["picasso"]
    
        //lombok
        api rootProject.ext.dependencies["lombok"]
        api rootProject.ext.dependencies["lombokJavax"]
    
    }
    
    
    • 业务组件,在集成模式下它以library的形式存在。在组件开发模式下它以application的形式存在,可以单独独立运行。 业务组件完整的gradle如下:
    if (Boolean.valueOf(rootProject.ext.isModule)) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    apply plugin: 'com.jakewharton.butterknife'
    
     ...
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        //公用依赖包
        implementation project(':common_base')
    
        //Arouter路由
        annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
        //黄油刀
        annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
    
    }
    
    
    • 配置文件,对项目中的第三库、app的版本等配置
    /**
     *  全局统一配置文件
     */
    ext {
        //true 每个业务Module可以单独开发
        //false 每个业务Module以lib的方式运行
        //修改之后需要Sync方可生效
        isModule = false
    
        //版本号
        versions = [
                applicationId           : "com.wss.amd",        //应用ID
                versionCode             : 1,                    //版本号
                versionName             : "1.0.0",              //版本名称
    
                compileSdkVersion       : 27,
                buildToolsVersion       : "27.0.3",
                minSdkVersion           : 17,
                targetSdkVersion        : 23,
    
                androidSupportSdkVersion: "27.1.1",
                constraintLayoutVersion : "1.1.1",
                runnerVersion           : "1.0.1",
                espressoVersion         : "3.0.1",
                junitVersion            : "4.12",
                annotationsVersion      : "24.0.0",
    
                multidexVersion         : "1.0.2",
                butterknifeVersion      : "8.4.0",
                arouterApiVersion       : "1.4.0",
                arouterCompilerVersion  : "1.2.1",
                arouterannotationVersion: "1.0.4",
                eventbusVersion         : "3.0.0",
                novateVersion           : "1.5.5",
                loggerVersion           : "2.2.0",
                fastjsonVersion         : "1.1.54",
                barlibraryVersion       : "2.3.0",
                picassoVersion          : "2.71828",
                bannerVersion           : "1.4.10",
                javaxVersion            : "1.2",
                lombokVersion           : "1.16.6",
                greendaoVersion         : "3.2.2",
    
        ]
        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"]}",
    
                        //事件订阅
                        "eventbus"            : "org.greenrobot:eventbus:${versions["eventbusVersion"]}",
    
                        //网络
                        "novate"              : "com.tamic.novate:novate:${versions["novateVersion"]}",
    
                        //日志
                        "logger"              : "com.orhanobut:logger:${versions["loggerVersion"]}",
    
                        //fastJson
                        "fastjson"            : "com.alibaba:fastjson:${versions["fastjsonVersion"]}.android",
    
                        //沉浸式状态栏
                        "barlibrary"          : "com.gyf.barlibrary:barlibrary:${versions["barlibraryVersion"]}",
    
                        //banner
                        "banner"              : "com.youth.banner:banner:${versions["bannerVersion"]}",
    
                        //图片加载
                        "picasso"             : "com.squareup.picasso:picasso:${versions["picassoVersion"]}",
    
                        //lombok
                        "lombokJavax"         : "javax.annotation:javax.annotation-api:${versions["javaxVersion"]}",
                        "lombok"              : "org.projectlombok:lombok:${versions["lombokVersion"]}",
    
                        //数据库
                        "greenDao"            : "org.greenrobot:greendao:${versions["greendaoVersion"]}",
        ]
    
    }
    
    

    最后别忘记在工程的中build.gradle引入该配置文件

    apply from: "config.gradle"
    
    

    修改isModule字段之后 需要Sysn才会生效

    三.搭建过程中遇到的问题

    1.Application、全局ContextActivity管理问题

    • 在功能组件即Demo中的common_base封装BaseApplication,在BaseApplication对第三方库初始化、全局Context的获取等操作。在BaseActivity中对Activity进行添加和移除的管理
    //BaseApplicion
    public class BaseApplication extends Application {
        ...
        //全局唯一的context
        private static BaseApplication application;
    
        //Activity管理器
        private ActivityManage activityManage;
    
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            application = this;
            //MultiDex分包方法 必须最先初始化
            MultiDex.install(this);
        }
        public void onCreate() {
            super.onCreate();
            activityManage = new ActivityManage();
            initARouter();
            initLogger();
        }
      /**
         * 获取全局唯一上下文
         *
         * @return BaseApplication
         */
        public static BaseApplication getApplication() {
            return application;
        }
    }
    
    //BaseActivity
    public abstract class BaseActivity extends Activity {  
        ...
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //加入Activity管理器
            BaseApplication.getApplication().getActivityManage().addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //将Activity从管理器移除
            BaseApplication.getApplication().getActivityManage().removeActivityty(this);
        }
    }
    
    

    2.AndroidManifest的管理

    我们知道APP在打包的时候最后会把所有的AndroidManifest进行合并,所以每个业务组件的Activity只需要在各自模块的AndroidManifest中注册即可。如果业务组件需要独立运行,则需要单独配置一份AndroidManifest,在gradlesourceSets根据不同的模式加载不同的AndroidManifest文件。

    gradle配置

    ...
    android {
       ...
        sourceSets {
            main {
                if (Boolean.valueOf(rootProject.ext.isModule)) {
                    manifest.srcFile 'src/main/module/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                    java {
                        //排除java/debug文件夹下的所有文件
                        exclude '*module'
                    }
                }
            }
        }
    }
    ...
    
    

    注意:在配置Gradle的时候 manifest.srcFile... manifest 是小写的

    其中集成模式加载的Manifest中不能设置Application和程序入口:

    //集成模式下Manifest
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.wss.module.wan">
    
        <application>
            <activity android:name=".main.WanMainActivity" />
        </application>
    </manifest>
    
    //组件模式下Manifest
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.wss.module.wan">
    
        <application
            android:name=".common.WanApplication"
            android:allowBackup="true"
            android:label="@string/app_name"
            android:theme="@style/AdmTheme">
    
            <activity android:name=".main.WanMainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
        </application>
    </manifest>
    
    

    需要注意的是如果在组件开发模式下,组件的Applicaion必须继承自BaseApplicaion

    3.不同组件之间的跳转

    业务组件之间没有依赖,不能通过常规的Intent显示的进行跳转,这个时候就需要引入路由的概念

    利用阿里的ARouter对需要跳转的页面做配置 gradle配置

    android {
       ...
           defaultConfig {
             ...
            //Arouter路由配置
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                    includeCompileClasspath = true
                }
            }
        }
    }
    dependencies{
         ...
        //Arouter路由
        annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
    }
    
    

    目标页面配置

    @Route(path = "/wan/WanMainActivity")
    public class WanMainActivity extends ActionBarActivity<WanMainPresenter> implements IWanMainView, OnRcyItemClickListener {
          ...
    }
    
    

    跳转

    ...
       ARouter.getInstance()
              .build("/wan/WanMainActivity")
              .navigation();
    ...
    
    

    4.不同组件之间通信

    可以利用第三方 如EventBus对消息进行管理。在common_base组件中的Base类做了对消息的简单封装,子类只需要重写regEvent()返回true即可对事件的注册,重写onEventBus(Object)即可对事件的接收。

    public abstract class BaseActivity extends Activity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ...
            if (regEvent()) {
                EventBus.getDefault().register(this);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (regEvent()) {
                EventBus.getDefault().unregister(this);
            }
        }
        /**
         * 子类接收事件 重写该方法
         */
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onEventBus(Object event) {
        }
    
        /**
         * 需要接收事件 重写该方法 并返回true
         */
        protected boolean regEvent() {
            return false;
        }
    
    

    5.butterknife的问题

    library中使用butterknife会存在找不到的问题。 推荐使用8.4.0版本,用R2代替R,onClick中使用if else不要使用switch case即可解决问题 。

    public class HomeFragment extends BaseMvpFragment<HomePresenter> implements IHomeView, OnRcyItemClickListener {
    
        @BindView(R2.id.banner)
        Banner banner;
    
        @BindView(R2.id.recycle_view)
        RecyclerView recyclerView;  
        ...
    
        @OnClick({R2.id.tv_title, R2.id.btn_open})
        public void onClick(View v) {
            if (v.getId() == R.id.tv_title) {
                //do something
    
            } else if (v.getId() == R.id.btn_open) {
                //do something
            }
        }
    
    }
    
    

    6.资源文件冲突问题

    目前没有比较好的约束方式,只能通过设置资源的前缀来防止资源文件冲突,然后在提交代码的时候对代码进行检查是否规范来控制。

    附录

    Android高级工程师进阶技术大纲和系统视频资料

    Android高级工程师进阶技术大纲 Android系统进阶视频资料

    免费获取方式;

    加Android高级进阶群,701740775。进群可免费领取一份最新技术大纲和Android进阶资料。请备注简书

    相关文章

      网友评论

        本文标题:Android开发高级进阶之组件化框架搭建

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