使用Gradle在自定义类加载Dalvik(Android新建系统)

Ric*_*ral 18 android dalvik dynamic-class-loaders gradle

根据Fred Chung在Android开发者博客上介绍Dalvik中Custom Class Loading:

Dalvik VM为开发人员提供了执行自定义类加载的工具.应用程序可以从其他位置(如内部存储)或通过网络加载它们,而不是从默认位置加载Dalvik可执行文件("dex").

但是,没有多少开发人员需要进行自定义类加载.但那些按照博客文章中的说明进行操作的人可能会在使用Gradle(Google I/O 2013中引入的Android新构建系统)模仿相同行为时遇到一些问题.

究竟如何调整新构建系统以执行与旧(基于Ant)构建系统相同的中间步骤?

Ric*_*ral 27

我的团队和我最近在我们的应用程序中达到了64K方法引用,这是dex文件中支持的最大数量.为了解决这个限制,我们需要将程序的一部分分成多个辅助dex文件,并在运行时加载它们.

我们按照问题中提到的博客文章来介绍旧的基于Ant的构建系统,一切都运行得很好.但是我们最近觉得有必要转向基于Gradle的新构建系统.

这个答案并不打算用完整的例子替换完整的博客文章.相反,它将简单地解释如何使用Gradle来调整构建过程并实现相同的功能.请注意,这可能只是一种方式,以及我们目前在我们的团队中如何做到这一点.这并不一定意味着它是唯一的方式.

我们的项目结构有点不同,这个例子作为一个单独的Java项目,将所有源代码编译成.class文件,将它们组装成一个.dex文件并完成,将单个.dex文件打包成.jar文件.

开始吧...

在根build.gradle中,我们有以下代码来定义一些默认值:

ext.androidSdkDir = System.env.ANDROID_HOME

if(androidSdkDir == null) {
    Properties localProps = new Properties()
    localProps.load(new FileInputStream(file('local.properties')))

    ext.androidSdkDir = localProps['sdk.dir']
}

ext.buildToolsVersion = '18.0.1'
ext.compileSdkVersion = 18
Run Code Online (Sandbox Code Playgroud)

我们需要上面的代码,因为虽然示例是一个单独的Java项目,但我们仍然需要使用Android SDK中的组件.我们稍后还需要一些其他属性......所以,在主项目的build.gradle中,我们有这种依赖:

dependencies {
    compile files("${androidSdkDir}/platforms/android-${compileSdkVersion}/android.jar")
}
Run Code Online (Sandbox Code Playgroud)

我们还简化了此项目的源集,这可能对您的项目来说不是必需的:

sourceSets {
    main {
        java.srcDirs = ['src']
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们将内置jar任务的默认配置更改为仅包含classes.dex文件而不是所有.class文件:

configure(jar) {
    include 'classes.dex'
}
Run Code Online (Sandbox Code Playgroud)

现在我们需要有一个新任务,它将把所有.class文件组装成一个.dex文件.在我们的例子中,我们还需要将Protobuf库JAR包含到.dex文件中.所以我在这里的例子中包括:

task dexClasses << {
    String protobufJarPath = ''

    String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''

    configurations.compile.files.find {
        if(it.name.startsWith('protobuf-java')) {
            protobufJarPath = it.path
        }
    }

    exec {
        commandLine "${androidSdkDir}/build-tools/${buildToolsVersion}/dx${cmdExt}", '--dex',
                    "--output=${buildDir}/classes/main/classes.dex",
                    "${buildDir}/classes/main", "${protobufJarPath}"
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,请确保在build.gradle文件中的某处(通常位于顶部)进行以下导入:

import org.apache.tools.ant.taskdefs.condition.Os
Run Code Online (Sandbox Code Playgroud)

现在我们必须让jar任务依赖于我们的dexClasses任务,以确保在最终的.jar文件组装之前执行我们的任务.我们用一行简单的代码来做到这一点:

jar.dependsOn(dexClasses)
Run Code Online (Sandbox Code Playgroud)

我们已经完成了......只需使用通常的assemble任务调用Gradle,最后的.jar文件${buildDir}/libs/${archivesBaseName}.jar将包含一个classes.dex文件(除了MANIFEST.MF文件).只需将其复制到您的应用资产文件夹中(您可以随时使用Gradle自动执行此操作,但这超出了此问题的范围)并按照博客文章的其余部分进行操作.

如果您有任何疑问,请在评论中大声说出来.我会尽力帮助我.