Jni 参数传递与操作——(C/C++ 代码与 java 代码的互相调用)

JNI中,C函数名的java对象参数,除了String类外则都表示为jobject类型(String类表示为jstring类型).
JNI提供了在本地代码中操作Java对象的功能。
基本原理
首先需要找到对象属于哪个类(class).类(class)在JNI中用jclass进行表示。
查找java类有两种方式
一、用类名 (如android.os.Binder) FindClass() 函数中查找得到jclass对象。
比如: jclass clazz = env->FindClass(kBinderPathName) ;
这里的kBinderPathName为"android.os.Binder",所以上面等同于jclass clazz= env->FindClass("android.os.Binder");
二、 可以通过jclass  GetObjectClass  (jobject obj0)方法得到一个jclass,以表示obj0对象属于哪个类.
比如: jclass clazz=env->GetObjectClass (obj);
然后对Field和函数再分别按下面的两种方式进行处理。
成员变量:通过GetFieldID/GetStaticFieldID得Field的id(以 jfieldID形式表示 ),然后调用GetXXXField/GetStaticXXXField函数就可以到Field的值,
调用SetXXXField/SetStaticXXXField就可以设置Field的值。
函数:和操作Field类似,通过GetMethodID/GetStaticMethodID得到函数的id(以jmethodID形式表示),然后就可以通过CallXXXMethod/CallStaticXXXMethod进行函数调用了.
GetFieldID/GetStaticFieldID简介
jfieldID GetFieldID (jclass cl0, const char * val1, const char * val2)
jfieldID GetStaticFieldID (jclass cl0, const char * val1, const char * val2)
前者用于非静态Field,后者用于静态Field
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,const char * val对应函数的名字
第三个参数,const char * val2用于表示Field是什么类型.
关于java参数类型的符号化表示请参考 JNI中java类型的符号化表示
GetXXXField/GetStaticXXXField简介
GetXXXField/GetStaticXXXField中的XXX表示对什么类型的Field进行操作。 前者用于非静态Field,后者 用于静态Field。
该系列函数包括
非静态
  jobject GetObjectField (jobject obj0, jfieldID fld1)
  jboolean GetBooleanField (jobject obj0, jfieldID fld1)
  jbyte GetByteField (jobject obj0, jfieldID fld1)
  jchar GetCharField (jobject obj0, jfieldID fld1)
  jshort GetShortField (jobject obj0, jfieldID fld1)
  jint GetIntField (jobject obj0, jfieldID fld1)
  jlong GetLongField (jobject obj0, jfieldID fld1)
  jfloat GetFloatField (jobject obj0, jfieldID fld1)
  jdouble GetDoubleField (jobject obj0, jfieldID fld1)
静态
 jobject GetStaticObjectField (jclass cl0, jfieldID fld1)
 jboolean GetStaticBooleanField (jclass cl0, jfieldID fld1)
 jbyte GetStaticByteField (jclass cl0, jfieldID fld1)
 jchar GetStaticCharField (jclass cl0, jfieldID fld1)
 jshort GetStaticShortField (jclass cl0, jfieldID fld1)
 jint GetStaticIntField (jclass cl0, jfieldID fld1)
 jlong GetStaticLongField (jclass cl0, jfieldID fld1)
 jfloat GetStaticFloatField (jclass cl0, jfieldID fld1)
 jdouble GetStaticDoubleField (jclass cl0, jfieldID fld1)
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,表示GetFieldID/GetStaticFieldID中得到的Field的id
SetXXXField/SetStaticXXXField简介
SetXXXField/SetStaticXXXField中的XXX表示对什么类型的Field进行操作。 前者用于非静态Field,后者 用于静态Field。
该系列函数包括
非静态
  void SetObjectField (jobject obj0, jfieldID fld1, jobject obj2)
  void SetBooleanField (jobject obj0, jfieldID fld1, jboolean val2)
  void SetByteField (jobject obj0, jfieldID fld1, jbyte val2)
  void SetCharField (jobject obj0, jfieldID fld1, jchar val2)
  void SetShortField (jobject obj0, jfieldID fld1, jshort val2)
  void SetIntField (jobject obj0, jfieldID fld1, jint val2)
  void SetLongField (jobject obj0, jfieldID fld1, jlong val2)
  void SetFloatField (jobject obj0, jfieldID fld1, jfloat val2)
  void SetDoubleField (jobject obj0, jfieldID fld1, jdouble val2)
