ubuntu下搭建eclipse + ndk编译JNI库

来源:互联网 发布:大数据的发展前景知乎 编辑:程序博客网 时间:2024/06/05 16:29

因为APK需要调用jni so库,想在eclipse中编译,不想在代码中编译so库再放到apk中,所以搭建了eclipse中的环境

首先eclipse中新建一个android项目,我的建立名字叫NDKJNI,MainActivity代码如下:

package com.example.ndkjni;import android.os.Bundle;import android.app.Activity;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;public class MainActivity extends Activity {//静态代码块在类第一次被加载的时候调用      static      {          System.loadLibrary("ndkjni");//名字还是叫ndkjni      }      //定义一个本地方法, 在jni目录中的c代码来实现这个方法      public native String helloFromC();            //带下划线, 因为C方法中声明也是下划线. 有区别.      public native String hello_c();  @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button1 = (Button) findViewById(R.id.button1);        button1.setOnClickListener(new OnClickListener()          {              @Override              public void onClick(View v)              {                  Toast.makeText(MainActivity.this, helloFromC(),                          Toast.LENGTH_SHORT).show();//Toast              }          });  }@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}

接着运行apk,这时候由于没有so库,编译是通过,但是运行肯定是报错误的,不过我们要的是生成的bin目录,看看项目目录下生成的bin目录:

lsc@lsc:~/workspace/NDKJNI/bin$ tree.├── AndroidManifest.xml├── classes│   └── com│       └── example│           └── ndkjni│               ├── BuildConfig.class│               ├── MainActivity$1.class│               ├── MainActivity.class│               ├── R$attr.class│               ├── R.class│               ├── R$dimen.class│               ├── R$drawable.class│               ├── R$id.class│               ├── R$layout.class│               ├── R$menu.class│               ├── R$string.class│               └── R$style.class├── classes.dex├── dexedLibs

接着在vim终端cd到bin/classes目录中,运行如下命令:

javah -classpath ./ -jni com.example.ndkjni.MainActivity

以上是eclipse项目,如果是studio项目,那应该在src/main/java目录下运行以上命令

这样会看到在当前目录下生成一个com_example_ndkjni_MainActivity.h文件

接着在eclipse中的NDKJNI项目中右键,新增一个名字叫jni的文件夹,右键复制com_example_ndkjni_MainActivity.h文件,直接在jni目录右键选择粘帖,这样com_example_ndkjni_MainActivity.h就被放到jni目录下了,我的com_example_ndkjni_MainActivity.h如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_ndkjni_MainActivity */#ifndef _Included_com_example_ndkjni_MainActivity#define _Included_com_example_ndkjni_MainActivity#ifdef __cplusplusextern "C" {#endif/* * Class:     com_example_ndkjni_MainActivity * Method:    helloFromC * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_helloFromC  (JNIEnv *, jobject);/* * Class:     com_example_ndkjni_MainActivity * Method:    hello_c * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_hello_1c  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

右键jni目录,新增一个叫com_example_ndkjni_MainActivity.cpp的文件(记住是cpp,不是c哦,c后面再说),加入内容如下:

#include "com_example_ndkjni_MainActivity.h"#include "jni.h"#include <android/log.h>#define LOG_TAG "System.out.c"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_helloFromC  (JNIEnv *env, jobject obj){    LOGI("this is a logcat from c");//在logcat中显示    //不能是中文.  (*(*env)).NewStringUTF 是一样的    return (env)->NewStringUTF("hello , my NDK!");}JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_hello_1c  (JNIEnv *env, jobject obj){ return (env)->NewStringUTF("hello , my NDK_____!");}

接着,为了编译c/c++文件,我们要配置一下eclipse和在jni目录下新增一个Android.mk,Android.mk内容如下:

    LOCAL_PATH := $(call my-dir)               include $(CLEAR_VARS)               LOCAL_MODULE    := ndkjni        LOCAL_SRC_FILES := com_example_ndkjni_MainActivity.cpp               LOCAL_LDLIBS += -llog #引入log库         include $(BUILD_SHARED_LIBRARY)  

然后在NDKJNI项目右键,new -> other.... -> C/C++ -> Convert to a C/C++ Project

这样,就可以编译C/C++了,接着配置编译脚本,同样右键项目 -> Properties -> C/C++ Build,把Use default bulid commad选项去掉,在下面的bulid command输入

android ndk包的路径,我的是输入:/home/lsc/android-ndk-r8e/ndk-build NDK_DEBUG=1(如果配置好了ndk环境,直接输入ndk-build NDK_DEBUG=1就行了)
为了去掉错误提示,以便编译通过,还需要在properties - > C/C++ General -> Code Analysis -> Syntax and Samantic Error中去掉相应的错误提示

这样就可以编译apk了

如果JNK是C文件而不是C++,那么除了修改Android.mk的里面的LOCAL_SRC_FILES名字外,c中的方法也有点不一样,如下:

#include "com_example_ndkjni_MainActivity.h"#include "jni.h"#include <android/log.h>#define LOG_TAG "System.out.c"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_helloFromC  (JNIEnv *env, jobject obj){    LOGI("this is a logcat from c");//在logcat中显示    //不能是中文.  (*(*env)).NewStringUTF 是一样的    return (*env)->NewStringUTF(env,"hello , my NDK!");}JNIEXPORT jstring JNICALL Java_com_example_ndkjni_MainActivity_hello_1c  (JNIEnv *env, jobject obj){ return (*env)->NewStringUTF(env,"hello , my NDK_____!");}
之所以C和C++不一样,是因为 C 中,JNI 函数调用由“(*env)->”作前缀,目的是为了取出函数指针所引用的值; C++ 中,JNIEnv 类拥有处理函数指针查找的内联成员函数。

下面将说明这个细微的差异,其中,这两行代码访问同一函数,但每种语言都有各自的语法。比如别的方法,在使用C/C++时候也有差别:

C 语法:jsize len = (*env)->GetArrayLength(env,array);

C++ 语法:jsize len =env->GetArrayLength(array);




如下还有一个范例,可以实现java和JNI相互调用变量和函数等

JNI  SerialPort.c:

/* * Copyright 2009-2011 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include <termios.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#include <jni.h>#include "SerialPort.h"#include "android/log.h"static const char *TAG="serial_port";#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)jclass javaClass;static speed_t getBaudrate(jint baudrate){switch(baudrate) {case 4800: return B4800;case 9600: return B9600;case 115200: return B115200;case 921600: return B921600;default: return -1;}}/* * Class:     android_serialport_SerialPort * Method:    open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_com_gpse_abc_SerialPort_nativeopen  (JNIEnv *env, jobject obj, jstring path, jint baudrate, jint flags){int fd;speed_t speed;jobject mFileDescriptor;/* Check arguments */{speed = getBaudrate(baudrate);if (speed == -1) {/* TODO: throw an exception */LOGE("Invalid baudrate");return NULL;}}/* Opening device */{jboolean iscopy;const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);fd = open(path_utf, O_RDWR | flags);LOGD("open() fd = %d", fd);(*env)->ReleaseStringUTFChars(env, path, path_utf);if (fd == -1){/* Throw an exception */LOGE("Cannot open port");return NULL;}}/* Configure device */{struct termios cfg;LOGD("Configuring serial port");if (tcgetattr(fd, &cfg)){LOGE("tcgetattr() failed");close(fd);return NULL;}cfmakeraw(&cfg);cfsetispeed(&cfg, speed);cfsetospeed(&cfg, speed);if (tcsetattr(fd, TCSANOW, &cfg)){LOGE("tcsetattr() failed");close(fd);return NULL;}}/* Create a corresponding file descriptor */{jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");//这个比较特殊,这个是默认构造函数的方法,一般用这个来初始化对象jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");//创建一个java空间对象mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);//为这个java空间对象赋值(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);}return mFileDescriptor;}/* * Class:     com_gpse_abc_SerialPort * Method:    read * Signature: ([Ljava/lang/String;I)V */JNIEXPORT void JNICALL Java_com_gpse_abc_SerialPort_nativeread  (JNIEnv *env, jobject obj, jobjectArray buffer, jint test){#if 1//表示获得obj属于那个类if(!javaClass)javaClass = (*env)->GetObjectClass(env, obj);//用这个指定类名的方法也可以获取jclass//jclass javaClass = (*env)->FindClass(env, "com/gpse/abc/CardReader");//获得java的JNICallJAVA方法jmethodID callback = (*env)->GetMethodID(env, javaClass, "JNICallJAVA", "(Ljava/lang/String;)V");//获得java 的jni_buffer[],[表示数组jfieldID javaBuffer = (*env)->GetFieldID(env, javaClass, "jni_buffer","[Ljava/lang/String;");//获得java的int类型成员jfieldID javaInt = (*env)->GetFieldID(env, javaClass, "int_test_JNI", "I");//获得java的String类型成员jfieldID javaString = (*env)->GetFieldID(env, javaClass, "string_test_JNI", "Ljava/lang/String;");jobject jBuffer = (*env)->GetObjectField(env, obj, javaBuffer);jint jInt = (*env)->GetIntField(env, obj, javaInt);LOGD("jInt = %d", jInt);(*env)->SetIntField(env, obj, javaInt, 87654321);jstring jString = (*env)->GetObjectField(env, obj, javaString);const char *jstr = (*env)->GetStringUTFChars(env, jString, NULL);LOGE("jString = %s", jstr);//jstring* p_jString = (*env)->GetObjectArrayElement(env, buffer, 0);//不能直接把"i am JNI!"作为参数传给java,而是要NewStringUTF,用完后接的deletejstring sendToJava = (*env)->NewStringUTF(env, "nativeread i am JNI!");(*env)->SetObjectField(env, obj, javaString, sendToJava);//invoke java function: update_fragment(*env)->CallVoidMethod(env, obj, callback, sendToJava);(*env)->DeleteLocalRef(env, sendToJava);#endif}/* * Class:     com_gpse_abc_SerialPort * Method:    native_setvalue * Signature: (ILjava/lang/String;[I[Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_gpse_abc_SerialPort_native_1setvalue  (JNIEnv *env, jobject obj, jint ji, jstring js, jintArray iarr, jobjectArray objarr){int i;if(!javaClass)javaClass = (*env)->GetObjectClass(env, obj);jstring sendToJava = (*env)->NewStringUTF(env, "i am JNI!");const char *jstr = (*env)->GetStringUTFChars(env, js, NULL);LOGE("jstr = %s", jstr);//(*env)->SetObjectField(env, obj, js, sendToJava);jsize length = (*env)->GetArrayLength(env, iarr);jint* jInt = (*env)->GetIntArrayElements(env, iarr, 0);for(i = 0; i < length; i++){LOGE("iarr[%d] = %d", i, jInt[i]);jInt[i] = i + 100;}(*env)->ReleaseIntArrayElements(env, iarr, jInt, 0);length = (*env)->GetArrayLength(env, objarr);for(i = 0; i < length; i++){jobject jString = (*env)->GetObjectArrayElement(env, objarr, i);jstr = (*env)->GetStringUTFChars(env, jString, NULL);LOGE("objarr[%d] = %s", i, jstr);(*env)->SetObjectArrayElement(env, objarr, i, sendToJava);(*env)->DeleteLocalRef(env, jString);}(*env)->DeleteLocalRef(env, sendToJava);//(*env)->ReleaseIntArrayElements(env, iarr, jInt, 0);(*env)->ReleaseStringUTFChars(env, js, jstr);return (jint)0;}/* * Class:     com_gpse_abc_SerialPort * Method:    close * Signature: ()V */JNIEXPORT void JNICALL Java_com_gpse_abc_SerialPort_nativeclose  (JNIEnv *env, jobject thiz){jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);LOGD("close(fd = %d)", descriptor);close(descriptor);}#define JNIREG_CLASS "com/gpse/abc/SerialPort"//指定要注册的类static JNINativeMethod gMethods[] = {{ "nativeopen", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void*)Java_com_gpse_abc_SerialPort_nativeopen },//绑定{ "nativeread", "([Ljava/lang/String;I)V", (void*)Java_com_gpse_abc_SerialPort_nativeread },//绑定{ "nativeclose", "()V", (void*)Java_com_gpse_abc_SerialPort_nativeclose},//绑定};static int registerNativeMethods(JNIEnv* env, const char* className,        JNINativeMethod* gMethods, int numMethods){jclass clazz;clazz = (*env)->FindClass(env, className);if (clazz == NULL) {return JNI_FALSE;}if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {return JNI_FALSE;}return JNI_TRUE;}/** Register native methods for all classes we know about.*/static int registerNatives(JNIEnv* env){if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,                                 sizeof(gMethods) / sizeof(gMethods[0])))return JNI_FALSE;return JNI_TRUE;}#if 1JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){JNIEnv* env = NULL;jint result = -1;if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {return -1;}assert(env != NULL);if (!registerNatives(env)) {//注册return -1;}/* success -- return valid version number */result = JNI_VERSION_1_4;return result;}#endif

