使用本机库(仅限 C,无 C++),但未找到实现

Posted

技术标签:

【中文标题】使用本机库(仅限 C,无 C++),但未找到实现【英文标题】:Using native lib (C only, no C++), and No Implementation Found 【发布时间】:2015-03-24 19:50:43 【问题描述】:

尝试在 android 和 AFAIK 上使用 this lib 的作品,链接和 .so 看起来不错。

调用 C 函数时出错(我关注了these intstructs)

D/OpusRecorder( 1964): Start recording!
E/art     ( 1964): No implementation found for int com.droidkit.opus.OpusLib.startRecord(java.lang.String) (tried Java_com_droidkit_opus_OpusLib_startRecord and Java_com_droidkit_opus_OpusLib_startRecord__Ljava_lang_String_2)
E/AndroidRuntime( 1964): FATAL EXCEPTION: Thread-8227
E/AndroidRuntime( 1964): Process: com.borneo.speech, PID: 1964
E/AndroidRuntime( 1964): java.lang.UnsatisfiedLinkError: No implementation found for int com.droidkit.opus.OpusLib.startRecord(java.lang.String) (tried Java_com_droidkit_opus_OpusLib_startRecord and Java_com_droidkit_opus_OpusLib_startRecord__Ljava_lang_String_2)
E/AndroidRuntime( 1964):    at com.droidkit.opus.OpusLib.startRecord(Native Method)
E/AndroidRuntime( 1964):    at com.borneo.speech.OpusRecorder.run(OpusRecorder.java:439)
W/ActivityManager(  724):   Force finishing activity 1 com.borneo.speech/.Speech_API_Activity

.so被打包在apk中

函数存在于 .so 中,Type="T"...

aar$ nm -D libopus.so | head
000084c1 T Java_com_droidkit_opus_OpusLib_closeOpusFile
000084c5 T Java_com_droidkit_opus_OpusLib_isOpusFile
00008491 T Java_com_droidkit_opus_OpusLib_openOpusFile
00008471 T Java_com_droidkit_opus_OpusLib_readOpusFile
00008489 T Java_com_droidkit_opus_OpusLib_seekOpusFile
00008145 T Java_com_droidkit_opus_OpusLib_startRecord ***

---CL 语言详细信息---

用 C 语言实现:

audio.c

#include "com_droidkit_opus_OpusLib.h"   <-- from the Java class=com.droidkit.opus.OpusLib via javah

//rev belo per @Nicklas

JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_startRecord(JNIEnv *env, jobject javaThis, jstring path) 
    const char *pathStr = (*env)->GetStringUTFChars(env, path, 0);

    int result = initRecorder(pathStr);

    if (pathStr != 0) 
        (*env)->ReleaseStringUTFChars(env, path, pathStr);
    

    return result;

c 标题...

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_droidkit_opus_OpusLib */

#ifndef _Included_com_droidkit_opus_OpusLib
#define _Included_com_droidkit_opus_OpusLib
#ifdef __cplusplus
extern "C" 
#endif
/*
 * Class:     com_droidkit_opus_OpusLib
 * Method:    startRecord
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_startRecord
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_droidkit_opus_OpusLib
 * Method:    writeFrame
 * Signature: (Ljava/nio/ByteBuffer;I)I
 */
JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_writeFrame
  (JNIEnv *, jobject, jobject, jint);

/*
 * Class:     com_droidkit_opus_OpusLib
 * Method:    stopRecord
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_droidkit_opus_OpusLib_stopRecord
  (JNIEnv *, jobject);

/*
 * Class:     com_droidkit_opus_OpusLib
 * Method:    isOpusFile
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_isOpusFile
  (JNIEnv *, jobject, jstring);

...
#ifdef __cplusplus

#endif
#endif

===JAVA 详细信息调用 C===

==EDITED== Java Native Decl.

/**
 * OpusLib native binding
 */
public class OpusLib 

    static 
        System.loadLibrary("opus");
    

    /**
     * Starting opus recording
     *
     * @param path path to file
     * @return non zero if started player
     */
    public native int startRecord(String path);


    /**
     * Writing audio frame to encoder
     *
     * @param frame buffer with sound in 16 bit mono PCM 16000 format
     * @param len   len of data
     * @return not null if successful
     */
    public native int writeFrame(ByteBuffer frame, int len);

    /**
     * Stopping record
     */
    public native void stopRecord();

    /**
     * Checking Opus File format
     *
     * @param path path to file
     * @return non zero if opus file
     */
    public native int isOpusFile(String path);

    /**
     * Opening file
     *
     * @param path path to file
     * @return non zero if successful
     */
    public native int openOpusFile(String path);

    /**
     * Seeking in opus file
     *
     * @param position position in file
     * @return non zero if successful
     */
    public native int seekOpusFile(float position);

    /**
     * Closing opus file
     */
    public native void closeOpusFile();

    /**
     * Reading from opus file
     *
     * @param buffer
     * @param capacity
     */
    public native void readOpusFile(ByteBuffer buffer, int capacity);

    /**
     * Is playback finished
     *
     * @return non zero if playback is finished
     */
    public native int getFinished();

    /**
     * Read block size in readOpusFile
     *
     * @return block size in bytes
     */
    public native int getSize();

    /**
     * Offset of actual sound for playback
     *
     * @return offset
     */
    public native long getPcmOffset();

    /**
     * Total opus pcm duration
     *
     * @return pcm duration
     */
    public native long getTotalPcmDuration();



        try 
            opus = new OpusLib();
        catch(UnsatisfiedLinkError ulx) 
            Log.e(LTAG, "Illegal native Load: " + ulx.getMessage());
        

