【问题标题】:Android Studio - Native method not found - Include .so file in buildAndroid Studio - 未找到本机方法 - 在构建中包含 .so 文件
【发布时间】:2014-03-21 20:16:54
【问题描述】:

我从 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)

应用程序.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):

  • com.example.myapplication2.app.MainActivity.stringFromJNI 和 Java_com_example_myapplication2_app_MainActivity_stringFromJNI 匹配
  • extern "C" 不存在,因为我在使用时遇到了这个错误:Error: "expected '(' before string constant"

你知道我为什么会遇到这个错误吗?

【问题讨论】:

    标签: android android-ndk android-gradle-plugin


    【解决方案1】:

    为您的应用启动添加System.loadLibrary("hello-jni") 调用。静态构造函数会是个好地方。

    【讨论】:

    • 对不起,我忘了提它,但它已经存在于我的代码中。我编辑我的问题。感谢您的快速响应。
    • 查看libhello-jni.so文件是否存在于(project)/libs/armeabi下。
    • 是的,它存在,也存在于 app/build/native-libs/armeabi/libhello-jni.so 下
    • 我使用的是 x86 编译,因为我在 Genymotion 下运行它。当我使用 ARM 编译时,我有另一个错误(并且,我的 apk 中没有 .so 文件): java.lang.UnsatisfiedLinkError: Couldn't load hello-jni from loader dalvik.system.PathClassLoader[DexPathList[[zip file " /data/app/com.example.myapplication2.app-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.example.myapplication2.app-1, /system/lib]]]: findLibrary 返回 null
    • APK 中没有 SO?英特尔构建中有一个吗?似乎构建过程以某种方式被破坏了。我对gradle一无所知:(
    【解决方案2】:

    错误在 gradle 文件中,它没有做任何事情。

    这是我的最终 .gradle 文件。

    在编译之前,它在编译之前添加了三个任务来做以下事情:

    1. 运行 NDK 构建
    2. 将 *.so 文件从 /src/main/libs 复制到 /build/lib
    3. 将/build/lib文件夹压缩成/libs/lib.jar

    要求:

    1. 你的 JDK 的 bin 文件夹必须在 PATH 环境变量中
    2. 您必须有一个 ANDROID_NDK 环境变量,其中包含您的 NDK(在 Android 网站上下载的那个)的路径

    然后,它会将所有需要的 .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'])
    }
    

    【讨论】:

    • 为什么需要放在罐子里?
    • jniLibs.srcDir 'src/main/libs' jni.srcDirs = [] //disable automatic ndk-build call .....这行帮助了我......谢谢
    猜你喜欢
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2014-03-30
    • 2016-01-20
    • 2016-02-10
    • 2023-03-17
    相关资源
    最近更新 更多