Android Studio - Native method not found - Include .so file in build
android, android-gradle-plugin, android-ndk
Solution
The error was in gradle file, which was not doing everything.
Here is my final .gradle file.
Before compilation, it adds three tasks before compilation to do the following :
- Run the NDK build
- Copy the *.so files from /src/main/libs to /build/lib
- Compress the /build/lib folder into /libs/lib.jar
Requirements :
- Your bin folder of JDK must be in the PATH environment variable
- You must have an ANDROID_NDK environment variable with the path to your NDK (the one downloaded on Android website)
Then, it will include all the needed .so files into your application, without any ndk command needed in your gradle.
Advantage of it : One APK for all platforms Inconvenient of it : APK bigger, because it contains .so files for all platforms.
apply plugin: 'android'
android {
compileSdkVersion 19
buildToolsVersion "19.0.3"
defaultConfig {
minSdkVersion 8
targetSdkVersion 19
versionCode 1
versionName "1.0"
ndk {
moduleName "hello-jni"
}
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call
}
}
task buildNative(type: Exec) {
if (System.env.ANDROID_NDK != null) {
println 'Running NDK build'
workingDir "src/main"
commandLine 'cmd', '/c', '%ANDROID_NDK%/ndk-build'
} else {
doLast {
println '##################'
println 'Skipping NDK build'
println 'Reason: ANDROID_NDK not set.'
println '##################'
}
}
}
task copyNativeLibs(dependsOn:buildNative, type: Copy) {
println 'Copying *.so files from /src/main/libs to /build/lib'
from(new File('src/main/libs')) { include '**/*.so' }
into new File(buildDir, 'lib')
}
task nativeLibsToJar(dependsOn:copyNativeLibs, type: Exec, description: 'create a jar archive of the native libs') {
println 'Compressing /build/lib into /libs/lib.jar'
workingDir "build"
commandLine 'cmd', '/c', 'jar cf ../libs/lib.jar lib'
}
tasks.withType(Compile) { compileTask -> compileTask.dependsOn nativeLibsToJar }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
pkgTask.jniFolders = new HashSet<File>();
pkgTask.jniFolders.add(new File(projectDir, 'native-libs'))
}
dependencies {
compile 'com.android.support:appcompat-v7:+'
compile fileTree(dir: 'libs', include: ['*.jar'])
}
Problem
I'm starting with NDK, and want to compile my first hello-world app with it. My application is just a simple application with an Activity, and my MainActivity is in com.example.myapplication2.app I would like to use a native method in it, and here is what I did : MainActivity.java ``` @Override protected void onCreate(Bundle savedInstanceState) { TextView tv = new TextView(this); tv.setText(stringFromJNI()); setContentView(tv); } public native String stringFromJNI(); static { System.loadLibrary("hello-jni"); } ``` in my jni folder : Android.mk ``` LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) ``` Application.mk ``` APP_ABI := all ``` hello-jni.c ``` #include <string.h> #include <jni.h> JNIEXPORT jstring JNICALL Java_com_example_myapplication2_app_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz) { return (*env)->NewStringUTF(env, "Hello from native code!"); } ``` And here, my gradle : ``` apply plugin: 'android' android { compileSdkVersion 19 buildToolsVersion "19.0.3" defaultConfig { minSdkVersion 8 targetSdkVersion 19 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } sourceSets.main { jniLibs.srcDir 'src/main/libs' jni.srcDirs = [] //disable automatic ndk-build call } productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { abiFilter "x86" } } mips { versionCode Integer.parseInt("4" + defaultConfig.versionCode) ndk { abiFilter "mips" } } armv7 { versionCode Integer.parseInt("2" + defaultConfig.versionCode) ndk { abiFilter "armeabi-v7a" } } arm { versionCode Integer.parseInt("1" + defaultConfig.versionCode) ndk { abiFilter "armeabi" } } fat } } def getVersionCodeFromManifest() { def manifestFile = file(android.sourceSets.main.manifest.srcFile) def pattern = Pattern.compile("versionCode=\"(\\d+)\"") def matcher = pattern.matcher(manifestFile.getText()) matcher.find() return Integer.parseInt(matcher.group(1)) } task copyNativeLibs(type: Copy, dependsOn: 'buildNative') { // TODO fix deprecated dependsOn 'buildNative' from(new File('src/main/libs')) { include '**/*.so' } into new File(buildDir, 'native-libs') } tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs } clean.dependsOn 'cleanCopyNativeLibs' tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask -> pkgTask.jniFolders = new HashSet<File>(); pkgTask.jniFolders.add(new File(projectDir, 'native-libs')) } task buildNative(type: Exec) { if (System.env.ANDROID_NDK != null) { def ndkBuild = new File(System.env.ANDROID_NDK, 'ndk-build') workingDir "src/main/jni" commandLine 'cmd', '/c', '%ANDROID_NDK%\\ndk-build' //executable ndkBuild } else { doLast { println '##################' println 'Skipping NDK build' println 'Reason: ANDROID_NDK not set.' println '##################' } } } task nativeLibsToJar( type: Zip, description: 'create a jar archive of the native libs') { destinationDir file("$buildDir/native-libs") baseName 'native-libs' extension 'jar' from fileTree(dir: 'libs', include: '**/*.so') into 'lib/' } dependencies { compile 'com.android.support:appcompat-v7:+' compile fileTree(dir: 'libs', include: ['*.jar']) } ``` When I want to run my project, I have no C error (I had before, that's why I'm sure compilation is done, and OK). But, when I want to run the application, I have the error : java.lang.UnsatisfiedLinkError: Native method not found: com.example.myapplication2.app.MainActivity.stringFromJNI:()Ljava/lang/String; What I've checked is (Android NDK Native method not found error) : - com.example.myapplication2.app.MainActivity.stringFromJNI and Java_com_example_myapplication2_app_MainActivity_stringFromJNI are matching - extern "C" is not present, because when I used it, I encountered this error : Error: "expected '(' before string constant" May you know why I encounter this error ?