...
public class OpusLib 
    static 
        System.loadLibrary("opus");
       

AFAIK,lib 加载正常

UnsatisfiedLink Excp 出现在下面的最后一行:

私有字符串 mPath = 路径;

   if (mShouldRecord)            
       int mint = opus.startRecord(mPath);     <-- throws unsatisfiedLink - no implementation found   

startRecord 在符号表中,尽管我不确定所有函数参数类型都匹配吗?见“string_2”...

startRecord__Ljava_lang_String_2)

==== 构建信息 Gradle 和linkedit ===

link :
command: /usr/local/src/android-ndk-r10d/ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/home/rob/src/tmp/speechnw/libraries/opus/build/intermediates/ndk/release/Android.mk APP_PLATFORM=android-19 NDK_OUT=/home/rob/src/tmp/speechnw/libraries/opus/build/intermediates/ndk/release/obj NDK_LIBS_OUT=/home/rob/src/tmp/speechnw/libraries/opus/build/intermediates/ndk/release/lib APP_STL=stlport_static APP_ABI=armeabi-v7a

[armeabi-v7a] Compile thumb  : opus <= audio.c                       
...
[armeabi-v7a] SharedLibrary  : libopus.so
[armeabi-v7a] Install        : libopus.so => /home/rob/src/tmp/speechnw/libraries/opus/build/intermediates/ndk/release/lib/armeabi-v7a/libopus.so

build.gradle

默认配置 minSdkVersion 8 targetSdkVersion 19 版本代码 1 版本名称“1.1.1”

ndk 
    moduleName "opus"

    cFlags "-DANDROID_NDK " +
            "-DDISABLE_IMPORTGL " +
            "-w -std=gnu99 -O3 -fno-strict-aliasing -fprefetch-loop-arrays " +
            "-DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 "+
            "-Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno "
            "-DAVOID_TABLES "
    ldLibs "log", "m"
    stl "stlport_static"
    abiFilter "armeabi-v7a"

【问题讨论】:

您是否使用javah 创建签名? 你在哪里声明你的类中的本地方法? 一些建议 - 在本地库加载时查找日志消息,确保您部署了最新版本的本地库,并尝试将 ndk 示例 hello-jni 合并到您的项目中,看看是否那个功能仍然有效(最后一个可能真的很有帮助——同时工作和不工作的代码往往会突出问题) 【参考方案1】:

替换这一行:

JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_startRecord(JNIEnv *env, jclass class, jstring path) 

与:

JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_startRecord(JNIEnv *env, jobject javaThis, jstring path) 

您在 C 中定义的函数映射到 Java 中的静态方法,但在 Java 中您将其用作实例方法。我替换的是:jclass class, 在你的 C 函数签名中带有 jobject javaThis,。另请注意,在您的 C 标头中,您还声明了带有实例方法签名的函数,正如您似乎想要的那样。

【讨论】:

您可能对后续功能有所了解,但这将如何解释观察到的问题?共享库中没有对参数类型或计数信息进行编码,因此动态链接器无法区分静态方法和实例方法,除非希望名称使用一致。 @Chris Stratton - “动态链接器无法区分静态方法和实例方法,除了希望名称使用一致” - 它定义明确,并且javah 在签名中编码。例如,请参阅Java Native Interface (JNI)。 @Nicklas 谢谢。您的解释是有道理的,但是... JNIEXPORT jint JNICALL Java_com_droidkit_opus_OpusLib_startRecord(JNIEnv *env, jobject javaThis, jstring path) --> logcat 中的相同错误 ... java.lang.UnsatisfiedLinkError: No implementation found for int com.droidkit.opus。 OpusLib.startRecord(java.lang.String)(试过 Java_com_droidkit_opus_OpusLib_startRecord 和 Java_com_droidkit_opus_OpusLib_startRecord__Ljava_lang_String_2) @jww - 不,事实并非如此,参数签名不会最终出现在共享库中。在nm 输出中查找本机方法实现的方法名称 - 这就是动态链接器可用的全部 - 根本不存在参数类型信息。其他任何东西都纯粹是 vm 混淆 自身 - 即,不是 C 代码的错。这与方法一旦链接失败的问题不同 - 现在的问题是它没有被链接。 @ChrisStratton 谢谢。重新“不被链接”。也许我应该回去使用显式 Android.mk 而不是 ndk 的集成 gradle DSL。我从来没有尝试过使用 ndk 的默认 DSL 任务构建 AStudio 类型的 android ndk。一直对 AS-ndk 使用 'ph0b's' 方法。 git模块有这种方式,所以我就用它了吗? ph0b.com/android-studio-gradle-and-ndk-integration

以上是关于使用本机库(仅限 C,无 C++),但未找到实现的主要内容,如果未能解决你的问题,请参考以下文章

调试不适用于 Android Studio 的 C++/本机库模块(使用 Cmake)

本机 C++ 通过代理 C++ 托管 dll 使用 C# dll

网页抓取选项 - 仅限 C++ 版本

在本机 C++ 项目中导入 C# dll 库

如何将本机 C++ 静态库链接到托管 C++ 程序集

C++实现查询本机信息并且上报