I want to ship native shared libraries with my apk. I added these shared libraries to the folder src/main/jniLibs/arm64-v8a
. The compiled
apk does include these libraries. I did verify this by checking the contents of unzip build/outputs/apk/debug/app-debug.apk
. These libraries are
at lib/arm64-v8a/
.
However, I cannot find the libraries on my target device. I installed the app via adb install build/outputs/apk/debug/app-debug.apk
. I identified the folder on the target with adb shell pm list packages -f MY_APP
. Although the path /data/app/{PATH1}/{PATH2}/lib/arm64/
does exist, it is empty. The libraries are, however, included in the base.apk
on the target.
I have not modified anything in my app (gradle config or CMake files, for instance) other than storing the libraries at src/main/jniLibs/arm64-v8a/
. I experimented with some teakes in the gradle config but that didn't help. I cannot find relevant information in the Android documentation, https://developer.android.com/studio/projects/gradle-external-native-builds. I am using Gradle 8.
You haven't explicitly triggered access to them during initial installation (via adb install) command that's why you are getting empty folders arm64
and arm64-v8a
.
Try below approach is for development purposes only. You can use adb push to manually push the libraries from your computer to the target device's appropriate location.
adb push <path_to_library_on_your_pc>/lib/arm64-v8a/ <target_device_path>/data/app/<package_name>/lib/arm64-v8a/
And then try if it works .
Android keeps its native libraries well-organized within the APK they are extracted and used appropriately depending on the application being installed on the device.
As you proceed to develop your Android application further with Native Libraries, the Libraries get packaged with your APK file within the directory lib
. The layout of that directory is as follows:
<APK>
└── lib/
├── armeabi-v7a/
│ └── <native_lib_1>.so
│ └── <native_lib_2>.so
├── arm64-v8a/
│ └── <native_lib_1>.so
│ └── <native_lib_2>.so
├── x86/
│ └── <native_lib_1>.so
│ └── <native_lib_2>.so
└── x86_64/
└── <native_lib_1>.so
└── <native_lib_2>.so
There is only one subdirectory reserved for the respective CPU architecture (ABI).
Process: In case of installation through any installer for any Android application:
The Android device's Package Manager identifies the lib
directory, it knows which libraries are required for the device's ABI.
At runtime, Package Manager will extract the native libraries that are for this device's ABI
to some spot in the device's filesystem. Usually, that is in the private data directory of the application, for which class traditionally was:
/data/data/<package_name>/lib/
or for newer Android versions (6.0 and higher) within the split APK directories :
/data/app/<package_name>-<identifier>/lib/<ABI>/
Some Android applications include native libraries that have been coded in the likes of C/C++.
extractNativeLibs
Flag:The extractNativeLibs flag is set within an application's AndroidManifest.xml file.
This establishes how the native libraries should be handled by a package manager during installation.
extractNativeLibs
:extractNativeLibs=true
(Default for minSdkVersion < 21):/data/data/<package_name>/lib/
.
This approach ensures compatibility with older versions of Android (below API Level 21);extractNativeLibs=false
(Default for minSdkVersion >= 21):/data/app/<package_name>-< identifier>/
.For new applications that target API Level 21 and above, extractNativeLibs=false
is preferred because of efficiency, among other things. You'll probably want to use extractNativeLibs=true
if you need to support old Android versions or for other reasons, for example, making direct use of NDK functionalities in building native libraries aimed at specific ABIs. Overview Also, extractNativeLibs flags allow/disallow the extraction of native libraries upon installation of the application. Setting this field to false serves a value in higher versions of Android OS by ensuring the efficiency of the app's size, installation, and update.
These are then loaded at runtime, dynamically, whenever the application is run, by the Android Runtime
(ART), or by the older versions of Dalvik VM
. The system adds the path where these libraries are extracted to the native library search path for that application.
Typically you would load a native library from your Java/Kotlin code using the System.loadLibrary
method:
System.loadLibrary("native_lib_1");
It loads the respective .so
file from scanning the directories where the native libraries would have extracted.
APK ABI filters If you are targeting more than one ABI with your application, you can use APK ABI filters in your APK to include only the desired ABIs:
android {
...
defaultConfig {
...
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
...
}
Developers have the ability to create custom APK splits for various ABIs to optimize the delivery size of the application. As a result, even if we upload our application to the Play Store, the APK served by the Play Store contains only the ABI required by the device, and hence reduces the size of the APK. Can be done in the build.gradle
:
android {
...
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk false
}
}
}
More information can be found here :