android_c++ 高级编程NDK学习笔记二

第一章 用JNI实现与原生代码通信

3.1 什么是jni

3.2 一个简单的示例

示例代码中查看实现步骤:

@@@@@@@@@@A 加载共享库@@@@@@@@@@@

static {

        System.loadLibrary("hello-jni");

    }

@@@@@@@@@@B 声明原生方法 @@@@@@@@@@

public native String  stringFromJNI();

public native String  unimplementedStringFromJNI();

@@@@@@@@@@@@@@@C 调用原生方法@@@@@@@@@@@@@@@@@@@

tv.setText( stringFromJNI() );

 

 

 

hello-jni.c中实现原生方法

jstring

Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,

                                                  jobject thiz )

{

    return (*env)->NewStringUTF(env, "Hello from JNI !");

}

 

c/c++ 头文件生成器javah

D:\workspace4\HelloJni>javah -classpath bin/classes -bootclasspath d:\tools\andr

oid-sdk-windows\platforms\android-10\android.jar -d jni com.example.hellojni.Hel

loJni

命令格介绍如下

D:\workspace4\HelloJni\jni>javah

用法:

  javah [options] 

其中, [options] 包括:

  -o                 输出文件 (只能使用 -d 或 -o 之一)

  -d 

                 输出目录

  -v  -verbose             启用详细输出

  -h  --help  -?           输出此消息

  -version                 输出版本信息

  -jni                     生成 JNI 样式的标头文件 (默认值)

  -force                   始终写入输出文件

  -classpath         从中加载类的路径

  -bootclasspath     从中加载引导类的路径

 是使用其全限定名称指定的

(例如, java.lang.Object)。

 

在jni目录下生成头文件com_example_hellojni_HelloJni.h,内容如下:

#include 

/* Header for class com_example_hellojni_HelloJni */

 

#ifndef _Included_com_example_hellojni_HelloJni

#define _Included_com_example_hellojni_HelloJni

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     com_example_hellojni_HelloJni

 * Method:    stringFromJNI

 * Signature: ()Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI

  (JNIEnv *, jobject);

 

