因为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();
@Override
protected 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
}
});
}
@Override
public 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 __cplusplus
extern "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,用完后接的delete
jstring 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 1
JNIEXPORT 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 __cplusplus
extern "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 port
W/dalvikvm( 3035): JNI WARNING: expected return type 'I'
W/dalvikvm( 3035): calling Lcom/gpse/abc/CardReader;.update_fragment ()V
W/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)->GetFieldID
5.
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 method
E/dalvikvm( 8440): Requested: Lcom/gpse/abc/CardReader;.nativeopen:(Ljava/lang/String;II)Ljava/io/FileDescriptor
E/serial_port( 8440): 22222
W/dalvikvm( 8440): JNI_OnLoad returned bad version (-1) in /data/data/com.gpse.abc/lib/libcardreader.so 0x41016378
W/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 }