9

I am currently using the OpenCV (OpenCV4Android) library which does not use the NDK (there is no C or C++ code). However, there are .so files for armeabi, armeabi-v7a, mips, and x86. If I include all of these in the project, the app size is 30mb, whereas if I include only 1, the app size is only 9mb. If I try to run the app on a device that doesn't have that device's architecture's .so file included, it crashes, whereas if I have it's .so file included, it works.

Therefore, I am wanting to release multiple APKs to different device architectures to reduce the file sizes. From what I've seen, this can only be done in the Application.mk file, but my library does not have one. Is there another way to target different Android architectures?

guneykayim
  • 5,210
  • 2
  • 29
  • 61
AggieDev
  • 5,015
  • 9
  • 26
  • 48
  • 2
    If there are .so files, then it's using the NDK. – user253751 Jan 22 '15 at 23:18
  • Is it possible, at (or after?) install time, to indicate that a separate APK must be installed? Then you could break your app into five, a core APK and four architecture-specific ones, and after the core app is installed it would detect the arch and install the necessary additional APK. I believe that if they are using the same package name they can work together as a unit, but I've never tried anything like this. Apologies if I'm way off base. – David Conrad Jan 22 '15 at 23:21
  • 1
    @immibis I assume the so files are from a precompiled library( OpenCV4Android ) and in this application project, the developer is not using NDK to build the so files. – kiranpradeep Jan 23 '15 at 06:23

3 Answers3

18

I am currently using the OpenCV (OpenCV4Android) library which does not use the NDK (there is no C or C++ code). However, there are .so files for armeabi, armeabi-v7a, mips, and x86.

As immibis put it, if there are .so files, then it is using the NDK.

If I try to run the app on a device that doesn't have that device's architecture's .so file included, it crashes, whereas if I have it's .so file included, it works.

That depends upon the device. Many x86 devices have libhoudini, which can run ARM NDK binaries, albeit more slowly than they would run native x86 binaries. Similarly, an armeabi-v7 device can run armeabi NDK binaries, though perhaps more slowly, particularly if floating-point processing is used.

Therefore, I am wanting to release multiple APKs to different device architectures to reduce the file sizes. From what I've seen, this can only be done in the Application.mk file, but my library does not have one.

The Application.mk file only controls what gets compiled, not what gets distributed.

Is there another way to target different Android architectures?

Use Gradle for Android, perhaps in conjunction with Android Studio, and the abi split:

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

The abi closure in the splits closure:

  • opts into having different APK files per CPU architecture

  • sets up a whitelist of the architectures that you want

  • also requests a "universal APK" that contains all of the architectures, for use with distribution channels that do not support separate APKs by architecture

The result of your build will be separate APKs by CPU architecture, plus the universal one.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • That is great news to hear, so if I use this method am I able to deploy multiple APKs to the store for one app to target multiple architectures? – AggieDev Jan 23 '15 at 00:18
  • ...using a single "include" for each APK, such as include 'armeabi-v7a'? – AggieDev Jan 23 '15 at 00:28
  • @AggieDev: You would list in the `include` statement the architectures you want and skip the ones that you don't want (e.g., `mips`, probably). You will get one APK per architecture. In terms of distribution on the Play Store, my understanding is that it supports separate APKs per architecture, though I haven't tried it personally. – CommonsWare Jan 23 '15 at 00:51
  • 1
    you'll have to have different version codes for each of these architectures as Kiran said, and upload your APKs to the developer console in "advanced mode". Here is a gist that may help you handling version codes properly from gradle: https://gist.github.com/ph0b/69586260bc20c58136ef – ph0b Jan 23 '15 at 13:13
10

Each apk for the application should have unique version code specified by android:versionCode. Some x86 devices can run ARMv7 binaries. So, to avoid a case of ARMv7 apk getting downloaded/used in x86 device ( and similar scenarios for other architectures ), you should order the version codes. For e.g., order the version codes so that, the x86 APK has a higher version code than ARMv7. More on versioning at this link.

A build.gradle sample for creating unique version coded apks for each architecture of your choice is shared by ph0b at github. Copying the same below.

splits {
        abi {
            enable true
            reset()
            include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
            universalApk true //generate an additional APK that contains all the ABIs
        }
    }

    // map for the version code
    project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]

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

Suggestion: Android multiple apk support documentation suggests to not use multiple apks if apk size is less then 50mb.

You should generally use multiple APKs to support different device configurations only when your APK is too large (greater than 50MB) due to the alternative resources needed for different device configurations. Using a single APK to support different configurations is always the best practice, because it makes the path for application updates simple and clear for users (and also makes your life simpler by avoiding development and publishing complexity).

kiranpradeep
  • 10,859
  • 4
  • 50
  • 82
  • 1
    The documentation updated the suggested size from 50mb to 100mb. "You should generally use multiple APKs to support different device configurations only when your APK is too large (greater than 100MB)" – NoilPaw Jan 09 '17 at 14:36
  • `Could not get unknown property 'abiFilter' for` – user25 May 06 '18 at 19:15
3

I do not recommend to use this pattern to build your Build number. ARCH - BUILD, because if one day you want to come back to one APK, you will have to increase your versionCode a lot. instead you can follow this pattern: BUILD - ARCH

android.defaultConfig.versionCode * 100 + project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0)

exemple for build 74 the build number will be: 7402 - armeabi-v7a 7408 - x86

sonique
  • 4,539
  • 2
  • 30
  • 39