/*

 * Class:     com_example_hellojni_HelloJni

 * Method:    unimplementedStringFromJNI

 * Signature: ()Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI

  (JNIEnv *, jobject);

 

#ifdef __cplusplus

}

#endif

#endif

 

 

@@@@@@@@@@可以在eclipse中生成头文件@@@@@@@@@@@@@

 

Run-àexternal tool -àeternal tools configurations

选中programe,单击新建new launch configuration

选中main选项卡:

Name:Generate C and C++ Header File

Location: ${system_path:javah}

Working directory:${project_loc}/jni

Arguments:-classpath "${project_classpath};${env_var:ANDROID_SDK_HOME}/platforms/android-14/android.jar"${java_type_name}

 

选中refresh选项卡:选中refresh resources upon completionà

the project containing the selected resource

选中common选项卡:选中display in favorites menu-àexternal tools

保存退出

 

测试生成头文件:

选中测试目标类文件-àrun-à external tool -à Generate C and C++ Header File

会在jni目录下生成头文件

 

方法声明

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(

JNIEnv *,  //指向jni函数表的接口指针

 jobject); //hellojni类的java对象引用

 

JNIEnv接口指针

原生代码通过jnienv接口指针提供的各种函数来使用虚拟机的功能

return (*env)->NewStringUTF(env, "Hello from JNI !");

 

实例方法和静态方法

jstring

Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,

                                                  jobject thiz )

实例方法可以通过第二个参数thiz取得(jobject实例引用)

 

jstring

Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,

                                                  jclass clazz )

 

 静态方法由于没有和实例绑定,通过第二个参数取得类引用jclass

 

 

3.3 数据类型

Java基本数据类型

Java类型

Jni类型

c/c++类型

大小

Boolean

Jbollean

Unsigned char

无符号8位

Byte

Jbyte

Char

有符号8位

Char

Jchar

Unsigned short

无符号16位

Short

Jshort

Short

有符号16位

Int 

Jint

Int

有符号32位

Long

Jlong

Long long

有符号64位

Float

Jfloat

Float

32位

double

jdouble

double

64位

 

Java引用类型映射

Java类型

原生类型

Java.lang.class

Jclass

Java.lang.throwable

Jthrowable

Java.lang.string

Jstring

Other objects 

Jobjects

Java.lang.object[]

JobjectsArray

Boolean[]

jbooleanArray

Byte[]

jbyteArray

Char[]

jcharArray

Short[]

jshortArray

Int[]

jintArray

Long[]

jlongArray

Float[]

jfloatArray

Double[]

jdoubleArray

Other Arrays

jarray

 

 

3.4 对引用数据类型的操作

 3.4.1对字符串操作

   创建字符串

   示例代码如下:

Jstring javaString;

javaString=(*env) ->NewStringUTF(env,hello world!);

   把java字符串转换为c字符串

     GetStringChars 将unicode格式的java字符串转换为c 字符串

     GetStringUTFChars 将utf-8格式的字符串转换为c字符串 

     注:以上两个函数的三个参数用来确定返回字串是指向副本还是栈中固定对象

     示例代码如下:

     Const jbyte* str;

     Jboolean isCopy;

     Str=(*env)->GetStringUTFChar(env,jstring,&isCopy);

     If(0!=str){

      Printf(java string is : %s,str);

         If(JNI_TRUE==isCopy){

     Printf(c string is copy of the java string!);

}else{

     Printf(c string points to actual string!);

}

}

 

释放字符串

   ReleaseStringChar用来释放unicode格式的字符串

   ReleaseStringUTFChar来释放utf-8格式的字符串

    示例代码如下:

     (*env)->ReleaseStringChar(env,javaString,sstr);

 

 3.4.2 数组操作

 

  创建数组 NewArray

    示例代码如下:

     jintArraay javaArray;

     javaArray=(*env)->NewIntArray(ent,10);

     if(0!=javaArray){

      //数组可以使用

}

  访问数组

   将数组复制成c数组

   让jni提供指向数组的指针

  对副本的操作 

    GetArrayRegion

    SetArrayRegion

    //将java数组复制到c数组中

     Jint nativeArray[10];

     (*env)->GetIntArrayRegion(env,javaArray,0,10,nativeArray);

    //从c数组向java数组提交所作 的修改

     (*env)->SetIntArrayRegion(env,javaArray,0,10,nativeArray);

  对直接指针的操作

    GetArrayElements

    ReleaseArrayElements

    //取得指向java数组元素的直接指针

    jint* nativeDirectArray;

    jboolean isCopy;

    nativeDirectArray=(*env)->GetIntArrayElements(env,javaArray,&isCopy); 

    //释放指向java数组的直接指针

(*env)->ReleaseIntArrayElements(env,javaArray,nativeDirectArray,0);

 注:第四个参数为释放模式

     0              将内容复制回来并释放原生数组

     JNI_COMMIT    将内容复制回来但不释放原生数组,一般用于更新数组

     JNI_ABORT     不将内容复制回来,释放原生数组

 

 3.4.3 NIO操作

   直接创建字节缓冲 NewDirectByteBuffer

    示例代码如下:

    //给定的字节数组创建缓冲区

     Unsigned char* buffer=(unsigned char*) malloc(1024);

     Jobject directBuffer;

     directBuffer=(*env)->NewDirectByteBuffer(env,buffer,1024);

     注:原生方法中分配的内存需要手动释放

 

   直接字节缓冲区获取 GetDirectBufferAddress

    //通过java字节缓冲取原生字节数组

    Unsigned char* buffer;

    Buffer=(unsigned char*)(*env)->GetDirectBufferAddress(env,directBuffer);

  3.4.4 访问域

      Java有两类域 :实例域和静态域

      @@@@@@@@@@获取域ID@@@@@@@@@@@

      示例代码如下:

      Public class JavaClass{

         /*实例域*/

        Private String instanceField=instance field;

         /*静态域*/

Private static String staticField=static field;

}

//用对象引用取得类

Jclass clazz;

Clazz=(*env)->GetObjectClass(env,instance);

