我从NDK开始,想用它编译我的第一个hello-world应用程序。
我的应用程序只是一个带有Activity的简单应用程序,而我的MainActivity位于com.example.myapplication2.app
我想在其中使用原生方法,这就是我所做的:
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");
}
在我的jni文件夹中:
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
您好-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!");
}
在这里,我的朋友:
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'])
}
当我想运行我的项目时,我没有C错误(我之前有过,这就是为什么我确定编译已完成,然后确定)。
但是,当我想运行应用程序时,我有错误:
java.lang.UnsatisfiedLinkError:找不到本机方法:com.example.myapplication2.app.MainActivity.stringFromJNI :()Ljava / lang / String;
我检查过的是(Android NDK Native method not found error):
你知道为什么我会遇到这个错误吗?
答案 0 :(得分:3)
向您的应用启动添加System.loadLibrary("hello-jni")
来电。静态构造函数将是一个好地方。
答案 1 :(得分:3)
错误发生在gradle文件中,该文件没有做任何事情。
这是我的最终.gradle文件。
在编译之前,它会在编译之前添加三个任务来执行以下操作:
要求:
然后,它会将所有必需的.so文件包含到您的应用程序中,而您的gradle中不需要任何ndk命令。
它的优点:适用于所有平台的一个APK 不方便:APK更大,因为它包含所有平台的.so文件。
申请插件:'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'])
}