How to reduce the apk size of Android app which need openCv lib( only for Image processing)

You can not remove the library libopencv_java3.so, it's mandatory.

Is there any way by which I can reduce my apk size?

Without modify the library libopencv_java3.so, you can build multiple apks for different architectures rather than package a flat APK. Only one arch of libopencv_java3.so will be packaged into your split APK. For example, you can use the configuration below to build only for x86, armeabi-v7a and mips architectures in your build.gradle

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

More information about reduce apk size by splitting, see here.

Or is it possible just to extract only the feature from OpenCV which I am interested in and create .so for those features?

Yes, of course. The building process for opencv is fully customized. If you have already know how to build opencv for Android, go to step 2 directly.

step 1

  1. get the source of opencv
  2. create directory opencv_build
  3. change to root directory of opencv you just cloned
  4. invoke the build script platforms/android/build_sdk.py --ndk_path $ANDROID_NDK_ROOT --sdk_path $ANDROID_SDK_ROOT ../opencv_build .

Note

The build procedure need android command to build apk, so the Android SDK tool version must less than 25.3.0, from 25.3.0, android command has been removed.

step 2

Assume the step 1 above run properly. Here I take armeabi-v7a as an example.

  1. Go to directory opencv_build/o4a/lib/armeabi-v7a
  2. You will find many *.a for different opencv module, the library libopencv_java3.so also in this directory
  3. Create a tiny version share library from the module you need, e.g

    $ arm-linux-androideabi-gcc -shared -o libopencv_tiny.so --sysroot=$ANDROID_NDK_ROOT/platforms/android-9/arch-arm -Wl,--whole-archive libopencv_core.a libopencv_imgcodecs.a libopencv_imgproc.a -Wl,--no-whole-archive
    $ du -h libopencv_tiny.so 
    5.2M    libopencv_tiny.so
    $ arm-linux-androideabi-strip --strip-unneeded libopencv_tiny.so 
    $ du -h libopencv_tiny.so 
    4.1M    libopencv_tiny.so
    $ du -h libopencv_java3.so 
    9.8M    libopencv_java3.so
    

The tiny version which include core, image codecs and image proc has a size 4.1M after strip, but the full version libopencv_java3.so has a size of 9.8M.

I use libopencv_tiny.so as the name just for convenience, you have to use the same name libopencv_java3.so in your project. Otherwise System.loadLibrary in Java can not find the native library.

For the rest of the architecture you need, e.g arm64-v8a, do the same.


For me, alijandro answer 2nd answer was the best solution but I got stuck in the process. So, I solved the issue by apk split. You need to upload apk's related to the platform and also a universal apk.It would be ideal , since OpenCV doesn't provide adding dependency at module level.

Universal apk will be downloaded in case platform does not match.Here is nicely written blog about apk publishing mutiple apk. You need to maintain same app id , Keystore, same app version but different code version

Step 1.Add gradle code for spk split. Include the architecture for which you want to create separate apk.

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

Step 2.Add below code for different Code version for different platform.

android{
//......
}
ext.versionCodes = ['armeabi': 3, 'armeabi-v7a': 4, 'arm64-v8a': 5, 
mips: 6, 'x86': 7, 'x86_64': 8]
import com.android.build.OutputFile
// For each APK output variant, override versionCode with a combination 
// of ABI APK value * 1000 + defaultConfig.versionCode
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 * 1000 + android.defaultConfig.versionCode
   }
  }

For Example, if the initial version code was 1, then generated codes will be:

 armeabi -> 3001
 armeabi-v7a -> 4001
 arm64-v8a -> 5001
 mips -> 6001
 x86 -> 7001
 x86_64 -> 8001

All together your Gradle code will look like this.

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

 ext.versionCodes = ['armeabi': 3, 'armeabi-v7a': 4, 'arm64-v8a': 5, 
mips: 6, 'x86': 7, 'x86_64': 8]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
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 * 1000 + android.defaultConfig.versionCode
}
}

Now you need to publish all the apk on playstore one by one.

Note: While publishing make sure you retain all the platform and universal apk.