组件化开发的好处有很多,随便想想就能列出几点
1. 利于多人合作开发,关注自己的部分,解耦程度很高。
2. moudle可以单独运行,开发效率快。
3. 测试方便,修改哪个部分,就可以直接打包测试,而不需要牵一发而动全身。
组件化开发的好处不止这些,但是发现并不是所有的项目都会改为组件化开发,因为组件化开发需要进行配置,过程比较麻烦,任何的开发方式都不是完美的。组件化更多的是用于大型多人合作的项目。
这篇文章主要是详细的去说明如何去配置,以及如何让两个模块去交互,而且是基于最原始的手写方式,没有用ARouter或其它框架。在一开始学习的时候,选择不用框架,写最基本的代码,这样更能理解其中的原理,甚至在配置的时候还会发出,代码还能这样写的感叹。
我画了一张简易的组件化项目架构图。

从图可以看到,我们主App(也就是Main App)里面有很多个Moudle,还有一个Library。现在用两个Moudle来举例,我们新建一个项目,然后再新建两个Moudle,名字分别为Logincomponent和minecomponent。再建一个基础的Library,名字为componentlib。项目结构如图。

接下来就开始进行配置了,所有的操作都是为了解决以下几个问题。
1. 两个Moudle,要做到Application与Library之间自由切换
2. Moudle之间的交互要通过componentlib。(这是为了降低耦合度,ARouter就是做这部分的工作。)
3. 当Moudle为Library的时候,要拿到主App的Application
先进行全局配置,sdk的版本、以及配置两个Moudle是否可单独运行。在项目的gradle.properties下面配置。

Logincomponent和minecomponent两部分需要的配置都是一样的,下面拿Logincomponent举例,为了写起来方便,下面直接简写为login和mine。
将Login部分的sdk版本改为引用全局的版本。要做到Application与Library的切换,就需要对一些配置进行灵活切换。它们的属性不同, apply的内容也不同。Appliction是有applicationId的,Library没有。Application和Library的AndroidManifest.xml里面的内容也不同。针对以上的问题,需要在Login的Build.gradle文件进行区分配置,代码如下:

