Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how generate multiple apk (using gradle , libGDX)

I try this

I want generate few APK (x86, armeabi-v7a). In order to reduce the size of APK in Google Play

But its doesnt work. Android studia says import com.android.build.OutputFile - Cannot resolve symbol 'OutputFile'. And in console i see. I try search in google other instructions and tried to do something about it myself, but I'm not good at gradle

Error:Cannot invoke method multiply() on null object

my gradle-file is

// map for the version code

ext.versionCodes = ['armeabi-v7a':1, 'armeabi':2, 'x86':3]

import com.android.build.OutputFile

android.applicationVariants.all { variant ->
    // assign different version code for each output
    variant.outputs.each { output ->
        output.versionCodeOverride =
                project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 + android.defaultConfig.versionCode
    }
}

android {
    buildToolsVersion "20.0.0"
    compileSdkVersion 20
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }


        instrumentTest.setRoot('tests')
    }
    defaultConfig {
        versionCode 11
        versionName '1.3'
    }
    productFlavors {
    }

    splits {
        abi {
            enable true
            reset()
            include 'x86', 'armeabi-v7a', 'armeabi'
            universalApk true
        }
    }





}




// needed to add JNI shared libraries to APK when compiling on CLI
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
    pkgTask.jniFolders = new HashSet<File>()
    pkgTask.jniFolders.add(new File(projectDir, 'libs'))
}
// called every time gradle gets executed, takes the native dependencies of
// the natives configuration, and extracts them to the proper libs/ folders
// so they get packed with the APK.
task copyAndroidNatives() {
    file("libs/armeabi/").mkdirs();
    file("libs/armeabi-v7a/").mkdirs();
    file("libs/x86/").mkdirs();

    configurations.natives.files.each { jar ->
        def outputDir = null
        if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
        if (jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
        if (jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
        if (outputDir != null) {
            copy {
                from zipTree(jar)
                into outputDir
                include "*.so"
            }
        }
    }
}
task run(type: Exec) {
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

    def adb = path + "/platform-tools/adb"
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.mygdx.crazyball.android/com.mygdx.crazyball.android.AndroidLauncher'
}
// sets up the Android Eclipse project, using the old Ant based build.
eclipse {
    // need to specify Java source sets explicitely, SpringSource Gradle Eclipse plugin
    // ignores any nodes added in classpath.file.withXml
    sourceSets {
        main {
            java.srcDirs "src", 'gen'
        }
    }

    jdt {
        sourceCompatibility = 1.6
        targetCompatibility = 1.6
    }

    classpath {
        plusConfigurations += [project.configurations.compile]
        containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES'
    }

    project {
        name = appName + "-android"
        natures 'com.android.ide.eclipse.adt.AndroidNature'
        buildCommands.clear();
        buildCommand "com.android.ide.eclipse.adt.ResourceManagerBuilder"
        buildCommand "com.android.ide.eclipse.adt.PreCompilerBuilder"
        buildCommand "org.eclipse.jdt.core.javabuilder"
        buildCommand "com.android.ide.eclipse.adt.ApkBuilder"
    }
}
// sets up the Android Idea project, using the old Ant based build.
idea {
    module {
        sourceDirs += file("src");
        scopes = [COMPILE: [plus: [project.configurations.compile]]]

        iml {
            withXml {
                def node = it.asNode()
                def builder = NodeBuilder.newInstance();
                builder.current = node;
                builder.component(name: "FacetManager") {
                    facet(type: "android", name: "Android") {
                        configuration {
                            option(name: "UPDATE_PROPERTY_FILES", value: "true")
                        }
                    }
                }
            }
        }
    }
}
dependencies {
}
like image 668
knotri Avatar asked Mar 18 '15 16:03

knotri


2 Answers

Looks like it fails on a try to get ABI for universal APK as there is no filter for it in your versionCodes map. You should check the result of output.getFilter(OutputFile.ABI) before applying multiplication in some manner like for example:

android.applicationVariants.all { variant ->
    // assign different version code for each output
    variant.outputs.each { output ->
        def abiFilter = output.getFilter(OutputFile.ABI)
        def abiMultiplier = 0

        if (abiFilter != null) {
            abiMultiplier = project.ext.versionCodes.get(abiFilter)
        }

        output.versionCodeOverride =
            abiMultiplier * 1000000 + android.defaultConfig.versionCode
    }
}
like image 146
Alex Bonel Avatar answered Nov 13 '22 13:11

Alex Bonel


TL;DR:

I just had the same problem, and for me the answer is thankfully simple. Instead of using OutputFilter.ABI in the call to output.getFilter, use OutputFilter.FilterType.ABI:

android.applicationVariants.all { variant ->
  variant.outputs.each { output ->
    def defaultCode = android.defaultConfig.versionCode
    def filter = output.getFilter(OutputFile.FilterType.ABI)
    def abiMultiplier = project.ext.versionCodes.get(filter)

    output.versionCodeOverride = abiMultiplier * 1000000 + defaultCode
  }
}

What broke

This is essentially what you have above, but refactored into a few more lines. The key difference is in the call to output.getFilter. The examples in the docs say to use OutputFile.ABI - as of more recent tools versions (I'm not sure which), this is wrong. The correct argument is OutputFile.FilterType.ABI.

The getFilter method is defined on the class ApkVariantOutputImpl. As you can see in the source code for ApkVariantOutputImpl, it takes a single argument of type OutputFile.FilterType; OutputFile.ABI is a String, and Groovy (despite all the other bits of magic it does) evidently does not convert the constant string into the proper enum, resulting in a null value.

Disclaimer

Just FYI, I had to dig pretty extensively through different versions of the build tools to find the source link - it seems that this has been an area of API instability. This works for me, with the following settings:

buildscript {
  dependencies {
    'com.android.tools.build:gradle:1.2.2'
  }
}

android {
  compileSdkVersion 22
  buildToolsVersion "22.0.1"

  // lots of stuff

  splits {
    abi {
      enable true
      reset()
      include 'x86', 'armeabi-v7a', 'mips'
    }
  }
}

If you want to build a universal APK (with all the different ABIs included), you'll need to account for the fact that the universal variant has no ABI filter, i.e. check the multiplier for null and replace it with something meaningful like zero.

like image 3
Ben Avatar answered Nov 13 '22 12:11

Ben