IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用

本文学习如何在C代码中创建Java对象和对象数组,前面我们学习了C代码中访问Java对象的属性和方法,其实在创建对象时本质上也就是调用构造函数,因此本文知识学习起来也很轻松。有了前面学习数组创建的方法后,C代码创建对象数组同样很容易,下面开始学习吧~

1. C代码创建Java对象

创建Java对象本质就是调用构造函数,这与上一篇文章中提到的调用方法使用方法一致。下面直接贴代码:

package com.huachao.java;

/**
 * Created by HuaChao on 2017/03/23.
 */

public class HelloJNI {

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

    private String name;

    private HelloJNI(String name) {
        this.name = name;
    }

    public static native HelloJNI getInstance();

    public static void main(String[] args) {
        HelloJNI obj = HelloJNI.getInstance();
        System.out.println(obj.name);

    }

}

接下来在c代码中完成对getInstance()的实现。

#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"

JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
  (JNIEnv * env,jobject thisObj){

        jclass cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI");

        //获取构造函数的ID
        jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
        if (NULL == midInit) return NULL;
        // 调用构造函数
        jstring name=(*env)->NewStringUTF(env, "HuaChao");
        jobject newObj = (*env)->NewObject(env, cls, midInit, name);

        return newObj;
}

有一点需要注意,在调用HellJNI(String name)构造函数时,需要先将c的本地字符串转为jstring类型,即调用NewStringUTF函数,再作为参数传入。运行结果如下:

HuaChao

2. C代码创建对象数组

创建对象数组其实就是结合对象的创建和数组的创建来实现,都是前面学过的知识。在HelloJNI.java中加入getArray函数,并且修改main函数如下:

 public static native HelloJNI[] getArray(String[] names);

 public static void main(String[] args) {
      String[] names = {"HuaChao", "Lianjin"};
      HelloJNI[] arr = HelloJNI.getArray(names);
      for (HelloJNI obj : arr) {
          System.out.println("name:" + obj.name);
      }

  }

在本地C代码中,对应getArray函数的实现如下:

JNIEXPORT jobjectArray JNICALL Java_com_huachao_java_HelloJNI_getArray
 (JNIEnv * env,jobject thisObj,jobjectArray names){

      // 获取HelloJNI类的引用
       jclass clazz = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
       //获取构造函数的ID
       jmethodID midInit = (*env)->GetMethodID(env, clazz, "<init>", "(Ljava/lang/String;)V");
       // 获取数组长度
       jsize length = (*env)->GetArrayLength(env, names);
       jobjectArray outJNIArray = (*env)->NewObjectArray(env, length, clazz, NULL);
       //遍历names数组
       int i;
       for (i = 0; i < length; i++) {
          jstring name = (*env)->GetObjectArrayElement(env, names, i);
          if (NULL == name)
                return NULL;
           jobject newObj = (*env)->NewObject(env, clazz, midInit, name);
          (*env)->SetObjectArrayElement(env, outJNIArray, i, newObj);
       }

       return outJNIArray;
 }

最后,运行如下:

name:HuaChao
name:Lianjin

3. C代码对Java类的局部和全局引用

与Java代码类似,在C代码函数里面创建的对象时,对对象的引用为局部引用,当函数执行结束时,引用无效。但是如果在函数外对对象进行引用,引用会一直有效,直到程序结束。前面我们频繁地用到了jclass和jmethodID以及jfieldID,下面我们尝试将其作为c代码的全局引用。

首先看看Java中代码。

package com.huachao.java;

/**
 * Created by HuaChao on 2017/03/23.
 */

public class HelloJNI {

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

    private String name;

    private HelloJNI(String name) {
        this.name = name;
    }

    public static native HelloJNI getInstance();

    public static void main(String[] args) {
        HelloJNI obj1 = HelloJNI.getInstance();
        HelloJNI obj2 = HelloJNI.getInstance();
        System.out.println("obj1:--->>name=" + obj1.name);
        System.out.println("obj2:--->>name=" + obj2.name);
    }

}

然后在C代码中,将jclass和jmethodID对象作为全局变量

#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"
static jclass cls;
static jmethodID midInit;
JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
  (JNIEnv * env,jobject thisObj){

        //如果cls为null,则调用FindClass赋值
        if(NULL==cls){
            cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
        }
        if(NULL==cls) return NULL;

        //如果midInit为NULL,获取构造函数的ID
        if(NULL==midInit){
            midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
        }
        if (NULL == midInit) return NULL;
        // 调用构造函数
        jstring name=(*env)->NewStringUTF(env, "HuaChao");
        jobject newObj = (*env)->NewObject(env, cls, midInit, name);

        return newObj;
}

点击运行,发现报错!!!!!

为什么会出现这样的情况呢?主要是第一次调用FindClass()函数时,能正常获取到com.huachao.java.HelloJNI类对象,并作为全局静态变量存储。但是,在接下来调用中,引用不再有效,虽然此时cls变量不为NULL,主要原因是FindClass()返回的是一个局部引用,当函数执行结束时,引用将变为无效的引用。

为了解决这个问题,可以通过调用NewGlobalRef函数将局部引用转为全局引用。完成转换后,记得将局部引用删除。修改c代码如下:

#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"
static jclass cls;
static jmethodID midInit;
JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
  (JNIEnv * env,jobject thisObj){

        //如果cls为null,则调用FindClass赋值
        if(NULL==cls){
           jclass clsLocal = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
           //局部引用转为全局引用
           cls = (*env)->NewGlobalRef(env, clsLocal);
           //删除局部引用
           (*env)->DeleteLocalRef(env, clsLocal);
        }
        if(NULL==cls) return NULL;

        //如果midInit为NULL,获取构造函数的ID
        if(NULL==midInit){
            midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
        }
        if (NULL == midInit) return NULL;
        // 调用构造函数
        jstring name=(*env)->NewStringUTF(env, "HuaChao");
        jobject newObj = (*env)->NewObject(env, cls, midInit, name);

        return newObj;
}

运行结果如下:

obj1:--->>name=HuaChao
obj2:--->>name=HuaChao

细心的同学会发现,同为全局引用,为什么jclass全局引用需要将本地引用转为全局引用,而jmethodID对象却不需要?注意,jmethodIDjfieldID并不是jobject对象,无需转换。

原文地址:https://www.cnblogs.com/Free-Thinker/p/10608629.html

时间: 2024-08-07 14:12:58

IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用的相关文章

在idea2018和vs2017平台下JNI编程调用C++算法(2)-调用嵌套dll实践

背景 我之所以采用JNI是由于我要实现一个java系统,java系统需调用C++核心程序.而目前的需求是不想破坏C++核心程序的独立性.因此想把JNI调用的接口写在C++核心程序之外.这就需要 首先将C++核心程序生成内层dll 在外层包装一层JNI接口调用内层dll 再将外层C++生成外层dll后,java调用外层dll. 那么JNI支持这种嵌套dll调用吗?经验告诉我肯定能,但是程序员从来不敢想当然,那就让我们试一试,顺便理清思路吧. 上篇文章介绍了在idea2018和vs2017两大平台下

本地C代码中创建Java对象

创建Java域的对象就是创建Java类的实例,再调用Java类的构造方法. 以Bitmap的构建为例,Bitmap中并没有Java对象创建的代码及外部能访问的构造方法,所以它的实例化必然是在JNI的c中实现的. BitmapFactory.java中提供了得到Bitmap的方法,时序简化为: BitmapFactory.java ->BitmapFactory.cpp -> GraphicsJNI::createBitmap()  [graphics.cpp] GraphicsJNI::cre

ios 编程--使用代码创建新的相册随加项目

2013-04-23 13:29:04| 分类: 默认分类 |举报|字号 订阅 编写ios 编程--使用代码创建新的相册,是参考http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/来写的. ALAssetsLibrary+CustomPhotoAlbum.h 里的代码是: #import <Foundation/Foundation.h> #impor

Android NDK开发(五)--C代码回调Java代码【转】

转载请注明出处:http://blog.csdn.net/allen315410/article/details/41862479 在上篇博客里了解了Java层是怎样传递数据到C层代码,并且熟悉了大部分的实际开发知识,基本上掌握这些就可以做一个基本的NDK开发了,但是光是了解Java回调C层的数据是不是还不够啊,考虑问题要考虑可逆性,Java能回调C,那么C能否反过来回调Java呢?答案是肯定可以的,这篇博客就介绍一个C语言如何调用Java层的代码.以下是一些问题场景,我们带着这个问题场景来分析

(五)使用代码创建UI

1 package com.example.codeUI; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.Gravity; 6 import android.widget.LinearLayout; 7 import android.widget.TextView; 8 9 public class MainActivity extends Activity { 10 11 @

《转》Windows平台下Git服务器搭建

该文章转自:http://www.codeceo.com/article/windows-git-server.html Windows平台下Git服务器搭建 第一步:下载Java,下载地址:http://www.java.com/zh_CN/ 第二步:安装Java.安装步骤不再详述. 第三步:配置Java环境变量. 右键”计算机” => ”属性” => ”高级系统设置” => ”高级” => “环境变量” => “系统变量”. 新建: 变量名:JAVA_HOME 变量值:D

在 JNI 编程中避免内存泄漏

JAVA 中的内存泄漏 JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏:JVM 内存中 native memory 的内存泄漏. Java Heap 的内存泄漏 Java 对象存储在 JVM 进程空间中的 Java Heap 中,Java Heap 可以在 JVM 运行过程中动态变化.如果 Java 对象越来越多,占据 Java Heap 的空间也越来越大,JVM 会在运行时扩充 Java Heap 的容量.如果 Java Heap 容量

Linux平台下:块设备、裸设备、ASMlib、Udev相关关系

对磁盘设备(裸分区)的访问方式分为两种:1.字符方式访问(裸设备):2.块方式访问 Solaris平台 : 在Solaris平台下,系统同时提供对磁盘设备的字符.块方式访问.每个磁盘有两个设备文件名: 一个在/dev/dsk目录下,比如/dev/dsk/c1t1d1s1,当以这个设备名操作时,就是以块的方式操作磁盘: 一个在/dev/rdsk目录下,比如/dev/rdsk/c1t1d1s1,当以这个设备名操作时,就是以字符方式(裸设备方式r)操作磁盘 Linux平台 : 在linux平台下,缺省

解析Java的JNI编程中的对象引用与内存泄漏问题

JNI,Java Native Interface,是 native code 的编程接口.JNI 使 Java 代码程序可以与 native code 交互--在 Java 程序中调用 native code:在 native code 中嵌入 Java 虚拟机调用 Java 的代码.JNI 编程在软件开发中运用广泛,其优势可以归结为以下几点: 利用 native code 的平台相关性,在平台相关的编程中彰显优势. 对 native code 的代码重用.native code 底层操作,更