静态
  void SetStaticObjectField (jclass cl0, jfieldID fld1, jobject obj2)
  void SetStaticBooleanField (jclass cl0, jfieldID fld1, jboolean val2)
  void SetStaticByteField (jclass cl0, jfieldID fld1, jbyte val2)
  void SetStaticCharField (jclass cl0, jfieldID fld1, jchar val2)
  void SetStaticShortField (jclass cl0, jfieldID fld1, jshort val2)
  void SetStaticIntField (jclass cl0, jfieldID fld1, jint val2)
  void SetStaticLongField (jclass cl0, jfieldID fld1, jlong val2)
  void SetStaticFloatField (jclass cl0, jfieldID fld1, jfloat val2)
  void SetStaticDoubleField (jclass cl0, jfieldID fld1, jdouble val2)
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,表示GetFieldID/GetStaticFieldID中得到的Field的id
第三个参数,表示新值
GetMethodID/GetStaticMethodID简介
jmethodID GetMethodID (jclass cl0, const char * val1, const char * val2)
jmethodID GetStaticMethodID (jclass cl0, const char * val1, const char * val2)
调用他们能得到函数的id(以jmethodID形式表示).前者用于非静态Field,后者用于静态Field
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,const char * val对应函数的名字
第三个参数,const char * val2用于表示函数的传入参数都有哪些,都是些什么类型,返回参数是什么类型。因为函数可以重载,所以必须要该参数才能定为函数。这里是以符号的形式表示传入参数的类型。
关于java参数类型的符号化表示请参考 JNI中java类型的符号化表示
CallXXXMethod/CallStaticXXXMethod
CallXXXMethod/CallStaticXXXMethod中 的XXX表示对什么返回类型的Field函数进行调用。 前者用于非静态函数的调用,后者 用于静态函数的调用。
该系列函数包括
非静态
  jobject CallObjectMethod (jobject obj0, jmethodID meth1, ...)
  jboolean CallBooleanMethod (jobject obj0, jmethodID meth1, ...)
  jbyte CallByteMethod (jobject obj0, jmethodID meth1, ...)
  jchar CallCharMethod (jobject obj0, jmethodID meth1, ...)
  jshort CallShortMethod (jobject obj0, jmethodID meth1, ...)
  jint CallIntMethod (jobject obj0, jmethodID meth1, ...)
  jlong CallLongMethod (jobject obj0, jmethodID meth1, ...)
  jfloat CallFloatMethod (jobject obj0, jmethodID meth1, ...)
  jdouble CallDoubleMethod (jobject obj0, jmethodID meth1, ...)
  void CallVoidMethod (jobject obj0, jmethodID meth1, ...)
静态
  jobject CallStaticObjectMethod (jclass cl0, jmethodID meth1, ...)
  jboolean CallStaticBooleanMethod (jclass cl0, jmethodID meth1, ...)
  jbyte CallStaticByteMethod (jclass cl0, jmethodID meth1, ...)
  jchar CallStaticCharMethod (jclass cl0, jmethodID meth1, ...)
  jshort CallStaticShortMethod (jclass cl0, jmethodID meth1, ...)
  jint CallStaticIntMethod (jclass cl0, jmethodID meth1, ...)
  jlong CallStaticLongMethod (jclass cl0, jmethodID meth1, ...)
  jfloat CallStaticFloatMethod (jclass cl0, jmethodID meth1, ...)
  jdouble CallStaticDoubleMethod (jclass cl0, jmethodID meth1, ...)
  void CallStaticVoidMethod (jclass cl0, jmethodID meth1, ...)
第一个参数,jobject obj0表示调用哪个对象的非静态函数;jcalss cl0表示调用哪个类的静态函数。
第二个参数,jmethodID meth1表示调用函数的id(以jmethodID形式进行表示)
第三个及以后的参数,他们是调用的java函数的传入参数
如何返回一个新的JAVA对象呢?
通过NewObject函数创建一个java对象,然后像操作一般的对象一样操作它,最后返回该对象就可以了。
jobject NewObject (jclass cl0, jmethodID meth1, ...)
第一个参数,jclass cl0表示创建哪个类的实例。
第二个参数,jmethodID meth1表示用哪个构造函数创建该类的实例。
可用如下的方法得到构造函数的Id:
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
具体实例:
MyCallbackActivity。java:
package com.demo.jniparamstrans;

 
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
 
 
 public class MyCallbackActivity extends Activity 
 {
     private Button intButton = null;
     private Button stringButton = null;
     private Button arrayButton = null;
     private TextView intTextView = null; 
     private TextView stringTextView = null; 
     private TextView arrayTextView = null; 
     
     private Handler mHandler = null;
     
     
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) 
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         
         intButton = (Button)this.findViewById(R.id.intbutton);
         //注册按钮监听
         intButton.setOnClickListener(new ClickListener());
         stringButton = (Button)this.findViewById(R.id.stringbutton);
         //注册按钮监听
         stringButton.setOnClickListener(new ClickListener());
         arrayButton = (Button)this.findViewById(R.id.arraybutton);
         //注册按钮监听
         arrayButton.setOnClickListener(new ClickListener());
         
         intTextView = (TextView)this.findViewById(R.id.inttextview);
         stringTextView = (TextView)this.findViewById(R.id.stringtextview);
         arrayTextView = (TextView)this.findViewById(R.id.arraytextview);
         
         //消息处理      
         mHandler = new Handler()
         {
             @Override
             public void handleMessage(Message msg)
             {
                 switch(msg.what)
                 {
                     //整型
                     case 0:
                     {
                         intTextView.setText(msg.obj.toString());
                         break;
                     }
                     //字符串
                     case 1:
                     {
                         stringTextView.setText(msg.obj.toString());
                         break;
                     }
                     //数组
                     case 2:
                     {   byte[] b = (byte[])msg.obj;                  
                         arrayTextView.setText(Byte.toString(b[0])+Byte.toString(b[1])+Byte.toString(b[2])+Byte.toString(b[3])+Byte.toString(b[4]));                     
                         break;
                     }
                 }
                                
             }       
             
         };
         
         
     }
             
     //按钮监听实现
     public class ClickListener implements View.OnClickListener
     {
 
         @Override
         public void onClick(View v) 
         {
             // TODO Auto-generated method stub
             switch(v.getId())
             {
                 case R.id.intbutton:
                 {
                     //调用JNI中的函数
                     callJNIInt(1);      
                     break;
                 }
                 case R.id.stringbutton:
                 {
                     //调用JNI中的函数
                     callJNIString("你好A");             
                     break;
                 }
                 case R.id.arraybutton:
                 {                
                     //调用JNI中的函数
                     callJNIByte(new byte[]{1,2,3,4,5});               
                     break;
                 }
             }
         }
         
     }
   
     
     //被JNI调用,参数由JNI传入
     private void callbackInt(int i)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 0;
         //消息内容
         msg.obj = i;
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //被JNI调用,参数由JNI传入
     private void callbackString(String s)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 1;
         //消息内容
         msg.obj = s;
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //被JNI调用,参数由JNI传入
     private void callbackByte(byte[] b)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 2;
         //消息内容
         msg.obj = b;     
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //本地方法,由java调用
     private native void callJNIInt(int i);
     private native void callJNIString(String s);
     private native void callJNIByte(byte[] b);
     
     static
     {
         //加载本地库
         System.loadLibrary("myjni");
     }
     
 }

