Java連結c++動態庫實現字串的傳輸---簡單的字元拼裝返回

Bouy發表於2020-12-06


前言

之前發表了一篇Java連結c++動態庫簡單的加法計算
然後發現在這個基礎上做字串的傳遞和處理的過程中出現了亂碼,導致不得不尋找解決方案,我不停的思考、實踐,最終解決了。
這裡有點打臉,我還在之前的文章裡說用gcc命令生成.h檔案比較麻煩,但是這種方法非常好用,而且不需要再Java中匯入jna包,並且Java自帶的jni.h檔案有介面,在c++中比較好用。
下面是我解決的步驟希望對大家有所幫助。


提示:可能字串亂碼在Java那邊也能解決,但是我暫時沒有想到方法

一、Java中的操作

1、新建Java專案名為javaJni

這裡我建立好了,就不演示了

2、新建一個class檔案新增以下內容

這裡第一個方法是做一個減法的介面,第二就是做一個字串的傳輸進行處理並返回字串,下面主函式呼叫生成的方法,不影響接下來生成.h檔案的操作。

package com.aijiao.test;

public class JNIDemo {

	// 定義一個方法,該方法在C中實現
	public native int testInt(int k);

	public native String testString(String arr);

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.loadLibrary("TestJNI");
		JNIDemo demo = new JNIDemo();
		int k = demo.testInt(91);
		System.out.println("value="+k);
		String m = "msadasdasasd";
		m = demo.testString(m);
		System.out.println(m);
	}

}

3、生成.h檔案

1)找到檔案的目錄,可以看到它的.class檔案
在這裡插入圖片描述
2)使用gcc命令生辰.h檔案
在專案bin層下輸入以下命令命令,生成.h檔案

javah -classpath . -jni com.aijiao.test.JNIDemo

在這裡插入圖片描述
在這裡插入圖片描述
生成的檔案如下

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

#ifndef _Included_com_aijiao_test_JNIDemo
#define _Included_com_aijiao_test_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_aijiao_test_JNIDemo
 * Method:    testInt
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_com_aijiao_test_JNIDemo_testInt
  (JNIEnv *, jobject, jint);

/*
 * Class:     com_aijiao_test_JNIDemo
 * Method:    testString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_aijiao_test_JNIDemo_testString
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

二、在c++中的操作

1.建立動態連結庫專案–名字為TestJNI

在這裡插入圖片描述

2.在原始檔右擊建立類–檔名字為TestJNI

在這裡插入圖片描述

3.把生成的.h檔案和jni.h、Jni_md.h移到動態庫的目錄下,如圖

在這裡插入圖片描述

4.標頭檔案右擊匯入現有項,把三個.h檔案新增到專案

5.在生成的TestJNI.cpp檔案中新增以下內容

#include "pch.h"
#include "com_aijiao_test_JNIDemo.h"
#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

JNIEXPORT jint JNICALL Java_com_aijiao_test_JNIDemo_testInt
(JNIEnv *, jobject, jint a) {
	int value = a - 7;
	return value;
}

JNIEXPORT jstring JNICALL Java_com_aijiao_test_JNIDemo_testString
(JNIEnv * env, jobject obj, jstring a) {
	char* b = jstringTochar(env, a);
	string c = b;
	c = c + c;
	//(env)->NewStringUTF(a)
	return (env)->NewStringUTF(c.c_str());
}


char* jstringTochar(JNIEnv *env, jstring jstr)
{ //UTF8/16轉換成gb2312
	int length = (env)->GetStringLength(jstr);
	const jchar* jcstr = (env)->GetStringChars(jstr, 0);

	int clen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, NULL, 0, NULL, NULL);

	char* rtn = (char*)malloc(clen); //更正。作者原來用的是(char*)malloc( length*2+1 ),當java字串中同時包含漢字和英文字母時,所需緩衝區大小並不是2倍關係。
	int size = 0;
	size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn, clen, NULL, NULL);
	if (size <= 0)
		return NULL;
	(env)->ReleaseStringChars(jstr, jcstr);
	rtn[size] = 0;
	return rtn;
}

6.修改生成的.h檔案

java生成.h檔案中的#include <jni.h>改為#include “jni.h”,並且把char* jstringTochar(JNIEnv *env, jstring jstr);新增到生成的.h檔案中
在這裡插入圖片描述

7.生成解決方案

在這裡插入圖片描述

三、執行Java呼叫c的方法

在這裡插入圖片描述
執行JNIDemo,發現執行成功,並且字串沒有亂碼
在這裡插入圖片描述


總結

以上就是我的整個過程,其中在c++中有一個jstringTochar類,是把Java傳來的string型別的字串轉為char*,然後轉為string型別進行操作,因為java和c++的編碼形式不同,這個介面就是把Java中的編碼型別UTF8/16轉換成c++的gb2312編碼形式。

下期預告

我下面會更新在linux中java如何呼叫so庫

發文不易,請點贊支援支援

參考文獻

JNI入門教程
jstringTochar型別轉換忘記了在哪找的,如果有人有連結,請發給我。

相關文章