基于ARouter实现Android项目组件化

传统的Android项目开发是将所有的代码全部放到一个module工程中,为了代码的复用,最多也就将各种通用库的代码,比如音视频播放库、网络库、二维码扫描库等等放到单独的module下,以library形式作为主工程的依赖。

使用传统的开发模式会使我们的主工程变得越来越庞大复杂,越来越难以维护,而且如果是比较大型的项目也不利于多人协作开发。

为了解决传统开发模式带来的问题,组件化的开发思路就诞生了。组件化的思路很简单,就是将我们项目中的业务进行拆分,各业务模块间不相互依赖

这么说可能有些抽象,举个例子。比如项目有登录、首页、音乐播放三个业务模块,登录完成后我们会跳转到首页,首页点击音乐列表进入音乐播放页面。使用传统的开发思路是,登录页面依赖首页,首页依赖播放列表页。而采用组件化的开发思路是,这三个模块互相都不知道对方的存在,只需要调用暴露的接口就能够实现各模块间的通讯。

接下来,我将带着大家一步步的去搭建我们的组件化的项目。

首先看下demo工程的目录结构。

其中ft_loginft_main为业务模块,lib_base为业务基础组件,app为我们的主工程。lib_base工程下我们一般放置业务相关的接口,lib_base的目录结构如下:

先看下登录模块,先定义LoginService接口:

package com.example.lib_base.ft_login.service;

import android.content.Context;

import com.alibaba.android.arouter.facade.template.IProvider;

public interface LoginService extends IProvider {

    void loginActivity(Context context);

    boolean hasLogin();
}

然后看下lib_basebuild.gradle配置:

apply plugin: 'com.android.library'
apply plugin: 'com.alibaba.arouter'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    //arouter库
    compileOnly('com.alibaba:arouter-api:1.5.1') {
        exclude group: 'com.android.support'
    }
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
}

接下来我们在ft_login工程中实现LoginService接口

package com.example.ft_login.service;

import android.content.Context;
import android.util.Log;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.example.ft_login.LoginActivity;
import com.example.lib_base.ft_login.service.LoginService;

@Route(path = "/login/login_service")
public class LoginServiceImpl implements LoginService {
    @Override
    public void loginActivity(Context context) {
        LoginActivity.start(context);
    }

    @Override
    public boolean hasLogin() {
        return false;
    }

    @Override
    public void init(Context context) {
        Log.i(LoginServiceImpl.class.getSimpleName(), "init()");
    }
}
package com.example.ft_login;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;

import com.example.lib_base.ft_main.service.impl.MainImpl;

public class LoginActivity extends FragmentActivity {

    public static void start(Context context) {
        Intent intent = new Intent(context, LoginActivity.class);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_login_layout);
    }


    public void login(View view) {
        MainImpl.getInstance().startMainActivity(this);
    }
}

ft_login模块的build.gradle配置跟lib_base的配置差不多,这里就不放代码了。

为了其他模块方便调用ft_login模块,需要另外新建一个包装类,LoginImpl

package com.example.lib_base.ft_login.service.impl;

import android.content.Context;

import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.launcher.ARouter;
import com.example.lib_base.ft_login.service.LoginService;

public class LoginImpl {

    @Autowired(name = "/login/login_service")
    protected LoginService mLoginService;

    private static LoginImpl mLoginImpl;

    public static LoginImpl getInstance() {
        if (mLoginImpl == null) {
            synchronized (LoginImpl.class) {
                if (mLoginImpl == null) {
                    mLoginImpl = new LoginImpl();
                }
                return mLoginImpl;
            }
        }
        return mLoginImpl;
    }

    private LoginImpl() {
        ARouter.getInstance().inject(this);
    }

    public void loginActivity(Context context) {
        mLoginService.loginActivity(context);
    }

    public boolean hasLogin() {
        return mLoginService.hasLogin();
    }

}

接下来看下app工程如下:

package com.example.test;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.example.lib_base.ft_login.service.impl.LoginImpl;

public class LoadingActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading_layout);

        handler.sendEmptyMessageDelayed(1, 2000);
    }

    Handler handler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            LoginImpl.getInstance().loginActivity(LoadingActivity.this);
            finish();
        }
    };

    @Override
    public void onBackPressed() {

    }
}
package com.example.test;

import android.app.Application;

import com.alibaba.android.arouter.launcher.ARouter;

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        ARouter.init(this);
    }
}

app工程的build.gradle配置跟ft_login差不多,主要区别在于dependencies中

dependencies { 
    ......
    //业务基础库
    implementation project(':lib_base')
    implementation project(':ft_main')
    implementation project(':ft_login')
    //arouter库
    implementation('com.alibaba:arouter-api:1.5.1') {
        exclude group: 'com.android.support'
    }
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
}

接下来启动工程就能看到从loading页面跳转到登录页面了。ft_main的写法同ft_login,这里不做展开了。

下面这张图是整个demo的工程结构图,大家可以更直观的感受组件化的整体结构。

组件化的核心思路就是将业务模块拆分,然后通过ARouter库来进行业务模块间的解耦。这样能避免业务模块间互相引用,能将复杂的业务模块拆分成多个小的组件,便于维护。

github上已经上传了demo

关于组件化大家也可以看下这两篇文章,写得非常详细,非常好。

组件化:

https://blog.csdn.net/guiying712/article/details/55213884

多人协作gradle配置:

https://blog.csdn.net/guiying712/article/details/72629948