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)

參考了一些網上的內容,記不住網址了,在此表示感謝!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章