//获取实例域的域ID

JfieldID instanceFieldID;

instanceFieldID=(*env)->GetFielID(env,clazz,instanceField,Ljava/lang/String;);

//获取静态域的域ID

jfieldID staticFielID;

staticFieldID=(*env)->GetStaticFieldID(env,clazz,staticField,Ljava/lang/String;);

@@@@@@@@@@@@@获取域@@@@@@@@@@@@@@@@

GetField

      //获取实例域

      Jstring instanceField;

      instanceField=(*env)->GetObjectField(env,instance,instanceFieldID);

      //获取动态域

      Jstring staticField;

       staticField=(*env)->GetStaticObjectField(env,clazz,staticFieldID);

 

  3.4.5 调用方法

   Java有两类方法:实例方法和静态方法

   示例代码:

   Public class JavaClass{

    /*实例方法*/

    Private String instanceMethod(){

      Return instance Method;

}

    /*静态方法*/

  Private static String staticMethod(){

     Return static Method;

}

}

   @@@@@@@@@@@@@@@@获取方法ID@@@@@@@@@@@@@@@@

   //获取实例方法ID

jmethodID instanceMethodID;

instanceMethodID=(*env)->GetMethodID(env,clazz,instanceMethod,()Ljava/lang/String;);

   //获取静态方法ID

jmethodID staticMethodID;

staticMethodID=(*env)->GetStaticMethodID(env,clazz,staticMethod””()Ljava/lang/String;);

   @@@@@@@@@@@@@@@@调用方法@@@@@@@@@@@@@@@@@@

   CallMethod

   //调用实例方法

   Jstring instanceMethodResult;

   instanceMethodResult=(*env)->CallStringMethod(env,clazz,instanceMethodID);

   //调用静态方法

   Jstring staticMethodResult;

   staticMethodResult=(*env)->CallStaticStringMethod(env,clazz,staticMethodID);

 

  3.4.6域和方法描述符

Java类型签名映射

Java类型

签名

Boolean

Z

Byte

B

Char

C

Short

S

Int

I

Long 

J

Float

F

Double

D

Fully-qualified-class

Lfully-qualified-class

Type[]

