您的当前位置:首页正文

Android Studio动态切换依赖库的一种实现方法

来源:华拓网

写在前头

  1. 本文编辑于2016年11月27日,请注意技术的时效性
  2. 请带着批判的态度阅读
  3. 可以参考项目
  4. 这里的动态切换依赖库是指可以动态切换Module中的Build Variant

其中软件版本如下:

  • Android Studio 2.2.2
  • gradle 2.14.1

最近在写模块化,将图片库,网络库,公共库,工具库都拆分出去,依赖库一多,如果不实现动态切换依赖库就特别麻烦,需要改动到gradle代码,故有本次的验证。


多个Module

正文

1. 添加一个通用方法工具库

/**
 *
 * 工具类 gradle
 */

import java.util.regex.Matcher
import java.util.regex.Pattern

/**
 * 获取当前的Flavor类型 查找不到返回defaultName
 *
 * @param libName
 * @param defaultName
 * @return
 */
ext.getCurrentFlavor = { String libName, String defaultName ->
    println "getCurrentFlavor args:" + libName + "," + defaultName

    Gradle gradle = getGradle()
    String  tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
    Pattern pattern;
    if(tskReqStr.contains("assemble")) {
        pattern =  + libName + ":assemble(\\w+)(Sources)")
    }
    else {
        pattern =  + libName + ":generate(\\w+)(Sources)")
    }

    Matcher matcher = pattern.matcher(tskReqStr)

    if(matcher.find()) {
        String result = toLowerCaseFirstOne(matcher.group(1))

        println "getCurrentFlavor:" + result
        saveToLocal(libName, result)
        return result.toString()
    } else {
        String result = getFromLocal(libName, defaultName)
        println "getCurrentFlavor: no match one, return local or default:" + result
        return result
    }
}


/**
 * 首字母转小写
 * @param value
 * @return
 */
def toLowerCaseFirstOne(String value){
    if (value == null || value.isEmpty()) {
        return ""
    }

    if(Character.isLowerCase(value.charAt(0))) {
        return value;
    } else {
        return (new StringBuilder()).append(Character.toLowerCase(value.charAt(0))).append(value.substring(1)).toString();
    }
}

/**
 * 保存到本地
 *
 * @param key
 * @param value
 * @return
 */
def saveToLocal(String key, String value) {
    File localFile = project.rootProject.file('local.properties')
    // 保存进local.properties,防止编译的时候找不到
    Properties properties = new Properties()
    properties.load(localFile.newDataInputStream())
    properties.setProperty(key, value);
    properties.save(localFile.newDataOutputStream(), "update-"+ key + "[" + value + "]")
}

/**
 *
 * 从本地读取数据
 *
 * @param key
 * @param defaultName
 * @return
 */
def getFromLocal(String key, String defaultName) {
    File localFile = project.rootProject.file('local.properties')
    // 保存进local.properties,防止编译的时候找不到
    Properties properties = new Properties()
    properties.load(localFile.newDataInputStream())
    return properties.get(key, defaultName)
}

2. 为有必要的依赖库添加默认的发布Flavor

操作步骤如下:

  1. 添加工具库
apply from: '../your path/utils.gradle'
  1. 设置默认的发布config,通过动态获取Tasks任务
 defaultPublishConfig(getCurrentFlavor(project.name, "your default flavor"))

以我的图片库为例子:

// 公共库
apply plugin: 'com.android.library'
// 关键步骤1: 添加工具库
apply from: '../tasks/utils.gradle'

android {
    // 关键步骤2: 修改默认的发布config
    defaultPublishConfig(getCurrentFlavor(project.name, "glideRelease"))

    ......

    productFlavors {
        glide {
            resValue("string","lib_img","Glide")

            dependencies {
                glideCompile fileTree(dir: 'src/glide/libs', include: ['*.jar'])
                glideCompile 'com.github.bumptech.glide:glide:3.7.0'
                glideCompile 'com.android.support:support-v4:25.0.0'
            }
        }

        uil {
            resValue("string","lib_img","Uil")
            dependencies {
                uilCompile fileTree(dir: 'src/uil/libs', include: ['*.jar'])
            }
        }
    }
}
   ......
}

3. 为调用依赖库的module添加依赖

这个就很简单了,跟调用一个通用的依赖一样,它自然会根据你设置的Build Variant来关联依赖库

// may like this
compile project(path: ":libimg")
Paste_Image.png

4 . 愉快的去切换动态依赖库吧

现在,你可以试试看,包括编译都会从local文件中动态选择。

local文件

大致的原理

  1. 动态设置defaultPublishConfig,可以决定默认发布的Flavor(真不知道怎么翻译...)
  2. 其实我们通过AS切换Build Variant的时候,是会执行了多次gradle任务的,当它执行到defaultPublishConfig时,会去执行我们设置的getCurrentFlavor方法,而getCurrentFlavor方法会动态返回一个Flavor名称,我们的目的就是保持这个动态返回跟用户选择的一致。
  3. getCurrentFlavor的操作逻辑是先根据Tasks需要执行的任务去匹配查找,如果找不到就会从本地的local文件中查找,如果再找不到就会读取默认值
  4. 为什么会先根据Tasks需要执行的任务去匹配查找,是因为我们切换Build Variant时,我们可以获取到当前用户切换的Flavor,如下图,我们就是从这里正则匹配的:


    当我们切换Build Variant时
  5. 为什么需要从local文件中读取呢,是因为刚刚说了切换Build Variant,是会执行多次gradle任务的,第一次执行时,我们是无法获取到Tasks任务的,所以一定需要有默认值。另外,当我们执行编译生成apk的时候,也无法获取到Tasks任务,如下图,都是从默认中获取:


    手动编译
  6. 为什么需要默认值呢,因为第一次执行的时候,我们既拿不到Tasks任务,又没有local文件可以读取。

PS: 服务器Jenkins等打包的时候,可以通过动态配置local文件,实现自定义打包。

参考文献