组件化框架配置

一.浅谈模块

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

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

二.基础搭建

先来一张整个项目构思图
在这里插入图片描述
根据项目构思图搭建的项目结构图
在这里插入图片描述

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

项目配置

在project工程目录下的build.gradle文件里,定义isModule 字段作为module和library切换的开关。

ext {
    isModule = false //false:作为Lib组件存在, true:作为application存在

    signingConfig = [
            signFile     : '../app/moduledemo.jks',
            storePassword: '123456',
            keyAlias     : 'moduledemo',
            keyPassword  : '123456'
    ]

    android = [
            applicationId    : "com.lwx.moduledemo",
            moduleAppId_news : "com.lwx.module_news",
            moduleAppId_login: "com.lwx.module_login",

            compileSdkVersion: 26,
            buildToolsVersion: '26.0.3',
            minSdkVersion    : 15,
            targetSdkVersion : 26,
            versionName      : '1.0.0',
            versionCode      : 100
    ]

    deps = [
            supportv4                   : 'com.android.support:support-v4:26.1.0',
            appcompatv7                 : 'com.android.support:appcompat-v7:26.1.0',
            constraintlayout            : 'com.android.support.constraint:constraint-layout:1.1.3',
            design                      : 'com.android.support:design:26.1.0',
            //阿里Arouter
            arouterapi                  : 'com.alibaba:arouter-api:1.5.0',
            aroutercompiler             : 'com.alibaba:arouter-compiler:1.2.2',

            // ------------- Test dependencies -------------
            junit                       : 'junit:junit:4.12',
            testrunner                  : 'com.android.support.test:runner:1.0.2',
            testespresso                : 'com.android.support.test.espresso:espresso-core:3.0.2',
    ]
}

引入阿里的Arouter(文档:https://github.com/alibaba/ARouter)

apply plugin: 'com.alibaba.arouter'

dependencies {
...
    classpath "com.alibaba:arouter-register:1.0.2"
...
}
app壳工程:

app壳没有任何功能主要就是集成每个业务组件,最终打包成一个完整的APK app壳的gradle做如下配置,根据配置文件中的isModule字段来依赖不同的业务组件

defaultConfig {
...
    //Arouter路由配置
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
        }
    }
}

dependencies {
    ...
    implementation project(':common_lib')
    if (!rootProject.ext.isModule) {
        implementation project(':module_news')
    }
    if (!rootProject.ext.isModule) {
        implementation project(':module_login')
    }
    annotationProcessor rootProject.ext.deps.aroutercompiler
 ...
}
common_lib模块:

功能组件主要负责封装公共部分,如第三方库加载、网络请求、数据存储、自定义控件、各种工具类等。 为了防止重复依赖问题,所有的第三方库都放在该模块加载,业务模块不在做任何的第三方库依赖,只做common_base库的依赖即可。 common模块无论在什么情况下都是以library的形式存在,所有的业务组件都必须依赖于common ,

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation rootProject.ext.deps.junit
    androidTestImplementation rootProject.ext.deps.testrunner
    androidTestImplementation rootProject.ext.deps.testespresso
    api rootProject.ext.deps.appcompatv7
    api rootProject.ext.deps.design
    api rootProject.ext.deps.constraintlayout
    api (rootProject.ext.deps.arouterapi){
        exclude module: 'support-v4'//去掉重复依赖,不去掉可能会报错
    }
    annotationProcessor rootProject.ext.deps.aroutercompiler
}
业务组件

在集成模式下它以library的形式存在。在组件开发模式下它以application的形式存在,可以单独独立运行。 业务组件完整的gradle如下:

//切换module和library
if (rootProject.ext.isModule) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
....
defaultConfig {
    if (rootProject.ext.isModule) {
        //组件模式下设置applicationId
        applicationId rootProject.ext.android.moduleAppId_login
    }

    minSdkVersion rootProject.ext.android.minSdkVersion
    targetSdkVersion rootProject.ext.android.targetSdkVersion
    versionCode rootProject.ext.android.versionCode
    versionName rootProject.ext.android.versionName

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    if (!rootProject.ext.isModule) {
        //集成模式下Arouter的配置,用于组件间通信的实现
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
        //强制前缀,解决资源冲突
        resourcePrefix "login_"
    }
}
......
sourceSets {
    main {
        //控制两种模式下的资源和代码配置情况
        if (rootProject.ext.isModule) {
            manifest.srcFile 'src/main/module/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }
    }
}
......
dependencies {
.....
    implementation project(':common_lib')
    if (!rootProject.ext.isModule) {
        //集成模式下需要编译器生成路由通信的代码
        annotationProcessor rootProject.ext.deps.aroutercompiler
    }
.....
}

三.实现Arouter跳转

经过上述配置之后就可以实现Arouter跳转了,我们首先在common_lib库定义一个常量类,这个类就是定义跳转路径的作用:

public class RouterPath {
    public static final String ROUTER_LOGIN= "/module_login/LoginActivity";
    public static final String ROUTER_NEWS= "/module_news/NewsActivity";
}

然后分别在module_login和module_news业务组件中的Activity加上Route注解定义路径

@Route(path = RouterPath.ROUTER_LOGIN)
public class LoginActivity extends AppCompatActivity {}

最后在App工程下的mainActivity中加上点击跳转事件

mBtnLogin.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
//传值
        ARouter.getInstance().build(RouterPath.ROUTER_LOGIN)
                .withString("phone","18212341234").withString("pwd","123456").navigation();
    }
});

mBtnNews.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
//不传值
        ARouter.getInstance().build(RouterPath.ROUTER_NEWS).navigation();
    }
});

四.遇到的问题

1.AndroidManifest的管理

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

gradle配置

...
android {
   ...
    sourceSets {
    main {
        //控制两种模式下的资源和代码配置情况
        if (rootProject.ext.isModule) {
            manifest.srcFile 'src/main/module/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }
    }
}
}
...

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

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

//集成模式下Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lwx.module_login">

    <application>
        <activity android:name=".LoginActivity" />
    </application>
</manifest>

//组件模式下Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lwx.module_login">

    <application
        android:name=".BaseApplication"
        android:allowBackup="true"
        android:label="@string/app_name"
        android:theme="@style/AdmTheme">

        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

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

2.不同组件之间通信

可以利用第三方 如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;
}
3.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
        }
    }

}
4.资源文件冲突问题

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

//强制前缀,解决资源冲突
resourcePrefix "login_"

最后附上demo

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值