如果是Moudle,代码不变,是Library,则
AndroidManifest.xml的内容就很简单,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.logincomponent">
<application
>
<activity android:name=".LoginActivity"/>
</application>
</manifest>
对于主App来说,如果是Library就会被主app依赖,所以在主app的build.gradle的dependencies{}里加入以下代码:
if (!loginRunAlone.toBoolean()){
implementation project(path: ':logincomponent')
}
if (!mineRunAlone.toBoolean()){
implementation project(path: ':minecomponent')
}
implementation project(path: ':componentlib')
当login是app可以独立运行的时候,基础的componentlib也会被依赖,build.gradle的dependencies{}里加入代码implementation project(path: ':componentlib')即可。
基础的一些配置基本完成,现在要考虑另外一个重要问题,login和Mine的交互是通过基础模块componentLib,这样达到解耦的目的,那么我们该如何编写呢。
在ComponentLib写两个接口,ILoginService和IMineService。
public interface ILoginService {
void launch(Context context,String targetClass);
Fragment newuserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle);
}
public interface IMineService {
void launch(Context context, int userId);
}
还要写一个类来设置和获取到它们。
public class ServiceFactory {
private static final ServiceFactory instance = new ServiceFactory();
public static ServiceFactory getInstance(){
return instance;
}
private ServiceFactory(){
}
private ILoginService iLoginService;
private IMineService iMineService;
public ILoginService getiLoginService() {
return iLoginService;
}
public void setiLoginService(ILoginService iLoginService) {
this.iLoginService = iLoginService;
}
public IMineService getiMineService() {
return iMineService;
}
public void setiMineService(IMineService iMineService) {
this.iMineService = iMineService;
}
}
有了接口,就需要去实现它,在相应的方法里面进行相应的操作,拿Login举例,新建一个类实现ILoginService接口。
public class LoginService implements ILoginService {
@Override
public void launch(Context context, String targetClass) {
Intent intent = new Intent(context,LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
public Fragment newuserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
UserInfoFragment userInfoFragment = new UserInfoFragment();
userInfoFragment.setArguments(bundle);
fragmentManager.beginTransaction().add(viewId,userInfoFragment).commit();
return userInfoFragment;
}
}
当Login和Mine为Library的时候,是需要用主app的Application的,接下来我们就要拿到主app的Application。
在componentlib写AppConfig,里面是Login和Mine的具体application类名
public class AppConfig {
public static final String[] COMPONENTS = {
"com.example.logincomponent.LoginApplication","com.example.maincomponent.MineApplication"
};
}
还要写一个接口
public interface IComponentApp {
void initializal(Application application);
}
Login和Library实现这个接口
public class LoginApplication extends Application implements IComponentApp {
private static Application application;
public static Application getApplication(){
return application;
}
//独立App时候会调用onCreate(),组件的时候就不会
@Override
public void onCreate() {
super.onCreate();
initializal(this);
}
@Override
public void initializal(Application app) {
application = app;
ServiceFactory.getInstance().setiLoginService(new LoginService());
}
}
在主app里将appLication传过去。
public class MainApplication extends Application implements IComponentApp {
private static Application application;
public static Application getApplication(){
return application;
}
@Override
public void onCreate() {
super.onCreate();
initializal(this);
}
@Override
public void initializal(Application application) {
for (String componet : AppConfig.COMPONENTS){
try{
//通过配置文件中的Application的路径,实例化并将MainApp的Application传过去
Class<?> clazz = Class.forName(componet);
Object object = clazz.newInstance();
if (object instanceof IComponentApp){
((IComponentApp)object).initializal(this);
}}catch (Exception e){
e.printStackTrace();
}
}
}
}
最后在MainActivity去调用就可以进行跳转了
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv_to_login).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动login组件
ServiceFactory.getInstance().getiLoginService().launch(MainActivity.this,"");
}
});
findViewById(R.id.main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//跳转到“我的”组件
ServiceFactory.getInstance().getiMineService().launch(MainActivity.this,0);
}
});
findViewById(R.id.showUI).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//显示“login”模块中的UI
Bundle bundle = new Bundle();
ServiceFactory.getInstance().getiLoginService().newuserInfoFragment(getSupportFragmentManager(),R.id.container,null);
}
});
}
}
代码写到这里,是会感觉很繁琐。现在一起来按照正常的app顺序捋一遍。在一开始运行的时候,就会调用主app的MainApplication类的onCreate(),从而调用initializal(),这一步就是初始化Login和mine的application,通过反射,login和mine会执行它们的initializal(),拿login举例,执行以下代码:
@Override
public void initializal(Application app) {
application = app;
ServiceFactory.getInstance().setiLoginService(new LoginService());
}
这就是拿到了application,并且设置了loginService。接着在MainActivity就得到了这个loginService,最后调用了相应的lanuch(),实现了Activity跳转。整个过程就是这样的,也可以打断点调试,并不复杂。
组件化的好处,就是比如有login和mine等很多业务,某一天发现不需要login业务,就可以直接删掉。重新回忆一下刚刚写的代码,好像并不能直接删掉,所以还需要进一步改善。发现当mine删掉的时候,调用ServiceFactory.getInstance().getiLoginService()会报空,按照常规做法就是再调用的时候判断一下,现在有一个更好维护代码的方法。在componentlib新建一个EmptyService类继承ILoginService,方法里面什么都不需要做,然后在ServiceFactory获取mILoginService的时候做出判断,代码如下:
EmptyService
public class EmptyService implements ILoginService {
@Override
public void lanuch(Context context, String targetClass) {
}
@Override
public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
return null;
}
}
ServiceFactory
public class ServiceFactory {
private final static ServiceFactory instancce=new ServiceFactory();
private ServiceFactory(){}
public static ServiceFactory getInstancce(){
return instancce;
}
private ILoginService mILoginService;
private IMineService mIMineService;
public ILoginService getILoginService() {
if (mILoginService == null) {//先判断是否为空
mILoginService=new EmptyService();
}
return mILoginService;
}
public void setILoginService(ILoginService ILoginService) {
mILoginService = ILoginService;
}
public IMineService getIMineService() {
return mIMineService;
}
public void setIMineService(IMineService IMineService) {
mIMineService = IMineService;
}
}
这样去写更利于代码的维护。
组件化最基础的配置就介绍完了,也许真正用组件化的时候会用上ARouter或其它框架,但是手写一些基础的东西去实现,也利于我们去学习和理解。