realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.45k stars 1.74k forks source link

APK split not working on Nexus 5/ Nexus 6P #2649

Closed chadsimtap closed 8 years ago

chadsimtap commented 8 years ago

Goal

APK size as small as Realm documentation says it should be

I work for a startup that is considering using Realm instead of SQLite. I am all for this--only one problem. They are concerned by the size it adds to our otherwise quite small APK. Looking at the documentation, the only solution seems to be the APK split. I tried following the APK split instructions at https://realm.io/docs/java/latest/#how-big-is-the-realm-library

It creates the APKs, but when I try to use them, I just get these errors: Skipping device 'Nexus 5 - 5.1.1' for 'app:release': Could not find build of variant which supports density 480 and an ABI in armeabi-v7a, armeabi Skipping device 'Nexus 6P - 6.0.1' for 'app:release': Could not find build of variant which supports density 560 and an ABI in arm64-v8a, armeabi-v7a, armeabi

These are the only testing devices I have available at the moment. When I don't do an APK split, the app (with Realm) works--so I know the needed code is there. But apparently Realm isn't ready to do an APK split for the processor architecture on those two very popular devices? Or maybe I'm somehow doing something wrong?

I posted this question on StackOverflow four days ago: http://stackoverflow.com/questions/36705330/cant-reduce-size-realm-adds-to-app?noredirect=1#comment61178933_36705330

Since there's been no answer there, I thought I would try here. I would also accept an alternative to the APK split if there's some way to get the APK size (both for downloading and post installation) down substantially.

Expected Results

Small, fully functional APKs.

Actual Results

APKs that cannot be installed on a Nexus 5 or Nexus 6P

Steps & Code to Reproduce

Do the APK split as described in the documentation. Try to use on a Nexus 5 or Nexus 6P.

Code Sample

// Manifest version information!
def versionMajor = 1
def versionMinor = 0
def versionPatch = 0
def versionBuild = 0     // bump for dogfood builds, public betas, etc.

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'io.fabric'
apply plugin: 'realm-android'

