组件化开发配置,一不小心就配错?详细配置步骤来啦

497 阅读6分钟

组件化开发的好处有很多,随便想想就能列出几点

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或其它框架,但是手写一些基础的东西去实现,也利于我们去学习和理解。