JNI demo手把手 && JNINativeMethod的参数解析 && JNI使用中的报错

(1)JNI工程建立

        在Android目录下任意创建一个目录jnidemo,并在该目录下创建三个文件:Android.mk,用于编译JNI工程的makefile文件;jnidemo.cpp,JNI代码文件;onload.cpp,用于注册JNI方法的文件。

mkdir jnidemo
cd jnidemo
touch Android.mk
touch jnidemo.cpp
touch onload.cpp

(2)编辑jnidemo.cpp

#include "JNIHelp.h"
#include "jni.h"
#define LOG_TAG "Service-JNI"

namespace android
{
	static jint nativeOpen(JNIEnv* env,jobject obj){
       		ALOGE("JNI test nativeOpen");
      		return 10;
	}

	static JNINativeMethod method_table[] = {
       		{"nativeOpen","()I",(void*)nativeOpen }
  	};

	int register_android_jnidemo_Service(JNIEnv *env){
      		return jniRegisterNativeMethods(env,"com/example/test/Demo",
              method_table,NELEM(method_table));
	}
};

这里我们提供了一个接口给Java层调用,即nativeOpen()。定义register_android_jnidemo_Service()方法,用于注册JNI文件,在该方法中,用到了两个关键的参数。一个是"com/example/test/Demo",对应着java代码的包名和类名,即调用JNI的java代码所在的包是“com.example.test”,类名是Demo;另一个是method_table,即是上面初始化的JNINativeMethod结构体。
(3)编辑onload.cpp

#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"

namespace android {
	int register_android_jnidemo_Service(JNIEnv* env);
};

using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");

    register_android_jnidemo_Service(env);
    return JNI_VERSION_1_4;
}

当java代码调用System.loadLibrary()加载JNI库的时候,将调用到onload.cpp的JNI_OnLoad()方法,然后将调用在jnidemo.cpp文件中定义的方法register_android_jnidemo_Service(env)对JNI进行注册。其实很简单吧,onload.cpp就是完成注册功能的。

(4)编辑Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:= \
    jnidemo.cpp \
    onload.cpp

LOCAL_SHARED_LIBRARIES := \
     libnativehelper \
     liblog

LOCAL_MODULE:= libjnidemo
include $(BUILD_SHARED_LIBRARY)

LOCAL_SHARED_LIBRARIES 用到的共享库,libnativehelper是用于注册JNI用到的共享库,liblog是用于打印log用到的共享库,即ALOGD()方法需要的。     LOCAL_MODULE 编译输出模块的名称,编译之后将生成libjnidemo.so文件

        在完成以上工作后,在jnidemo目录下执行mm命令,之后将在out产品目录下的system/lib/目录下生成libjnidemo.so文件,将该文件push到目标板的/system/lib目录下。

 (5)制作一个简单的apk,新建一个Demo类,在activity的onCreate函数中完成new Demo实例,就可以了。Demo.java内容:

package com.example.test;

import android.util.Log;

public class Demo {
    String TAG="Demo";
    static {
       System.loadLibrary("jnidemo");
    }

    public native int nativeOpen();	//以native 申明JNI函数
    public Demo(){
       Log.v(TAG,"get from jni = "+nativeOpen());
    }
}

activiy的内容

package com.example.test;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Demo();               
    }
}

运行时就会在eclipse的Logcat中发现如下log:

01-03 00:23:56.693: E/Service-JNI(2709): JNI test nativeOpen
01-03 00:23:56.693: V/Demo(2709): get from jni = 10
第一条是在JNI的nativeOpen方法中打印的,第二条是在Java代码中打印的。看到这个结果,说明JNI 已经ok了。

参考原文:http://blog.sina.com.cn/s/blog_89f592f501013ge3.html

========================================JNINativeMethod的参数解析==============================================

        Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下:
typedef struct {
        const char* name;
        const char* signature;
        void* fnPtr;
} JNINativeMethod;

第一个变量name是Java中函数的名字;第二个变量signature,用字符串是描述了函数的参数和返回值;第三个变量fnPtr是函数指针,指向C函数。其中比较难以理解的是第二个参数,例如
"()V"
"(II)V" 
实际上这些字符是与函数的参数类型一一对应的。"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();"(II)V" 表示 void Func(int, int);

        具体的每一个字符的对应关系如下:
 
字符 Java类型 C类型
V      void            void
Z       jboolean     boolean
I        jint              int
J       jlong            long
D      jdouble       double
F      jfloat            float
B      jbyte            byte
C      jchar           char
S      jshort          short 
数组则以"["开始,用两个字符表示 
[I       jintArray      int[]
[F     jfloatArray    float[]
[B     jbyteArray    byte[]
[C    jcharArray    char[]
[S    jshortArray   short[]
[D    jdoubleArray double[]
[J     jlongArray     long[]
[Z    jbooleanArray boolean[]

上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。

Ljava/lang/String; jstring 

========================================JNI使用中的报错==============================================

(1)在使用jni时,.c文件和.cpp文件中jni函数的使用语法是不同的。否则编译会报错,详见:http://blog.csdn.net/forandever/article/details/50396058

(2)在使用jstring 类型时,要注意ReleaseStringUTFChars的使用。详见:https://segmentfault.com/a/1190000005859213


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值