android {
    def globalConfiguration = rootProject.extensions.getByName("ext")

    compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
    buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")

    defaultConfig {
        applicationId "com.gane"
        minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
        targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
        versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
        versionName "${versionMajor}.${versionMinor}.${versionPatch}"
        vectorDrawables.useSupportLibrary = false
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    packagingOptions {
        exclude 'LICENSE.txt'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/ASL2.0'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
    }

    lintOptions {
        quiet true
        abortOnError false
        ignoreWarnings true
        disable 'InvalidPackage'            //Some libraries have issues with this.
        disable 'OldTargetApi'
        //Lint gives this warning but SDK 20 would be Android L Beta.
        disable 'IconDensities'             //For testing purpose. This is safe to remove.
        disable 'IconMissingDensityFolder'  //For testing purpose. This is safe to remove.
    }

    signingConfigs {
        debug {
        }

        release {
            storeFile file('matrix')
            storePassword KEYSTORE_PASSWORD
            keyAlias 'matrix'
            keyPassword KEY_PASSWORD
        }
    }

    buildTypes {
        debug {
            applicationIdSuffix '.debug'
            versionNameSuffix '-debug'

            debuggable true
            minifyEnabled false
            shrinkResources false
            // build.gradle testCoverageEnabled true causes debugger to be unable to view local variables/watches
            // https://code.google.com/p/android/issues/detail?id=93730
            // https://code.google.com/p/android/issues/detail?id=123771
            testCoverageEnabled false
            ext.enableCrashlytics = false
        }

        release {
            debuggable false
            minifyEnabled true
            shrinkResources true
            testCoverageEnabled false
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        alpha.initWith(buildTypes.release)
        alpha {
            minifyEnabled false
            shrinkResources false
        }

        beta.initWith(buildTypes.release)
        beta {
            minifyEnabled false
            shrinkResources false
        }
    }

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

dependencies {
    def appDependencies = rootProject.ext.appDependencies
    def appTestDependencies = rootProject.ext.appTestDependencies

    compile appDependencies.supportAppCompact
    compile appDependencies.supportCardView
    compile appDependencies.supportDesign
    compile appDependencies.supportPercent
    compile appDependencies.supportCustomTabs

    apt appDependencies.daggerCompiler
    compile appDependencies.dagger
    compile appDependencies.butterKnife
    compile appDependencies.gson
    compile appDependencies.okHttp
    compile appDependencies.okHttpUrlConnection
    compile appDependencies.picasso
    compile appDependencies.rxJava
    compile appDependencies.rxAndroid
    provided appDependencies.javaxAnnotation

    provided appDependencies.autoValue
    apt appDependencies.autoValue

    compile(appDependencies.crashlytics) {
        transitive = true;
    }

    compile(appDependencies.hapiTenantLibrary) {
        transitive = true
        exclude module: 'android'
        exclude module: 'gson'
        exclude module: 'okhttp'
        exclude module: 'okhttp-urlconnection'
        exclude module: 'rxjava'
    }

    testCompile appTestDependencies.junit
    testCompile appTestDependencies.hamcrest
    // Robolectric to help us test Android based components (Activity, Service, BroadcastReceivers, etc)
    testCompile(appTestDependencies.robolectric) {
        exclude group: 'commons-logging', module: 'commons-logging'
        exclude group: 'org.apache.httpcomponents', module: 'httpclient'
    }
    testCompile appTestDependencies.mockito
    testCompile 'org.apache.maven:maven-ant-tasks:2.1.3' // fixes issue on linux/mac
}

android.applicationVariants.all { variant ->
    def appName
    //Check if an applicationName property is supplied; if not use the name of the parent project.
    if (project.hasProperty("applicationName")) {
        appName = applicationName
    } else {
        appName = parent.name
    }

    if (variant.buildType.name != "debug" && variant.outputs.zipAlign) {
        variant.outputs.each { output ->
            def timestamp = new Date().format("yyyyMMdd-HHmm", TimeZone.getTimeZone("UTC"));
            def newApkName
            newApkName = "${appName}-${variant.versionName}-${timestamp}.apk"
            output.outputFile = new File(output.outputFile.parent, newApkName)
        }
    }
}

Version of Realm and tooling

Realm version(s): 89.0

Android Studio version: Android Studio 2.1 Beta 2

Which Android version and device: Nexus 6P (Marshmallow) and Nexus 5 (Lollipop)

jcarty commented 8 years ago

Try adding 'armeabi-v7a' to your include line:

splits {
 abi {
        enable true
        reset()
        include 'arm', 'arm-v7a', 'arm64', 'mips', 'x86', 'x86_64', 'armeabi-v7a'
    }
}
chadsimtap commented 8 years ago

I knew I had tried it before, but tried it again now. As I expected, I got this error: Execution failed for task ':app:installRelease'.

com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Failed to finalize session : INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113

But while I was retesting it just now, I discovered something interesting: it fails for a release build, but passes a debug build. That doesn't really help me, since production APK size is obviously the concern, but maybe that quirk will be helpful.

emanuelez commented 8 years ago

What version of the Android Gradle plugin are you using?

emanuelez commented 8 years ago

Also, @jcarty is right. It should be something like:

include 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64', 'armeabi-v7a'

I'm updating our gridview example (which is using abi splits) and will update the docs accordingly

emanuelez commented 8 years ago

I updated the website with the correct abi names and the next release will also fix the example that uses splits.

chadsimtap commented 8 years ago

Using new split from documentation:

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

Android Studio 2.1 RC Gradle version 2.12 Gradle Android plugin: 2.10-rc1

Identical results on both Nexus 6P and Nexus 5: Execution failed for task ':app:installRelease'.

com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Failed to finalize session : INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113

Why would it work for the debug build and fail for the release build?

emanuelez commented 8 years ago

This sounds very weird. Do you have a sample project that replicates this issue? We're not able to reproduce it on our side.

chadsimtap commented 8 years ago

Thanks for looking into that. I tried making a simple sample project, which worked. So a coworker and I dug into it. It's apparently related to the end of the Gradle file I posted, which renames the APK files. I hadn't written that code, so that hadn't occurred to me. Sorry about that!

So the problems were: 1) outdated APK split information (which you guys fixed in documentation) 2) my Gradle file was renaming the APK file, so the system couldn't find the right APK to load. (my fault)

Thanks for your help! Should be all set now and it looks like we'll be using Realm!

beeender commented 8 years ago

It seems the issue is fixed :tada: I am closing this now. Feel free to reopen if necessary!