1. 程式人生 > >一個例子掌握JNI開發

一個例子掌握JNI開發

繼續上一篇博文eclipse搭建JNI開發環境,現在我們從程式碼角度分析,C和Java混合程式設計時能實現的功能。

使用javah命令,編譯生成.h標頭檔案時,每個函式,至少都會有兩個引數。JNIEnv 和jclass/jobject。其中,當native方法是靜態方法(類方法)時,第二個引數是jclass,當native方法是成員方法時,第二個引數是jobject。其餘的引數,會根據你在java檔案中宣告的方法引數型別,生成具體的簽名。jni中型別在jni標頭檔案中定義規則如下:

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;
對應簽名:
java型別 jni型別 型別簽名
char jchar C
int jint I
long jlong J
float jfloat F
double jdouble D
boolean jboolean Z
byte jbyte B
short jshort S
void V
L全限定名;,比如String, 其簽名為Ljava/lang/util/String;
陣列 [型別簽名, 比如 [B

Jni.java 檔案中,對應7個native方法。

1.呼叫C語言的printf函式,輸出固定內容。

publicstaticnativevoid print();

2.轉入指定字串,用printf函式輸出。

publicstaticnativevoid print(String str);

3.用C語言實現拼接字串的功能,並返回給java。

public static native String append(String str);

4.傳入字串,作為Test類建構函式的函式,C語言呼叫Java類的建構函式,生成jobject,操縱Test類的所有方法和屬性。

public native void test(String test);

5.傳入Test類的物件,操縱操縱Test類的所有方法和屬性。

public native void test(Test test);

6.將傳入的位元組陣列轉16進位制字串返回。

public native String toHex(byte[] test);

7.將傳入的字串轉成16進位制位元組陣列返回。

public native byte[] toBytes(String test);

完整示例程式碼如下:

com_flueky_jni_Jni.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_flueky_jni_Jni */

#ifndef _Included_com_flueky_jni_Jni
#define _Included_com_flueky_jni_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_flueky_jni_Jni
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__
  (JNIEnv *, jclass);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    print
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2
  (JNIEnv *, jclass, jstring);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    append
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append
  (JNIEnv *, jclass, jstring);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    test
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    test
 * Signature: (Lcom/flueky/jni/Test;)V
 */
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2
  (JNIEnv *, jobject, jobject);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    toHex
 * Signature: ([B)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex
  (JNIEnv *, jobject, jbyteArray);

/*
 * Class:     com_flueky_jni_Jni
 * Method:    toBytes
 * Signature: (Ljava/lang/String;)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
main.cpp
/*
 * main.cpp
 *
 *  Created on: 2016年3月22日
 *      Author: flueky
 */

#include <stdio.h>
#include "com_flueky_jni_Jni.h"
#include <jni.h>
#include <stdlib.h>
#include <string.h>
/**
 * 操作test類的物件
 */
void operate_test(JNIEnv *env, jobject obj) {
	//根據物件,獲取到jclass
	jclass test_cls = env->GetObjectClass(obj);
	//獲取成員方法id
	jmethodID get_mid = env->GetMethodID(test_cls, "getTest",
			"()Ljava/lang/String;");
	//回撥成員方法
	jstring test = (jstring) env->CallObjectMethod(obj, get_mid);
	jsize len = env->GetStringUTFLength(test);
	const char* str = env->GetStringUTFChars(test, JNI_FALSE);
	char* result = (char*) malloc(sizeof(char) * (len + 1));
	strcpy(result, str);
	//標誌結束
	*(result + len) = 0;
	printf("getTest 輸出:%s\n", result);
	//獲取append方法id,呼叫append方法
	jmethodID append_mid = env->GetMethodID(test_cls, "append",
			"(Ljava/lang/String;)V");
	env->CallVoidMethod(obj, append_mid, env->NewStringUTF("append test"));
	printf("append: append test\n");
	//獲取成員變數id,類變數id GetStaticFieldID
	jfieldID test_fid = env->GetFieldID(test_cls, "test", "Ljava/lang/String;");
	//獲取成員變數值
	test = (jstring) env->GetObjectField(obj, test_fid);
	len = env->GetStringUTFLength(test);
	str = env->GetStringUTFChars(test, JNI_FALSE);
	result = (char*) malloc(sizeof(char) * (len + 1));
	strcpy(result, str);
	//標誌結束
	*(result + len) = 0;
	printf("append 結果:%s\n", result);

	//獲取靜態方法id
	jmethodID print_mid = env->GetStaticMethodID(test_cls, "print",
			"(ICFZLjava/lang/String;)V");
	//呼叫靜態方法
	env->CallStaticVoidMethod(test_cls, print_mid, 1, 'c', 1.2f, true, test);

	//刪除obj物件
	env->DeleteLocalRef(obj);
	env->DeleteLocalRef(test);

}

JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__(JNIEnv *env,
		jclass cls) {
	printf("小飛哥0217\n");
}

JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2(
		JNIEnv *env, jclass cls, jstring jstr) {

	jsize len = env->GetStringUTFLength(jstr);
	const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);
	char* result = (char*) malloc(sizeof(char) * (len + 1));
	strcpy(result, str);
	//標誌結束
	*(result + len) = 0;
	printf("本地輸出:%s", result);
}

JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append(JNIEnv *env, jclass,
		jstring jstr) {
	//獲取jstring 的長度
	jsize len = env->GetStringUTFLength(jstr);
	//jstring 轉字串陣列
	const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);
	//分配結果字串空間
	char* result = (char*) malloc(sizeof(char) * (len + 7 + 1));
	//字串函式處理
	strcpy(result, "append ");
	strcpy(result + 7, str);
	//標誌結束
	*(result + 7 + len) = 0;
	return env->NewStringUTF(result);
}

/**
 * 操作test類
 */
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2(
		JNIEnv *env, jobject obj, jstring jstr) {
	//Test類
	jclass test_cls = env->FindClass("com/flueky/jni/Test");
	//Test類的構造方法id,構造方法名固定<init>,返回型別void
	jmethodID init_mid = env->GetMethodID(test_cls, "<init>",
			"(Ljava/lang/String;)V");
	//建立Test物件
	jobject test_obj = env->NewObject(test_cls, init_mid, jstr);

	operate_test(env, test_obj);

}
JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2(
		JNIEnv *env, jobject obj, jobject test_obj) {
	operate_test(env, test_obj);
}

JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex(JNIEnv *env,
		jobject jobj, jbyteArray jbytes) {
	//獲取字串長度
	jsize len = env->GetArrayLength(jbytes);
	//分配結果的記憶體
	char *result = (char *) malloc(sizeof(char) * (len * 2 + 1));
	//分配快取的記憶體
	jbyte* temp = (jbyte *) malloc(sizeof(jbyte) * len);
	//從位元組陣列中取字元
	env->GetByteArrayRegion(jbytes, 0, len, temp);
	//轉16進位制
	for (int i = 0; i < len; i++) {
		*(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';
		*(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';
	}
	//釋放快取的記憶體
	free(temp);
	*(result + len * 2) = 0;
	//生成jstring
	jstring str = env->NewStringUTF(result);
	free(result);
	return str;
}

JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes(JNIEnv *env,
		jobject jobj, jstring jstr) {
	//獲取字串長度
	jsize len = env->GetStringUTFLength(jstr);
	//分配位元組陣列空間
	jbyteArray jbytes = env->NewByteArray(len * 2);
	//將jstring轉成字元陣列
	const jchar * temp = env->GetStringChars(jstr, JNI_FALSE);
	//分配結果的記憶體
	char *result = (char *) malloc(sizeof(char) * (len * 2));
	//轉16進位制
	for (int i = 0; i < len; i++) {
		*(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';
		*(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';
	}
	//將字元存到位元組數組裡
	env->SetByteArrayRegion(jbytes, 0, len * 2, (const jbyte *) result);
	free(result);
	return jbytes;
}
Jni.java
package com.flueky.jni;

public class Jni {

	static {
		System.loadLibrary("JNI_CPP");
	}

	/**
	 * 本地方法,用C語言實現
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午4:23:00
	 */
	public static native void print();

	/**
	 * 本地方法,用C語言實現
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:10:43
	 * @param str
	 */
	public static native void print(String str);

	/**
	 * 拼接字元傳並返回
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:12:03
	 * @param str
	 * @return
	 */
	public static native String append(String str);

	/**
	 * 測試操作Test類
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:16:06
	 * @param test
	 */
	public native void test(String test);

	/**
	 * 測試操作Test
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:16:59
	 * @param test
	 */
	public native void test(Test test);

	/**
	 * 將test 轉16進位制
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:25:06
	 * @param test
	 * @return
	 */
	public native String toHex(byte[] test);

	/**
	 * 將test轉位元組
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:25:17
	 * @param test
	 * @return
	 */
	public native byte[] toBytes(String test);

}
Test.java
package com.flueky.jni;

public class Test {

	private String test;

	public Test(String test) {
		super();
		this.test = test;
	}

	public String getTest() {
		return test;
	}

	public void append(String str) {
		this.test = test + " " + str;
	}

	/**
	 * 測試呼叫靜態方法,多引數
	 * 
	 * @author flueky [email protected]
	 * @date 2016年3月22日 下午6:19:13
	 * @param str
	 */
	public static void print(int i, char c, float f, boolean z, String test) {
		System.out.println(String.format("Test printf:int = %d,char = %c,float = %.2f,boolean = %s,test = %s", i, c, f,
				z + "", test));
	}

}

main.java

package com.flueky.jni;

public class Main {

	public static void main(String[] args) {
		
		Jni.print();// 小飛哥0217
		Jni.print("csdn 測試");// 本地輸出:csdn 測試
		System.out.println(Jni.append("flueky"));// append flueky

		Jni jni = new Jni();

		jni.test(new Test("小飛哥0217"));
		jni.test("CSCN 測試");

		System.out.println(new String(jni.toBytes("ABCDE")));
		System.out.println(jni.toHex("12345".getBytes()));
	}

}

Jni方法說明:

1.獲取jclass物件:

a.env->FindClass("com/flueky/jni/Test");注意,這裡不是類的簽名。

b.env->GetObjectClass(obj);

2.獲取方法id:

a.env->GetMethodID(test_cls, "getTest","()Ljava/lang/String;");//獲取成員方法id

b.env->GetStaticMethodID(test_cls, "print","(ICFZLjava/lang/String;)V");//獲取靜態方法id

第一個引數,jclass物件,第二個引數方法名稱,第三個引數,方法簽名

3.呼叫方法:

a.env->CallVoidMethod(obj, append_mid, env->NewStringUTF("append test"));//呼叫成員方法

第一個引數jobject,第二個引數方法id,後面引數,依次是Java方法中的引數。

b.env->CallStaticVoidMethod(test_cls, print_mid, 1, 'c', 1.2f, true, test);//呼叫靜態方法

第一個引數jclass,第二個引數方法id,後面引數,依次是Java方法中的引數。

4.獲取屬性id:

a.env->GetFieldID(test_cls, "test", "Ljava/lang/String;");//獲取成員屬性id

b.env->GetStaticFieldID(test_cls, "test""Ljava/lang/String;");//獲取靜態屬性id,程式裡沒用到。

第一個引數jclass,第二個引數屬性名稱,第三個引數屬性簽名

5.獲取屬性值:

a.env->GetObjectField(obj, test_fid);

第一個引數,jobject,第二個引數,屬性id

b.env->GetStaticObjectField(test_cls, test_fid);

第一個引數,jclass,第二個引數,屬性id

6.生成jobject物件,通常都是從Java方法中傳遞過來,還有一種情況是呼叫java的構造方法來生成jobject物件。

獲取構造方法id,env->GetMethodID(test_cls, "<init>","(Ljava/lang/String;)V");

第一個引數jclass,第二個引數構造方法名稱(固定<init>),第三個引數構造方法簽名(返回型別固定void簽名V)

生成jobject,env->NewObject(test_cls, init_mid, jstr);

第一個引數jclass,第二個引數構造方法id,後面的引數依次是Java中建構函式的引數。

上述3 和 5 呼叫的jni函式名稱中,Char、Boolean、Byte、Int、Long、Short、Float、Double、Object、Void,可以相互替換,除了Void,其他型別的函式均有返回值。

typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
參照在Jni標頭檔案中的定義,Object型別的函式,返回值是jobject,可以根據實際情況轉成以上型別。

相關推薦

一個例子掌握JNI開發

繼續上一篇博文eclipse搭建JNI開發環境,現在我們從程式碼角度分析,C和Java混合程式設計時能實現的功能。 使用javah命令,編譯生成.h標頭檔案時,每個函式,至少都會有兩個引數。JNIEnv 和jclass/jobject。其中,當native方法是靜態方法(類

一個合格的HTML5開發工程師,需要掌握哪些知識呢?

HTML5開發不同於網頁設計,是網頁設計的深化,更關注於網站如何互動和瀏覽。web前端開發人員使用的技術CSS和HTML、JavaScript,根據設計師設計的雛形來編寫程式碼。佈局,框架,瀏覽器涉及到不同的領域知識廣度,把網站介面更好地呈現給使用者。一個合格的HTML5開發工程師,需要掌握哪些知識

JNI 資料傳輸的一個例子

#include "Config.h" #include "Format.h" #define LOG_TAG "CADTrack_Jni" #include "Log.h" #include <jni.h> #include <andr

【設計模式】Java服務開發應用策略模式的一個例子

當我們寫程式碼時遇到了需要if else 需要實現的問題時,就可以考慮是否引入某種設計模式,能讓程式碼寫得更加graceful。 假設我們現在有個設計使用者查詢的RESTful介面的需求,使用者可能有多個屬性,有id,firstName ,lastName,age,adr

iOS開發-一個例子學習iOS中的常見設計模式

原文iOS Design Patterns iOS設計模式 ,你可能已經聽說過這個術語,但你知道這意味著什麼嗎?雖然大多數的開發人員認為設計模式是非常重要的,但目前關於這個問題的文章不是很多,我們的開發人員有時寫程式碼有時不注重設計模式。 設計模式是軟體設

[Android NDK]Android JNI開發例子 ---3 在JNI中實現openGL功能

這個例子使用native部分來實現opengl es而不是java部分。 Java部分與例子2相似。 MainActivity.java public class MainActivity extends Activity { GLSurfaceView m

[AI開發]一個例子說明機器學習和深度學習的關係

深度學習現在這麼火熱,大部分人都會有‘那麼它與機器學習有什麼關係?’這樣的疑問,網上比較它們的文章也比較多,如果有機器學習相關經驗,或者做過類似資料分析、挖掘之類的人看完那些文章可能很容易理解,無非就是一個強調‘端到端’全自動處理,一個在特徵工程上需要耗費大量時

一個Android studio JNI程序

say debug native mil 教程 lib 國內 generate mea 現在網上有很多實用JNI的教程,本文主要用於記錄自己實現的方法和遇到的坑。 第一步,下載NDK. NDK可以去谷歌Android developer上下載。國內也有些中國版

一個例子讀懂 RequireJS

[0 src navigator resume www attribute base alert var 用一個例子讀懂 RequireJS 例子來自官方,我稍微改造了一下,如下: // project.html &

一個例子理解break和continue的區別

print 循環 str string out void i++ continue sys 結論:break用於終止整個循環,而continue用於終止某一次循環。public class Test {   public static void main(String[]

一個例子加深對servlet與tcp協議的理解

puts 一個 .com images 發送信息 mage 交流 tcp協議 host 理解一下servlet Java Servlet 是運行在 Web 服務器或應用服務器上的程序,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 服務器上的數據庫或

創建一個簡單項目的開發步驟

ges 技術 nbsp .cn 簡單 一個 技術分享 mage logs 創建一個簡單項目的開發步驟

01_c++下jni開發說明

img margin ati 函數 end create ica activit jni <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:to

react學習(二)試寫一個例子

生產環境 cti doc nbsp font register conf function htm 一、render方法 參考https://itbilu.com/javascript/react/EJiqU81te.html React的組件最終通過render方法渲染到

一個帖子學會Android開發四大組件

閱讀 task rom 活動 num code 才會 logs command 註:本文來自“友盟杯”,僅在此閱讀,學習 這個文章主要是講Android開發的四大組件,本文主要分為 一、Activity詳解二、Service詳解三、Broadcast Receiver詳

LoadRunner例子:檢查點為參數的一個例子

out ssi end cti common 登陸 diff source star LoadRunner例子:檢查點為參數的一個例子 檢查點是LoadRunner的一個功能,用來驗證業務功能的正確性。如果檢查的內容是變化的,腳本該如何寫呢? 問題

scrapy 的一個例子

extra 邏輯 進入 spi lines rec 使用步驟 middle over 1、目標:   scrapy 是一個爬蟲構架,現用一個簡單的例子來講解,scrapy 的使用步驟 2、創建一個scrapy的項目:   創建一個叫firstSpider的項目,命令如

MySQL inner join判斷驅動表和被驅動表的一個例子

span tab blog limit es2017 技術 spa employees rst 下述SQL中,驅動表是S表,因為S表有過濾條件 s.emp_no in (10001,10002)。 select s.emp_no ,count(distinc

[PY3]——一個例子理解多線程和daemon

div height 對話 block true nbsp for 守護進程 art 理解 (今天糾結已久,多謝junqi大大幫助理解,一語總結便解心頭疑惑)(下面幾點總結基本就是我們的對話) 1. 進程就相當於一個機器,多進程就相當於有多個機器在同時運行,多線程是

Spring-Boot:6分鐘掌握SpringBoot開發

post 運行 bind dev add cto 簡單 osi all 構建項目 從技術角度來看,我們要用Spring MVC來處理Web請求,用Thymeleaf來定義Web視圖,用Spring Data JPA來把閱讀列表持久化到數據庫裏,姑且先用嵌入式的H2數據庫。