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

作者 : 开心源码 本文共10244个字,预计阅读时间需要26分钟 发布时间: 2022-05-12 共148人阅读

背景

当一个项目经过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进行增加和移除的管理
//BaseApplicionpublic 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;    }}//BaseActivitypublic 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进阶资料。请备注简书

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android开发高级进阶之组件化框架搭建

发表回复