在android中使用JNI对“乘法”的未定义引用

Posted

技术标签:

【中文标题】在android中使用JNI对“乘法”的未定义引用【英文标题】:undefined reference to 'multiply' using JNI in android 【发布时间】:2015-03-18 06:06:48 【问题描述】:

我是 android JNI 的新手并尝试以下代码:

包名:com.example.jnitest

Java 类:

public class MainActivity extends Activity 

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    MainActivity mainActivity = new MainActivity();


    System.out.println("Sum of two variables : "+ sumOfTwovariable((int)mainActivity.multiply(10, 25), 10));


public native long sumOfTwovariable(int v1, int v2);

static 
    System.loadLibrary("JNITest");


public native long multiply(int v1, int v2);
static
    System.loadLibrary("com_example_pdemo_MainActivity");




Android.mk 文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := JNITest
LOCAL_SRC_FILES := JNITest.cpp
LOCAL_SHARED_LIBRARIES := Prebuild_com_example_pdemo_MainActivity
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/includes
LOCAL_STATIC_LIBRARIES := Prebuild_com_example_pdemo_MainActivity
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE    := Prebuild_com_example_pdemo_MainActivity
LOCAL_SRC_FILES :=    $(TARGET_ARCH_ABI)/libcom_example_pdemo_MainActivity.so

include $(PREBUILT_SHARED_LIBRARY)

我已将预构建的 com_example_pdemo_MainActivity.so 库放在 JNI-> armeabi(write multiple function) 文件夹中,包含在 Android.mk 文件中


JNI 内部:

使用以下命令创建头文件:javah -jni -classpath....

Header file created using javah : com_example_jnitest_MainActivity.h

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

#ifndef _Included_com_example_jnitest_MainActivity
#define _Included_com_example_jnitest_MainActivity
#ifdef __cplusplus
extern "C" 
#endif
/*
 * Class:     com_example_jnitest_MainActivity
  * Method:    sumOfTwovariable
  * Signature: (II)J
*/
JNIEXPORT jlong JNICALL    Java_com_example_jnitest_MainActivity_sumOfTwovariable(JNIEnv *, jobject, jint, jint);

 #ifdef __cplusplus
 
#endif

#endif

原生函数的定义:JNITest.cpp

  #include "com_example_jnitest_MainActivity.h"

extern "C" 
long multiply(int val1,int val2);


   JNIEXPORT jlong JNICALL      Java_com_example_jnitest_MainActivity_sumOfTwovariable(
    JNIEnv *env, jobject obj, jint val1, jint val2) 
int val=multiply(val1,val2);
return (val+val1);

当我编译它时,我得到了对“乘法”的未定义引用。让我知道我犯了什么错误。

提前致谢..

【问题讨论】:

您是否在本机代码中的任意位置定义了乘法? 是的,我有。我在 com_example_pdemo_MainActivity.so(exteranl .so 文件)中编写了多个函数定义。 @YogeshChander 你在哪里定义了乘法? 我已经在 com_example_pdemo_MainActivity.so(预建共享库文件)中编写了乘法函数定义。 请发布您的 com_example_pdemo 的本机代码 【参考方案1】:

multiply 也是原生方法。应该和 sumOfTwovariable 一样定义:

JNIEXPORT jlong JNICALL    Java_com_example_jnitest_MainActivity_multiply(JNIEnv *, jobject, jint, jint);

【讨论】:

我已经预构建共享库 com_example_pdemo_MainActivity.so 库放在 JNI-> armeabi 中。在该预构建共享库中,我定义了乘法函数 @YogeshChander 但是你有没有按照这个问题中的代码定义它?【参考方案2】:

在 com_example_pdemo_MainActivity.so 的本机代码中,像这样定义乘法:

// this can be called from native code, including in different .so files:
long multiply(int val1,int val2)

    long result;
    // ...compute result...
    return result;


// this can be called from Java via JNI:
JNIEXPORT jlong JNICALL Java_com_example_pdemo_MainActivity_multiply(JNIEnv *env, jobject obj, jint a, jint b)

     return multiply(a, b);

这将允许您从本机代码或通过 JNI 调用它。

将您的 Android.mk 文件更改为此,将预构建的 .so 文件放在首位:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := Prebuild_com_example_pdemo_MainActivity
LOCAL_SRC_FILES :=    $(TARGET_ARCH_ABI)/libcom_example_pdemo_MainActivity.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := JNITest
LOCAL_SRC_FILES := JNITest.cpp
LOCAL_SHARED_LIBRARIES := Prebuild_com_example_pdemo_MainActivity
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/includes
include $(BUILD_SHARED_LIBRARY)

最后,在您的 Java 代码中,按以下顺序将两个库加载到一个静态块中:

static
    System.loadLibrary("com_example_pdemo_MainActivity");
    System.loadLibrary("JNITest");

JNITest 需要 com_example_pdemo_MainActivity,所以它需要先运行,否则会出现运行时链接错误。

此外,在 JNITest.cpp 中,删除围绕 multiply() 声明的 extern "C"。如果您使用 C++ 链接编译了 com_example_pdemo_MainActivity.so 文件,那么在尝试与 JNITest 链接时使用 C extern 将导致链接错误:

// extern "C" <-- remove
    long multiply(int v1,int v2);
// <-- remove

如何在字节缓冲区中将数据传入和传出 JNI:

JNIEXPORT int JNICALL Java_com_example_jnitest_MainActivity_compressImage(JNIEnv *pEnv, jobject this, jobject inputByteBuffer, jobject outputByteBuffer)

    jbyte* pInBuf = (jbyte*)(*pEnv)->GetDirectBufferAddress(pEnv, inputByteBuffer);
    jbyte* pOutBuf = (jbyte*)(*pEnv)->GetDirectBufferAddress(pEnv, outputByteBuffer);

    int lenOutImage = 0; char *pOutputImage;
    pOutputImage = CompressImage((char *)pInBuf,&lenOutImage);

    // copy to byte buffer
    memcpy((char)pOutBuf, pOutputImage, lenOutImage);        
    free(pOutputImage); // free pOutputImage?

    return lenOutImage;

【讨论】:

为什么?为什么要写两种方法,一种方法? @EJP 因为 OP 想要从本机代码和 Java 调用 multiply() (假设有本机声明)。可以直接从 Java_com_example_jnitest_MainActivity_sumOfTwovariable 调用 Java_com_example_pdemo_MainActivity_multiply,毫无意义地传递 env 和 obj 参数,但这很丑不是吗?他们每次想从本机代码中调用它时都必须使用 JNI 版本吗? 在他关于从本机代码调用 multiply() 的问题中没有一个字。 @EJP 是的。这就是他在 JNITest.cpp 中尝试做的事情,这就是导致错误的原因 我已经尝试过了,但遇到了同样的问题。让我知道任何其他相同的建议。谢谢

以上是关于在android中使用JNI对“乘法”的未定义引用的主要内容,如果未能解决你的问题,请参考以下文章

在android NDK中包含本地头文件时的未定义引用

对函数 Android NDK 的未定义引用

使用 Android NDK 构建本机 OpenCV 会给出“对 'cv::String::deallocate()' 的未定义引用”

Android FFmpeg:对atof,log2和log2f的未定义引用

对 `CORBA::ValueBase::ValueBase()' 的未定义引用,我该如何解决? (在omniORB中使用哪个库)

使用 CMake 对 SDL 的未定义引用