Android模块化分层化之: maven 库中上传的 aar 包 获取 BuildConfig 相关常量值

最近在做项目分层化的一些工作,具体思路是将原有项目一些基础服务模块和设备服务模块抽离出来,上传到本地的 Maven 服务器,然后再通过在主项目中的 build.gradle 文件中通过 compile 语句进行导包处理。但通过这种方法编译成的 aar 包只能是 release 版本,无法使用到 BuildConfig 动态设置的一些常量,比如常见的「Debug 日志开关」,我们一般都是在日志类通过 BuildConfig.DEBUG 来获取包状态,从而设置是否要打印出日志。

题外话

这里特别提一下为什么要用 Maven 这种方式,实际上单纯的抽离模块可以通过子模组的方式,Git 也有 submodule 这样的子模组管理方法,但用 Git 管理子模组会有很多问题,比如项目引用子模组的指针问题,在一个并行开发团队中,子模组指针往往会导致很多出人意料的问题,以后我会专门的在这方面写一篇博客做一个解析。

通过把子模块上传至 Maven 库有很多好处,比如导入起来很方便,只需要在配置文件中 compile 即可,而且开发该模块的时候只需要单独打开该子模组的代码,不像通过 Git 管理的依赖子模组,即使你只是为了修改子模组代码,也需要打开原来的完整项目,然后在项目下的子 module 进行开发,最后子模组代码和主项目代码得同时提交。

compile 'com.github.bumptech.glide:okhttp-integration:1.4.0'

另外它还有一些安全性的好处。通过这种方式,实际上也是一种对代码的保护,客户端开发人员不会轻易的修改这部分代码。

具体如何通过 Android studio 构建 maven 库,请移步 Gradle实战:发布aar包到maven仓库

具体问题

我把项目中的一些基础服务专门抽离了出来,其中就包括了一些跟 Log 日志打印相关的代码,原来项目中在build.gradle 中的 buildTypes 中设置了 DEVELOP_MODE 常量,来控制在不同渠道打包下的 Log 开关。

buildTypes {
        debug {
            // log日志开关
            buildConfigField("boolean", "DEVELOP_MODE", "true")
            }

        release{
            buildConfigField("boolean", "DEVELOP_MODE", "false")
        }

然后,在具体的日志类中调用,动态获取 DEVELOP_MODE

public class DEBUG {
    public final static boolean DEVELOP_MODE = BuildConfig.DEVELOP_MODE;
}

现在,当我把这部分代码单独抽离出来到名叫「middleWare」的子 library 后,即使我仍然可以在该 library 中的 build.gradle 文件设置上述的 buildTypes 代码,但通过 maven 编译出来的 aar 包只能是 release 版本,自然也无法获取到那些动态配置的常量值。

所以,只能通过获取引入项目的 BuildConfig 类来获取了,所以我们自然的想到了用反射。下面是具体代码,注释也比较详细:

public class BuildConfigProvider {
    private static Context sContext;

    private static String packageName;

    /**
     * 通过反射获取ApplicationContext
     *
     * @return
     */
    private static Context getContext() {
        if (sContext == null) {
            try {
                final Class activityThreadClass = Class.forName("android.app.ActivityThread");
                final Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread");
                final Object activityThread = currentActivityThread.invoke(null);
                final Method getApplication = activityThreadClass.getDeclaredMethod("getApplication");
                final Application application = (Application) getApplication.invoke(activityThread);
                sContext = application.getApplicationContext();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return sContext;
    }

    /**
     * 通过反射获取包名
     *
     * @return
     */
    private static String getPackageName() {
        if (packageName == null) {
            try {
                final Class activityThreadClass = Class.forName("android.app.ActivityThread");
                final Method currentPackageName = activityThreadClass.getDeclaredMethod("currentPackageName");
                packageName = (String) currentPackageName.invoke(null);
            } catch (Exception e) {
                packageName = getContext().getPackageName();
            }
        }

        return packageName;
    }
    /**
     * 获取具体的域值
     *
     * @return
     */
    public static Object getBuildConfigValue(String fieldName) {
        try {
            Class clazz = Class.forName(getPackageName() + ".BuildConfig");
            Field field = clazz.getField(fieldName);
            return field.get(null);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        return "";
    }
}

这里实际上可以用一个方法获取 context 然后就直接可以拿到 packageName,为了看的清晰,所以分成两个方法了。

然后把之前那个 DEBUG 类适当改动下,将 DEBUG 类放在该子模块代码中:

    public final static boolean DEVELOP_MODE = isDevelopMode();
    static boolean isDevelopMode(){
        return (boolean)BuildConfigProvider.getBuildConfigValue("DEVELOP_MODE");
    }
}

这样只需要在引入项目中的 buildTypes 中设置 DEVELOP_MODE 的各种配置就能在项目中动态的调用日志开关了。

比如我们项目中的日志打印类 Glog.class,每个日志等级的打印前都会有 DEBUG.DEVELOP_MODE 的判断:

public class GLog {

    public static void d(String message) {
        if(TextUtils.isEmpty(message)){
            return;
        }
        //判断日志开关
        if (DEBUG.DEVELOP_MODE) {
            final StackTraceElement[] stack = new Throwable().getStackTrace();
            final int i = 1;
            final StackTraceElement ste = stack[i];
            Log.println(Log.DEBUG, LOG_TAG, String.format("[%s][%s][%s]%s", ste.getFileName(), ste.getMethodName(), ste.getLineNumber(), message));
        }
    }
}

这里只是通过打印日志的例子来展示如何在 aar 中调用主项目的 BuildConfig 类获取编译类型,其他各种 BuildConfig 中的域都可以用这种方式获取到

你可能感兴趣的:(Android开发,Android架构)