首先说一下原理和主要步骤:
组件化的核心就是通过配置Gradle,使子APP可以在独立应用和Library之间自由切换,然后主APP依赖子APP和公共的Library,子APP依赖公共Library。
在主APP初始化的时候,通过反射获取子APP的Application实例,初始化子APP,将主APP的Application传递给子APP使用。将然后子APP暴露接口给公共的Library,主APP通过调用公共Library中接口方法实现与子APP进行交互。
具体步骤:
创建一个主APP,一个library,若干个子APP
- 主APP和子APP作为一个统一的APP,需要有相同的配置,所以在gradle.properties文件中定义配置信息。
- 改变主APP的Gradle配置信息
3.改变公共Library的Gradle配置信息,和主APP相同。
4.在所有子APP分别创建一个新的manifest文件,当子APP处于Library状态时将读取这个manifest文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.happyplay.childone">
<application>
<activity android:name=".MainActivity">
</activity>
</application>
</manifest>
5.配置所有子APP的Gradle配置信息
//根据自定义的boolean值切换application和library状态
if (childOneRunAlone.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
//统一配置参数
compileSdkVersion compile_sdk_version.toInteger()
defaultConfig {
//根据状态判断是否需要设置applicationId
if(childOneRunAlone.toBoolean()){
applicationId "com.happyplay.childone"
}
//统一配置参数
minSdkVersion min_sdk_version.toInteger()
//统一配置参数
targetSdkVersion target_sdk_version.toInteger()
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main {
// 独立运行与集成调试时使用不同的 AndroidManifest.xml 文件
if (childOneRunAlone.toBoolean()) {
//正常的manifest文件路径
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
//后来创建的新的manifest路径
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(':baselibrary')
}
6.让主APP依赖所有子APP和公共Library,让所有子APP都依赖公共Library。
到此为止,Gradle配置完成,可以在gradle.properties文件中修改自定义boolean值切换子APP状态。
7.在公共Library中定义初始化接口
public interface Initiative {
void init(Application app);
}
8.在主APP和子APP中创建自定义Application类,并实现Library中的初始化接口。
9.在公共Library中新建类,创建String数组,将所有子APP的Application文件全类名存储起来。
public class ChildApplicationList {
public static final String [] APPS={"com.happyplay.childone.MyApplication","com.happyplay.childtwo.MyApplication"};
}
10.在主APP初始化的时候,通过反射获取子APP的Application实例,并将自己的Application传给子APP使用。
//主APP自定义Application类,需要在清单文件中配置
public class MyApplication extends Application implements Initiative {
@Override
public void onCreate() {
super.onCreate();
init(this);
}
@Override
public void init(Application app) {
for (String className :ChildApplicationList.APPS) {
try {
//通过反射获取子APP的Application实例
Class<?> clazz = Class.forName(className );
Object o = clazz.newInstance();
if(o instanceof Initiative){
//将主APP的Application传给子APP
((Initiative) o).init(this);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
11.在公共Library中定义子APP对外暴露的接口
public interface ChildOneService {
// 这里只是做一个简单的演示,弹一个吐司
void showToast(Context context,String s);
}
12.在子APP中新建一个类实现这个接口
public class ChildOneServiceImpl implements ChildOneService {
@Override
public void showToast(Context context,String s) {
Toast.makeText(context,s,Toast.LENGTH_SHORT).show();
}
}
13.也在公共Library中新建一个类实现这个接口,方法体都为空即可,防止子APP被删除后报空指针异常。
public class EmptyChildOneService implements ChildOneService {
@Override
public void showToast(Context context, String s) {
}
}
14.在公共Library中新建一个类,接收所有子APP的接口实现类。
public class ServiceFactory {
//子APP一的接口
private ChildOneService childOneService;
private static volatile ServiceFactory serviceFactory;
private ServiceFactory() { }
public static ServiceFactory getInstance() {
if (serviceFactory == null) {
synchronized (ServiceFactory.class) {
if (serviceFactory == null) {
serviceFactory=new ServiceFactory();
}
}
}
return serviceFactory;
}
public ChildOneService getChildOneService () {
//如果子APP一被删除,childOneService会为空,用EmptyChildOneService代替,防止异常。
if(childOneService==null){
childOneService=new EmptyChildOneService();
}
return childOneService;
}
public void setChildOneService (ChildOneService childOneService){
this.childOneService = childOneService;
}
}
14.在子APP初始化的时候将接口实现类传递给公共Library。
public class MyApplication extends Application implements Initiative {
private static Application application;
public static Application getApplication() {
return application;
}
@Override
public void onCreate() {
super.onCreate();
init(this);
}
@Override
public void init(Application app) {
//当子APP状态为library的时候,这里的Application是主APP传递过来的
//当子APP状态为独立APP的时候,这里的Application是自己的。
application = app;
// 将暴露的接口传递给公共Library
ServiceFactory.getInstance().setChildOneService(new ChildOneServiceImpl());
}
}
15.在主APP中调用公共Library中的接口方法即可实现与子APP交互
public void onClick(View view) {
ServiceFactory.getInstance().getChildOneService().showToast(getApplicationContext(),"弹吧");
}
到此为止,模块化开发框架搭建完成,个人感觉和MVP的设计思想很相似。主APP和子APP通过公共Library进行交互,而不是直接持有对方,达到了解耦的目的。这样一人开发一个模块,互不干扰,是大型项目必备的策略。