JNI 使用注意事项

前言:

     如果你是为了让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);
}

猜你喜欢

转载自blog.csdn.net/Kernel_Heart/article/details/83545909