Android组件化框架搭建

栏目: IOS · Android · 发布时间: 7年前

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

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

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

Android组件化框架搭建

一.浅谈模块

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

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

二.基础搭建

先来一张整个项目构思图

Android组件化框架搭建

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

Android组件化框架搭建

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

  • 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 其结构如下:
    Android组件化框架搭建
    在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 文件。

Android组件化框架搭建
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 显示的进行跳转,这个时候就需要引入路由的概念

Android组件化框架搭建

利用阿里的 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组件化框架搭建

其中使用的MVP结构可以参考另一篇文章

最后放上 Demo地址 ,共同学习,有什么不好的地方,欢迎大家指出!

参考文献

移动架构这么多,如何一次搞定所有 戏说移动江湖开发历程 模块化,组件化傻傻分不清?附带组件化福利 寄Android开发Gradle你需要知道的知识 解决组件化开发butterknife 在 library中使用的坑 `


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Practical Algorithms for Programmers

Practical Algorithms for Programmers

Andrew Binstock、John Rex / Addison-Wesley Professional / 1995-06-29 / USD 39.99

Most algorithm books today are either academic textbooks or rehashes of the same tired set of algorithms. Practical Algorithms for Programmers is the first book to give complete code implementations o......一起来看看 《Practical Algorithms for Programmers》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具