前言:
如果你是为了让Java调用C语言,使用复杂的JNI大可不必,可以选择更简单便捷安全的方式——JNA。当然,如果用C调用Java,那就只能用JNI了。JNI使用复杂,尤其是提供的函数使用时一定要注意内存问题。因为Java层申请内存后是没有内存释放的,完全依赖java虚拟机来释放,而C层不然,必须时刻谨记申请的内存一定要及时释放。
JNI函数大全可参考:https://www.cnblogs.com/H-BolinBlog/p/6097829.html
JNI使用时会遇到诸多问题,目前我遇到的问题如下:
1、New作为前缀的方法,如NewByteArray()等,在使用完毕后,必须手动调用DeleteLocalRef()来释放(返回值除外)。
GetByteArrayELement()必须要调用ReleaseByteArrayElements()进行释放。如果只是想取jbytearray中的数据,那么完全可以用GetByteArrayRegion()实现。
2、在不同线程调用java方法,需要保存jobject对象,这时需要将jobject对象用NewGlobalRef()函数将其转化为全局引用,使用完毕后使用DeleteLocalRef ()释放。
3、在JNI层获取到的jbytearray长度是整个byte数组的容量,而非有效数据长度。最好将数据格式化,如定义成LV格式,前若干字节作为长度标识,长度之后才是有效数据。
4、不同线程使用JNIEnv*对象,需要AttachCurrentThread将env挂到当前线程,否则无法使用env。
5、javap命令是对java的class文件操作;javah命令需要在包名到上一层路径运行才行,否则无法生成.h文件。
6、尽量避免频繁调用JNI或者是使用JNI传输大量到数据。
7、由于JNI层引用申请个数有限制,如果忘记释放引用(全局或本地)而又不断申请新的引用时就会出现这样的错误:Reference Table overflow (max=1024) 或者是 Reference Table overflow (max=512)。
最后,奉上我当时写的代码,java类的功能是HTTPS收发,用来给Native层使用。
java类如下:
public class JniCallBack {
private static JniCallBack jniCallBack;
static {
System.loadLibrary("java_fun");
}
public static JniCallBack getInstance(){
if(jniCallBack == null){
jniCallBack = new JniCallBack();
}
return jniCallBack;
}
private JniCallBack() {
}
public native void JNINativeInit();
public static byte[] m_request = new byte[2048];
public static byte[] m_response = new byte[2048];
public static byte[] m_url = new byte[256];
public static int m_reqLen = 0;
public static int m_respLen = 0;
public static int m_urlLen = 0;
public static int sendRecv(){
try {
m_respLen = 0;
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] tm = {new MyX509TrustManager()};
sslContext.init(null, tm, new SecureRandom());
byte[] bytesURL = new byte[m_urlLen];
System.arraycopy(m_url, 0, bytesURL, 0, m_urlLen);
String strUrl = new String(bytesURL);
URL url = new URL(strUrl);
//创建HttpURLConnection 对象
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "x-ISO-TPDU/x-auth");
conn.setRequestProperty("Connection", "Keep-Alive"); //请求的数据
conn.setRequestProperty("User-Agent", "Donjin Http 0.1"); //请求的数据
conn.setRequestProperty("Cache-Control", "no-cache"); //请求的数据
conn.setRequestProperty("Accept", "*/*"); //请求的数据
conn.setRequestProperty("Content-Length" , m_reqLen + ""); //设置输入和输出流
conn.setDoOutput(true);
conn.setDoInput(true);
OutputStream os = conn.getOutputStream();
os.write(m_request, 0, m_reqLen);
os.flush();
os.close(); //得到HTTP响应码
InputStream is = conn.getInputStream(); //获取返回报文
ByteArrayOutputStream output = new ByteArrayOutputStream();
int n = 0, off = 0;
while (-1 != (n = is.read(m_response))) {
output.write(m_response, off, n);
off += n;
}
m_respLen = conn.getContentLength();
return m_respLen;
}catch (Exception e){
Log.e("",e.getMessage());
e.printStackTrace();
}
return -1;
}
}
JNI层java_fun.cpp文件如下:
#include "java_fun.h"
#include "string.h"
static JavaVM * g_javavm = NULL;
jbyteArray g_jba_req;
jbyteArray g_jba_resp;
int HttpExchange(unsigned char *response, int *pRespLen,
const unsigned char *request, int reqLen,
int timeout)
{
int status;
JNIEnv* _jniEnv = NULL;
status = g_javavm->GetEnv((void **)&_jniEnv, JNI_VERSION_1_6);
if (status < 0)
{
status = g_javavm->AttachCurrentThread(&_jniEnv, NULL);
if (status < 0)
{
_jniEnv = NULL;
}
}
if (NULL == _jniEnv)
{
return -1;
}
jclass tmpclass =_jniEnv->FindClass("com/bsit/qm702/jni/JniCallBack");
_jniEnv->SetByteArrayRegion(g_jba_req, 0, reqLen, (jbyte*)request);
_jniEnv->SetStaticIntField(tmpclass, _jniEnv->GetStaticFieldID(tmpclass, "m_reqLen", "I"), (jint)reqLen);
int ret = (int)_jniEnv->CallStaticIntMethod(tmpclass, _jniEnv->GetStaticMethodID(tmpclass, "sendRecv", "()I"));
if (ret > 0)
{
_jniEnv->GetByteArrayRegion(g_jba_resp, 0, ret, (jbyte *)response);
*pRespLen = ret;
}
//释放
_jniEnv->DeleteLocalRef(tmpclass);
return ret;
}
int HttpSetUrl(unsigned char *url, int urlLen)
{
int status;
JNIEnv* _jniEnv = NULL;
status = g_javavm->GetEnv((void **)&_jniEnv, JNI_VERSION_1_6);
if (status < 0)
{
status = g_javavm->AttachCurrentThread(&_jniEnv, NULL);
if(status < 0)
{
_jniEnv = NULL;
}
}
if (NULL == _jniEnv)
{
return -1;
}
jclass tmpclass =_jniEnv->FindClass("com/bsit/qm702/jni/JniCallBack");
jbyteArray jba_url = (jbyteArray)_jniEnv->GetStaticObjectField(tmpclass, _jniEnv->GetStaticFieldID(tmpclass, "m_url", "[B"));
_jniEnv->SetByteArrayRegion(jba_url, 0, urlLen, (jbyte*)url);
_jniEnv->SetStaticIntField(tmpclass, _jniEnv->GetStaticFieldID(tmpclass, "m_urlLen", "I"), (jint)urlLen);
//释放
_jniEnv->DeleteLocalRef(tmpclass);
_jniEnv->DeleteLocalRef(jba_url);
return 0;
}
extern "C" JNIEXPORT void
JNICALL
Java_com_bsit_qm702_jni_JniCallBack_JNINativeInit(
JNIEnv *env,
jobject thiz) {
env->GetJavaVM(&g_javavm);
jclass tmpclass =env->FindClass("com/bsit/qm702/jni/JniCallBack");
jobject tmpobj = env->NewObject(tmpclass, env->GetMethodID(tmpclass, "<init>", "()V"));
jbyteArray jba_req = (jbyteArray)env->GetStaticObjectField(tmpclass, env->GetStaticFieldID(tmpclass, "m_request", "[B"));
g_jba_req = (jbyteArray)env->NewGlobalRef(jba_req);
jbyteArray jba_resp = (jbyteArray)env->GetStaticObjectField(tmpclass, env->GetStaticFieldID(tmpclass, "m_response", "[B"));
g_jba_resp = (jbyteArray)env->NewGlobalRef(jba_resp);
env->DeleteLocalRef(jba_req);
env->DeleteLocalRef(jba_resp);
env->DeleteLocalRef(tmpclass);
env->DeleteLocalRef(tmpobj);
}
extern "C" JNIEXPORT void
JNICALL
Java_com_bsit_qm702_jni_JniCallBack_JNINativeFinal(
JNIEnv *env,
jobject thiz) {
env->DeleteGlobalRef(g_jba_req);
env->DeleteGlobalRef(g_jba_resp);
}