前言:
实际开发中越是大型的项目,代码量越多,而AndroidStudio编译的速度越慢。除了抬高电脑配置外,如何提高程序员的开发效率越是迫在眉睫。除此之外,团队合作开发,合并代码也是一个头疼的问题。虽然可以使用svn/git来规避一些问题,但团队中一个人的代码出了问题,导致自己也是被迫停止开发也是可能的。
组件化:
项目代码臃肿的时候,通常考虑拆分代码,分层的方式。组件化是将项目按照业务拆分成一个个组件,进而达到解耦分层。一个组件化必须能够单独调试,集成编译,数据传输,UI转跳,生命周期,代码边界这六大特征。
通过一个组件化模型来进一步了解:
通过上图可知:
工欲善其事必先利其器,不能因配置问题导致开发拖延,因此先配置好需要的各种库。
本项目使用到以下库
先创建一个config.gradle,编写以下代码:
// 第三方库,App的版本号的管理和组件模式的切换配置
ext{
isAlone=false; //false:作为Lib集成存在, true:作为application组件存在
android = [
compileSdkVersion : 27,
minSdkVersion :15,
targetSdkVersion :27,
versionCode :1,
versionName :"1.0",
]
libsVersion=[
// App dependencies version
supportLibraryVersion = "27.0.2",
support_v4 = "27.0.2",
arouter_api = "1.3.1",
arouter_compiler = "1.1.4",
sqlBrite="1.1.1",
rxJava="1.3.0",
rxAndroid="1.2.1",
retrofit="2.3.0",
converter_gson="2.3.0",
adapter_rxjava="2.3.0",
okhttp="3.8.0",
logging_interceptor="3.8.0",
glide="3.8.0",
]
dependencies = [
//android 官方库
appcompatV7 : "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion",
support_v4 : "com.android.support:support-v4:$rootProject.support_v4",
design : "com.android.support:design:$rootProject.supportLibraryVersion",
cardview : "com.android.support:cardview-v7:$rootProject.supportLibraryVersion",
palette : "com.android.support:palette-v7:$rootProject.supportLibraryVersion",
recycleview : "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion",
annotations : "com.android.support:support-annotations:$rootProject.supportLibraryVersion",
//路由通讯
arouter_api : "com.alibaba:arouter-api:$rootProject.arouter_api",
arouter_compiler : "com.alibaba:arouter-compiler:$rootProject.arouter_compiler",
// SQLBrite
sqlBrite :"com.squareup.sqlbrite:sqlbrite:$rootProject.sqlBrite",
//RxJava
rxJava :"io.reactivex:rxjava:$rootProject.rxJava",
rxAndroid :"io.reactivex:rxandroid:$rootProject.rxAndroid",
// OkHttp
okhttp :"com.squareup.okhttp3:okhttp:$rootProject.okhttp",
logging_interceptor :"com.squareup.okhttp3:logging-interceptor:$rootProject.logging_interceptor",
//retrofit
retrofit :"com.squareup.retrofit2:retrofit:$rootProject.retrofit",
converter_gson :"com.squareup.retrofit2:converter-gson:$rootProject.converter_gson",
adapter_rxjava :"com.squareup.retrofit2:adapter-rxjava:$rootProject.adapter_rxjava",
//glide
glide :"com.github.bumptech.glide:glide:$rootProject.glide",
]
}
接下来,在Project的build.gradle中添加以下代码,进行配置引用。
apply from:"config.gradle"
在每一个module组件中,进行依赖。
先在CommonLibrary基类库中builde.gradle进行依赖:
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//Android Support ,任何一个module依赖的官方包都在这里配置
api(rootProject.ext.dependencies.appcompatV7) {
exclude module: "support-annotations"
exclude module: "support-v4"
}
api(rootProject.ext.dependencies.support_v4) {
exclude module: "support-annotations"
}
api rootProject.ext.dependencies.recycleview
api rootProject.ext.dependencies.design
api rootProject.ext.dependencies.annotations
// arouter 依赖
api rootProject.ext.dependencies.arouter_api
//rxJava
api(rootProject.ext.dependencies.rxJava) {
exclude module: "rxAndroid"
}
api rootProject.ext.dependencies.rxAndroid
//sqlbrite
api rootProject.ext.dependencies.sqlBrite
//retrofit
api rootProject.ext.dependencies.retrofit
api rootProject.ext.dependencies.converter_gson
api rootProject.ext.dependencies.adapter_rxjava
// okhttp
api rootProject.ext.dependencies.okhttp
api rootProject.ext.dependencies.logging_interceptor
//glide
api rootProject.ext.dependencies.glide
}
最后,每一个业务组件(例如:movie组件和collection组件)的build.gradle依赖:
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
defaultConfig {
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.isAlone) {
// 组件模式下设置applicationId
applicationId "com.xingen.collection"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
}
dependencies {
implementation project(':CommonLibrary')
//集成模式下需要编译器生成路由通信的代码
annotationProcessor rootProject.ext.dependencies.arouter_compiler
}
将每个业务组件需要用到的通用操作都封装到一个通用的commonlibrary基类库中,然后直接在每个业务组件中依赖该库。
如下图所示,将一些常见的操作,数据库实体类,网络操作,图片加载,mvp架构等等。 除此之外,统一的style风格,权限也应该定义在CommonLibrary中。
除此之外,每个组件的Application问题。如何获取每个组件的通用Application?
写一个超类BaseApplication,将一些常见初始化操作放到里面.
public class BaseApplication extends Application {
private static BaseApplication instance;
@Override
public void onCreate() {
super.onCreate();
instance=this;
initRouter();
}
public static BaseApplication getInstance(){
return instance;
}
private void initRouter(){
if (BuildConfig.DEBUG) {
//一定要在ARouter.init之前调用openDebug
ARouter.openDebug();
ARouter.openLog();
}
ARouter.init(this);
}
}
写一个工具类,让每个组件可以获取到这个BaseApplication。
public class ContextUtils {
public static BaseApplication getAppContext(){
return BaseApplication.getInstance();
}
}
然后让每个组件的Application去继承这个BaseApplication。以movie组件为例。
public class MovieListApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
}
}
最后在组件的Androidmanifest.xml中去引用该定义的MovieListApplication。
组件化具备每个组件单独调试和集成的特点。
在config.gradle中设置一个标志,便于切换。
ext{
isAlone=false; //false:作为Lib集成存在, true:作为application组件存在
}
组件的组件模式是指单独调试的Application项目,集成模式是指library项目,依赖到空壳app中。
这里以movie组件为案例,来介绍,在其build.gradle中。
//控制组件模式和集成模式
if (rootProject.ext.isAlone) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
defaultConfig {
if (rootProject.ext.isAlone) {
// 组件模式下设置applicationId
applicationId "com.xingen.movie"
}
}
}
每次切换模式,AndroidStudio需要Sync
下,在运行对应的项目。
4.1 组件在集成模式和组件模式下的代码和AndroidManifest.xml
在组件模式下,需要单独调试,具备MainActivity和Applciation类。
这里以Movie组件为例,在java文件夹下创建一个debug文件夹,放入对应MainActivity和Applciation类。
在main文件夹下创建一个module文件夹,放入组件模式下的AndroidManifest.xml。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xingen.movie">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name="debug.MovieListApplication"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.OverSystemBar">
<activity android:name="debug.MovieListActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
manifest>
而集成模式下的Androidmanifext.xml,不能有Application和主的Activity,不能声明App名称、图标等属性。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xingen.movie">
<application android:theme="@style/AppTheme.OverSystemBar">
<activity android:name="debug.MovieListActivity">activity>
application>
manifest>
最后在gradle配置,自由切换集成模式和组件模式下的不同代码。
android {
sourceSets {
main {
//控制两种模式下的资源和代码配置情况
if (rootProject.ext.isAlone) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//集成开发模式下排除debug文件夹中的所有Java文件
java {
exclude 'debug/**'
}
}
}
}
}
最终,movie组件项目的结构,如下所示:
4.2 资源命名冲突
若是多个组件项目中都具备相同名字的图标,那么集成打包App会报错。
最好的解决方式:就是每个组件的资源文件(图片,xml)都约定好以什么组件名为前缀。
4.3 library依赖问题
第三方依赖库可能到引入重复包问题,通过exclude module
去除指定重复包,具体详情查看1.先配置依赖库和版本号统一的管理
。
模块之间的通讯通过Arouter来实现。
例如,main模块中要获取到movie模块中的一个Fragment类,但两个模块又不能直接依赖。通过Arouter的暴露服务来通讯可以解决。
先在CommonLibraray中定义一个IProvider子类,定义一个服务.
public interface MovieListProvider extends IProvider {
Fragment createFragment();
}
定义该服务的路径:
public final class RouterPath {
/**
* 电影列表的路径
*/
public static final String PATH_MOVIE_LIST="/MovieService/movie_list";
}
然后在Movie组件中,实现该服务。
@Route(path = RouterPath.PATH_MOVIE_LIST)
public class MovieListProviderImpl implements MovieListProvider {
@Override
public Fragment createFragment() {
MovieListFragment fragment=MovieListFragment.newInstance();
MovieListPresenter presenter=new MovieListPresenter(fragment, NetClient.getInstance(), SQLClient.getInstance());
return fragment;
}
@Override
public void init(Context context) {
}
}
最后在main组件中获取该服务对象,从而获取到movie组件中的fragment。
public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener {
@Autowired
MovieListProvider movieListProvider;
@Override
public void init(Bundle savedInstanceState)
ARouter.getInstance().inject(this);
Fragment fragment = movieListProvider.createFragment();
getSupportFragmentManager().beginTransaction().add(R.id.movielist_content_layout, fragment).commit();
}
}
当然,还有很多关于Arouter的用法,请点击查看Arouter文档。
单独Movie组件效果:
全部组件集成到空壳App的效果:
资源参考:
浅谈Android组件化:https://mp.weixin.qq.com/s/RAOjrpie214w0byRndczmg
Android组件化方案:https://blog.csdn.net/guiying712/article/details/55213884