Skip to content

Instantly share code, notes, and snippets.

@Bresiu
Created May 12, 2016 13:12
Show Gist options
  • Save Bresiu/a9cdd477e3646e389f8225f046af7814 to your computer and use it in GitHub Desktop.
Save Bresiu/a9cdd477e3646e389f8225f046af7814 to your computer and use it in GitHub Desktop.
Gradle script to trim android main-dex classes when 65k limit is reached using standard multi-dex components.
// A blacklist of absolute package path directories that will be excluded from the main dex list. ex: 'com/company/package/path'
// the package name can be partial, since class names encountered that start with any of the given package names will be discarded.
ext.packagesToDiscardFromMainDexFile = []
def shouldDiscardClass(String className) {
packagesToDiscardFromMainDexFile.any { className.startsWith(it) }
}
def patchMainDexKeepList(Project proj, String variantName) {
Task collectTask = proj.tasks.findByName("collect${variantName.capitalize()}MultiDexComponents")
Task processManifestTask = proj.tasks.findByName("process${variantName.capitalize()}Manifest")
if (!collectTask || !processManifestTask)
return
collectTask.dependsOn(processManifestTask) << {
File androidManifestFile = processManifestTask.outputs.files.filter {
it.absolutePath.matches('.*full.*AndroidManifest\\.xml')
}.singleFile
File manifestKeepFile = collectTask.outputs.files.filter {
it.absolutePath.endsWith('manifest_keep.txt')
}.singleFile
def nonExportedClasses = new XmlSlurper().parse(androidManifestFile).application.'**'.findAll { node ->
if (node.name().matches('(activity|service|receiver|provider)')) {
return (node.'@android:exported' == 'false'
|| (node.'@android:exported' != 'true'
&& node.'intent-filter'.size() == 0))
}
}.collect {
it.'@android:name'.text()
}
def manifestKeepText = manifestKeepFile.text
manifestKeepFile.withWriter('utf-8') { writer ->
manifestKeepText.eachLine { line ->
boolean isNonExportedLine = nonExportedClasses.any {
line.contains(it)
}
if (!isNonExportedLine)
writer.writeLine(line)
}
}
}
}
def patchMainDexClassesList(Task task) {
task.doFirst {
for (File inputFile : task.inputs.files.files) {
if (inputFile.absolutePath.endsWith('maindexlist.txt')) {
def source = inputFile.text
inputFile.withWriter('utf-8') { writer ->
source.eachLine { line ->
if (!shouldDiscardClass(line)) {
writer.writeLine(line)
}
}
}
}
}
}
}
subprojects {
afterEvaluate { proj ->
// patch main dex classes proguard keep list (debug builds only, since we don't use proguard in that environment)
if (proj.plugins.findPlugin('com.android.application')
&& proj.android.hasProperty('applicationVariants')) {
proj.android.applicationVariants.all { variant ->
if(variant.name.toLowerCase().endsWith('debug'))
patchMainDexKeepList(proj, variant.name)
}
}
// patch main dex classes list (debug builds only, since we don't use proguard in that environment)
proj.tasks.whenTaskAdded { task ->
if (task.name.matches('transformClassesWithDexFor.*Debug')) {
patchMainDexClassesList(task)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment