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 }
- ubuntu下搭建eclipse + ndk编译JNI库
- Ubuntu下搭建 eclipse+NDK for android JNI 的开发环境
- Ubuntu下搭建 Eclipse+NDK for Android JNI 的开发环境
- Ubuntu下搭建 eclipse+NDK for android JNI 的开发环境
- Eclipse NDK+JNI环境搭建
- Eclipse环境下通过Cygwin使用NDK编译jni程序
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库(1)
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库(2)
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库(1)
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库(2)
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库(1)
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库一
- Windows7 下Eclipse集成Cygwin配置Android NDK环境编译JNI库二
- Ubuntu 下用 Eclipse 编译调试 Android NDK 工程
- Ubuntu 下用 Eclipse 编译调试 Android NDK 工程
- Ubuntu 下用 Eclipse 编译调试 Android NDK 工程
- Android NDK&&JNI 编译环境的搭建
- UDP、TCP打洞简明流程
- 来自百度,为什么要重构(Refactoring)
- TOMCAT+花生壳完美配置OK!
- android安全:forceStopPackage对android的Alarm的影响
- ACM 算法知识点分布及题型
- ubuntu下搭建eclipse + ndk编译JNI库
- linux下基本常常用到的shell命令(部分)
- vnc2flv(或者vnc2swf)的原理分析
- 2013年7月2号
- Linux 查看CPU信息、机器型号等硬件信息
- 2013年6月1日工作日志
- 简单使用videoview播放视频
- SQL Server系统表说明
- 在一个TabHost内实现多个Activity的跳转