ubuntu下搭建eclipse + ndk编译JNI库

因为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 }





















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
1.) 在项目根目录下创建jni目录,下面是要放进去的第一个文件Android.mk LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := main_jni #LOCAL_CFLAGS := LOCAL_SRC_FILES := main.cpp #LOCAL_LDLIBS := include $(BUILD_SHARED_LIBRARY) 2.) 在jni目录创建main.cpp #include using namespace std; #ifdef __cplusplus extern "C" { #endif class Test { public: Test(){}; ~Test(){}; int SomeFunc() { return 20140522; } }; jint Java_com_yxiaolv_testjni_MainActivity_SomeFunc(JNIEnv *env, jobject thiz) //红色部分必须和你java类包名一致 { Test *test = new Test(); return test->SomeFunc(); } #ifdef __cplusplus } #endif //将下面第3.)至第5.)步的代码片段放到eclipse创建android项目时自动产生的MainAcitivity.java中就可以了 3.) 在Java Activity代码中添加对这个jni的调用 (本例中是MainActivity.java ): static { System.loadLibrary("main_jni"); } 4.) 在调用者Activity中定义函数 native int SomeFunc(); 5.) 在activity中调用 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = new TextView(this); int i = SomeFunc(); tv.setText( String.valueOf(i) ); setContentView(tv); } //下面的第6.)和第7.)步可以被集成的.cproject 和在.project中添加plugin之后一键编译,不用单独起dos窗口了。由于没有找到生成.cproject的工具,只能手写,所以本文不使用该方法,以后找到了好的方法再补充。委屈 6.) 在dos窗口中,cd到项目路径下,运行ndk-build 命令,该命令会自动搜索该路径子目录下的native项目,进行编译。 (Note: 编译完成后不要忘记了刷新项目)。 7.) 重新编译和部署项目,就可以看到已经包含了.so的apk运行在目标机上了。 摘自 http://stackoverflow.com/questions/13654678/how-compile-c-project-via-android-ndk 1). 如果不知道怎样安装和使用windows版的NDK,可以参考 http://blog.csdn.net/do_script/article/details/26478583

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值