main.xml:
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:orientation="vertical" >
 
     <Button 
         android:id="@+id/intbutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个整数1"
         /> 
     
     <TextView
         android:id="@+id/inttextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的整数:" 
         />
     
     <Button 
         android:id="@+id/stringbutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个字符A"
         /> 
     
     <TextView
         android:id="@+id/stringtextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的字符:" 
         />
     
     <Button 
         android:id="@+id/arraybutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个数组12345"
         /> 
     
     <TextView
         android:id="@+id/arraytextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的数组:" 
         />
     
 
 </LinearLayout>

callback.c:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <jni.h>
#include <android/log.h>

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))

/**********传输整数*************

 */
JNIEXPORT void Java_com_demo_jniparamstrans_MyCallbackActivity_callJNIInt(
		JNIEnv* env, jobject obj, jint i) {
	LOGI("Int Begin");

	//找到java中的类
	jclass cls = (*env)->FindClass(env,
			"com/demo/jniparamstrans/MyCallbackActivity");
	//再找类中的方法
	jmethodID mid = (*env)->GetMethodID(env, cls, "callbackInt", "(I)V");
	if (mid == NULL) {
		LOGI("int error");
		return;
	}
	//打印接收到的数据
	LOGI("from java int: %d", i);
	//回调java中的方法
	(*env)->CallVoidMethod(env, obj, mid, i);

}

/********传输字符串*************
 */
JNIEXPORT void JNICALL Java_com_demo_jniparamstrans_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env, "com/demo/jniparamstrans/MyCallbackActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");
     if (mid == NULL)
     {
         LOGI("string error");
return;
     }
     const char *ch;
     //获取由java传过来的字符串
     //GetStringUTFChars将jstring转换成为UTF-8格式的char*
     ch = (*env)->GetStringUTFChars(env, s, NULL);
     //打印
     LOGI("from java string: %s", ch);
//ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
(*env)->ReleaseStringUTFChars(env, s, ch);
//回调java中的方法
(*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));

}

/********传输数组(byte[])*************
 */
JNIEXPORT void JNICALL Java_com_demo_jniparamstrans_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)
{
//找到java中的类
jclass cls = (*env)->FindClass(env, "com/demo/jniparamstrans/MyCallbackActivity");
//再找类中的方法
jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");
if (mid == NULL)
{
	LOGI("byte[] error");
	return;
}

//获取数组长度
jsize length = (*env)->GetArrayLength(env,b);
LOGI("length: %d",length);
//获取接收到的数据
int i;
jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);
//打印
for(i=0;i<length;i++)
{
	LOGI("%d",p[i]);
}

char c[5];
c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5;
//构造数组
jbyteArray carr = (*env)->NewByteArray(env,length);
(*env)->SetByteArrayRegion(env,carr,0,length,c);
//回调java中的方法, CallXXXMethod 表示对 XXX 返回类型的 Field 函数进行调用
(*env)->CallVoidMethod(env, obj, mid ,carr);
}

Android.mk:
LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
 LOCAL_MODULE    := myjni
 LOCAL_SRC_FILES := callback.c
 
 LOCAL_LDLIBS    := -llog
 
 include $(BUILD_SHARED_LIBRARY)

参考了一些网上的内容,记不住网址了,在此表示感谢!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值