JNI SerialPort.h

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>#include<assert.h>/* Header for class android_serialport_api_SerialPort */#ifndef _Included_android_serialport_api_SerialPort#define _Included_android_serialport_api_SerialPort#ifdef __cplusplusextern "C" {#endif/* * Class:     android_serialport_api_SerialPort * Method:    open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_com_gpse_abc_SerialPort_nativeopen  (JNIEnv *, jobject, jstring, jint, jint);/* * Class:     com_gpse_abc_SerialPort * Method:    read * Signature: ([Ljava/lang/String;I)V */JNIEXPORT void JNICALL Java_com_gpse_abc_SerialPort_nativeread  (JNIEnv *, jobject, jobjectArray, jint);/* * Class:     com_gpse_abc_SerialPort * Method:    native_setvalue * Signature: (ILjava/lang/String;[I[Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_gpse_abc_SerialPort_native_1setvalue  (JNIEnv *, jobject, jint, jstring, jintArray, jobjectArray);/* * Class:     android_serialport_api_SerialPort * Method:    close * Signature: ()V */JNIEXPORT void JNICALL Java_com_gpse_abc_SerialPort_nativeclose  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif



java SerialPort.java:

/* * Copyright 2009 Cedric Priscal *  * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *  * http://www.apache.org/licenses/LICENSE-2.0 *  * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.  */package com.gpse.abc;import android.util.Log;import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;public class SerialPort {    private static final String TAG = "SerialPort";    private  String string_test_JNI = "JNI call JAVA String";    private int int_test_JNI = 12345678;    private String jni_buffer[];    private String java_buffer[] = {"fafasd","afasdfa", "ddddddddfdf"};        private int intarray[] = {1, 2, 3, 4, 5};    private String str = "wo ha ha ";    /*     * Do not remove or rename the field mFd: it is used by native method close();     */    private FileDescriptor mFd;    private FileInputStream mFileInputStream;    private FileOutputStream mFileOutputStream;    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {/* Check access permission */        if (!device.canRead() || !device.canWrite()) {            try {                /* Missing read/write permission, trying to chmod the file */                Process su;                su = Runtime.getRuntime().exec("/system/bin/su");                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"                        + "exit\n";                su.getOutputStream().write(cmd.getBytes());                if ((su.waitFor() != 0) || !device.canRead()                        || !device.canWrite()) {                    throw new SecurityException();                }            } catch (Exception e) {                e.printStackTrace();                throw new SecurityException();            }        }        mFd = nativeopen(device.getAbsolutePath(), baudrate, flags);        if (mFd == null) {            Log.e(TAG, "native open returns null");            throw new IOException();        } else {            Log.d(TAG, "Open Serial: " + device.getAbsolutePath() + " Success");        }        mFileInputStream = new FileInputStream(mFd);        mFileOutputStream = new FileOutputStream(mFd);                nativeread(java_buffer, 88888888);        Log.e(TAG, "after call JNI read, int_test_JNI = " + int_test_JNI);                native_setvalue(7845, str, intarray, java_buffer);        Log.e(TAG, "after call JNI setvalue");        Log.e(TAG, "str = " + str);                for(int i =0; i < intarray.length; i++)        Log.e(TAG, "intarray = " + intarray[i]);                for(int i =0; i < java_buffer.length; i++)        Log.e(TAG, "java_buffer = " + java_buffer[i]);                    }    // Getters and setters    public InputStream getInputStream() {        return mFileInputStream;    }    public OutputStream getOutputStream() {        return mFileOutputStream;    }    void JNICallJAVA(String buf){    Log.e(TAG, "JNI call java function: " + buf);    }        // JNI    private native FileDescriptor nativeopen(String path, int baudrate, int flags);    public native void nativeread(String buf[],int test);    public native int native_setvalue(int i, String s, int[] in, String[] st);    public native void nativeclose();    static {        System.loadLibrary("cardreader");    }}

最后附上的是调试过程遇到的错误log以及解决方法:

1.D/serial_port( 3035): Configuring serial portW/dalvikvm( 3035): JNI WARNING: expected return type 'I'W/dalvikvm( 3035):              calling Lcom/gpse/abc/CardReader;.update_fragment ()VW/dalvikvm( 3035):              in Lcom/gpse/abc/SerialPort;.open:(Ljava/lang/String;II)Ljava/io/FileDescriptor; (CallIntMethod)W/dalvikvm( 3035): JNI WARNING: can't call Lcom/gpse/abc/CardReader;.update_fragment on instance of Lcom/gpse/abc/SerialPort;W/dalvikvm( 3035):              in Lcom/gpse/abc/SerialPort;.open:(Ljava/lang/String;II)Ljava/io/FileDescriptor; (CallIntMethod)F/libc    ( 3035): Fatal signal 11 (SIGSEGV) at 0x572ae158 (code=2), thread 3035 (com.gpse.abc)原因:JNI使用来(*env)->CallVoidMethod(env, obj, callback)方法,但是java层定义的是void的返回类型,所以要更改为CallVoidMethod即可2.如果java的native方法声明为satic类型,则JNI方法的型参是没有 jobject 参数的,而是jclass参数3.如果JNI是cpp代码,则用例如下面的代码env->GetFieldID(cFileDescriptor, "descriptor", "I");如果JNI是c代码,则用例如下面的代码(*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");4.request for member 'GetFieldID' in something not a structure or union这是由于c文件用了env->GetFieldID方法,正确的应该是(*env)->GetFieldID5.W/dalvikvm( 5028): JNI WARNING: jclass arg has wrong type (expected Ljava/lang/Class;, got Lcom/gpse/abc/SerialPort;)W/dalvikvm( 5028):              in Lcom/gpse/abc/SerialPort;.read:([Ljava/lang/String;I)V (GetFieldID)F/libc    ( 5028): Fatal signal 11 (SIGSEGV) at 0x00000004 (code=1), thread 5028 (com.gpse.abc)I/DEBUG   ( 1320): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***I/DEBUG   ( 1320): Build fingerprint: 'Infotmic/m75d_ls/m75d_ls:4.1.1/JRO03H/eng.jbs.20130315.190303:user/release-keys'出现类似上面的错误,是因为JNI中(*env)->GetFieldID(env, obj, "int_test_JNI", "I"),这里第二个型参不能为obj对象,而要为jclass类6.W/dalvikvm( 7017): JNI WARNING: instance fieldID 0x40fdd1d8 not valid for class Lcom/gpse/abc/SerialPort;W/dalvikvm( 7017):              in Lcom/gpse/abc/SerialPort;.read:([Ljava/lang/String;I)V (GetObjectField)F/libc    ( 7017): Fatal signal 11 (SIGSEGV) at 0x82018618 (code=1), thread 7017 (com.gpse.abc)I/DEBUG   ( 1320): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***7.E/dalvikvm( 8440): ERROR: couldn't find native methodE/dalvikvm( 8440): Requested: Lcom/gpse/abc/CardReader;.nativeopen:(Ljava/lang/String;II)Ljava/io/FileDescriptorE/serial_port( 8440): 22222W/dalvikvm( 8440): JNI_OnLoad returned bad version (-1) in /data/data/com.gpse.abc/lib/libcardreader.so 0x41016378W/dalvikvm( 8440): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/gpse/abc/SerialPort;D/AndroidRuntime( 8440): Shutting down VM原因是注册的JNINativeMethod中:{ "nativeopen", "(Ljava/lang/String;II)Ljava/io/FileDescriptor", (void*)Java_com_gpse_abc_SerialPort_nativeopen }这里第二项少了一个分号,正确的为:{ "nativeopen", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void*)Java_com_gpse_abc_SerialPort_nativeopen }













原创粉丝点击