[type

Method type

(arg-type)ret-type

 

Java类文件反汇编程序 javap

 

@@@@@@@@@@@@@在命令行方式下运行@@@@@@@@@@@@@@@@@@@@

D:\workspace4\HelloJni>javap -classpath bin/classes -p -s com.example.hellojni.H

elloJni

Compiled from "HelloJni.java"

public class com.example.hellojni.HelloJni extends android.app.Activity {

  static {};

    Signature: ()V

 

  public com.example.hellojni.HelloJni();

    Signature: ()V

 

  public void onCreate(android.os.Bundle);

    Signature: (Landroid/os/Bundle;)V

 

  public native java.lang.String stringFromJNI();

    Signature: ()Ljava/lang/String;

 

  public native java.lang.String unimplementedStringFromJNI();

    Signature: ()Ljava/lang/String;

}

@@@@@@@@@@@@@@@@在eclipse下运行@@@@@@@@@@@@@@@@@@@@@

Run-àexternal tool -àeternal tools configurations

选中programe,单击新建new launch configuration

选中main选项卡:

Name: Java Class File Disassembler

Location: ${system_path:javap}

Working directory:${project_loc}

Arguments:-classpath "${project_classpath};${env_var:ANDROID_SDK_HOME}/platforms/android-14/android.jar" ${java_type_name}

选中common选项卡:选中display in favorites menu-àexternal tools

保存退出

测试

Compiled from "HelloJni.java"

public class com.example.hellojni.HelloJni extends android.app.Activity {

  static {};

    Signature: ()V

 

  public com.example.hellojni.HelloJni();

    Signature: ()V

 

  public void onCreate(android.os.Bundle);

    Signature: (Landroid/os/Bundle;)V

 

  public native java.lang.String stringFromJNI();

    Signature: ()Ljava/lang/String;

 

  public native java.lang.String unimplementedStringFromJNI();

    Signature: ()Ljava/lang/String;

}

 

3.5 异常处理

 Jni中异常发生后要显示的实现异常处理

 @@@@@@@@@@@@@捕获异常@@@@@@@@@@@@@@

    //抛出异常的java例子

   Public class JavaClass{

    /*抛出方法*/

    Private void throwingMethod() throws NullPointerException{

     Throw new NullPointerException(null pointer);

}

    /*声明原生方法*/

    Private native void accessMethods();

}

   //原生代码中的异常处理

   Jthrowable ex;

   

   (*env)->CallVoidMethod(env,instance,throwingMethodId);

   Ex=(*env)->ExceptionOccurred(env);

If(0!=ex){

  (*env)->ExceptionClear(env);

  /*exception handler*/

}

 

 

 @@@@@@@@@@@@@抛出异常@@@@@@@@@@@@@@@

//原生代码中抛出异常 

Jclass clazz;

//找到异常类

Clazz=(*env)->FindClass(env,java/lang/NullPointerException);

If(0!=clazz){

    //在抛出异常时应释放所有已分配的原生资源

   (*env)->ThrowNew(env,clazz,exception message!);

}

 

 

 

 

 

 

3.6 局部和全局引用

  3.6.1 局部引用

   在原生函数反回后会自动释放,也可以用deleteLocalRef手动释放

   示例代码:

   //删除一个引用

   Jclass clazz;

   Clazz=(*env)->FindClass(env,java/lang/string);

   

   (*env)->DeleteLocalRef(env,clazz);

 

  ensureLocalCapacity请求局部引用槽

  3.6.2 全局引用

   @@@@@@@@@@@@@@创建全局引用@@@@@@@@@@@@@@@@@

   //用给定的局部引用创建全局引用

   Jclass  localClazz;

   Jclass globalClazz;

   localClazz=(*env)->FindClass(env,java/lang/String);

   globalClazz=(*env)->NewGlobalRef(env,localClazz);

   

   (*env)->DeleteLocalRef(env,localClazz);

   @@@@@@@@@@@@@@删除全局引用@@@@@@@@@@@@@@@@@

   (*env)->DeleteGlobalRef(env,globalClazz);

 

  3.6.3 弱全局引用

    @@@@@@@@@@@@@创建弱全局引用@@@@@@@@@@@@@@@@@@@

    //用给定的局部引用创建弱全局引用

     Jclass weakGlobalClazz;

     weakGlobalClazz=(*env)->NewGlobalRef(env,localClazz);

    @@@@@@@@@@@@@@弱全局引用的有效性检验@@@@@@@@@@@@@@@@

     //检验弱全局变量是否仍然有效

     If(JNI_FALSE==(*env)->IsSameObject(env,weakGlobalClazz,NULL)){

   /*对象仍处于活动状态可以使用*/

}else{

  /*对象被垃圾回收器回收,不能使用*/

}

@@@@@@@@@@@@@@@删除弱全局引用@@@@@@@@@@@@@@@@@

    //删除弱全局引用

    (*env)->DeleteWeakGlobalRef(env,weakGlobalClazz);

3.7 线程

 

  局部引用不能在线程间共享,全局引用可以在线程间共享

    @@@@@@@@@@@@@@同步 @@@@@@@@@@@@@@

     //java同步代码块

    Synchronized(obj){

     /*同步线程安全代码块*/

}

//java同步代码块的原生等价

If(JNI_OK==(*env)->MoniterEnter(env,obj)){

  /*错误处理*/

}

/*同步线程安全代码块*/

If(JNI_OK==(*env)->MoniterExit(env,obj)){

  /*错误处理*/

}

   @@@@@@@@@@ 原生线程@@@@@@@@@@@@

   //将当前线程和虚拟机附着和分离

   JavaVM* cachedJvm;

   JNIEnv* env;

   ..

   /*将当前线程附着到虚拟机*/

   (*cachedJvm)->AttachCurrentThread(cachedJvm,&env,NULL);

   

   /*将当前线程与虚拟机分离*/

   (*cachedJvm)->DetachCurrentThread(cachedJvm);

你可能感兴趣的:(android,jni)