JNI用法小结

JNI用法小结

因项目需要学习了用JNI调用动态库的方法,现总结如下。

 

这次学到了两个知识点:

1.         调用第三方动态库的接口;

2.         回调java自身或自定义的函数

名词介绍:

 JNI Java Native Interface 的缩写,用以解决 Java 对本地方法的操作问题,而本地方法是以库文件的形式存放的(.dll for windows.so for Unix)。

 

NativeJNI关键字,用来声明要调用本地的方法。

 

实现过程

1) 编写含有native声明的方法的java

 

如我要调用研华1780动态库里的初化函数与发脉冲函数,则写如下的类:

package signalContrl;

 

public class SignalContrl {

    static{

           System.loadLibrary("signalCtrl");

    }

   

    public native boolean ini ();

   

    public native boolean startCounter(int channel,int rate);

   

    public native boolean startFreq(int channel,int FoutSrc,int usDivider);

   

    public native boolean stopCounter();

   

    public native boolean stopFreq();

   

}

其中System.loadLibrary(“signalCtrl”);是声明要调用的库的名字为“signalCtrl.

其中用到关键字native,说明这些方法都是要调用本地动态库里的函数。

 

2) 使用javac命令编译所编写的java

在安装了jdk的情况下(我装的jdk1.5),切换到dos界面,转到存放SignalCtrl.java的目录下,调用javac SignalCtrl.java。然后当前目录下就会出现了个 SignalCtrl.class文件。

 

3) 使用javah java类名生成扩展名为h的头文件

dos界面输入命令“cd ..”切换到上级目录。

然后输入命令“javah signalCtrl.SignalCtrl”。其中signalCtrl为包名,SignalCtrl为类名。命令执行完后,在当前目录就会生成“signalContrl_SignalContrl.h”文件。

4) 使用C++实现本地方法

Visual C++中新建一个空的动态库工程,命名为“signalCtrl”,将上面产生的“signalContrl_Signal-Contrl.h”添加到工程中,然后根据需要实现头文件中的方法。

例如实现控件1780发脉冲的方法如下:

JNIEXPORT jboolean JNICALL Java_signalContrl_SignalContrl_startCoun -ter(JNIEnv *, jobject, jint channel,jint rate)

{

           //开启计数功能的函数

           usChan = channel;

           ptCounterPulseStart.counter  = channel;

           ptCounterPulseStart.period   = (float)(1.0/rate);

           ptCounterPulseStart.UpCycle  = ptCounterPulseStart.period/2;

           ptCounterPulseStart.GateMode = 0;

           

             if((ErrCde = DRV_CounterPulseStart(lDriverHandle,

             (LPT_CounterPulseStart)&ptCounterPulseStart)) != 0)

             {

             DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg);

             return false;

             }

             startNum++;

           return true;

}

 

上例中,函数名是从“signalContrl_Signal-Contrl.h”文件中拷贝过来的,然后函数体内通过对参数的进行处理,然后调用1780的动态库里的函数“DRV_CounterPulseStart()”。这样就成功使用了1780的动库。

 

5) 将C++编写的文件生成动态连接库

Visual C++编译写好的工程,这时在Debug目录中就会生成一个signalCtrl.dll的文件。将此文件拷贝到需要的java项目的工作目录下,这样使用jni的工作就做完了。此后你就可以调用“SignalContrl”类里的方法,来实现对1780的控制。测试代码如下:

package testSignalContrl;

 

import signalContrl.SignalContrl;

 

public class GaneratorSignal {

 

    SignalContrl sc = new SignalContrl();

   

    public static void main(String[] args) {

        GaneratorSignal gS = new GaneratorSignal();

        boolean  flag = gS.sc.ini();

        if(!flag)

        {

            System.out.print("打开失败");

        }

        flag = false;

        flag = gS.sc.startCounter(0,500);

        //gS.sc.startFreq(0, 7, 13);

        if(!flag)

        {

            System.out.print("开始发脉冲失败!请重新启动");

        }

    }

}

 

上例中,首先实例化了一个GaneratorSignalgS, 然后由gS调用变量sc的方法ini()初始化1780卡,然后调用startCounter()使用1780卡发送脉冲。

 

面对基础的调用,上面几步就可以实现对本地方法的调用了。但是java里面有很多地方用到了回调,为了能根据动态库的使用情况,实时的回调java已经编写好的方法,我实现了jni函数的回调功能。基体做法如下:

a)       首先定义一个接口:

package MyCallback;

 

public interface Car {

        public void run();

}

b)       实现这个接口:

package MyCallback;

 

public class Cars implements Car{

        public void run() {  

             System.out.println("开车上班路上");  

        //回调方法中可以调用类似客户端的方法得到一些客户端的信息,如speed    

         }

   

        public Cars get(){

            System.out.println("创建Cars实例。");  

            return new Cars();

        }

   

        public void run(int i){

        System.out.println(i);

        }

}

编写jni类:

package MyCallback;

 

public class Employee {  

     

    static{

           System.loadLibrary("Car");

    }

    public native void shangBan(Cars car);

      

    public native void otherMethod();

}  

    上类中声明了两个方法:shangBan()otherMethod();其中上shangBan()方法接收了一个javaCars,做参数。在其C++实现中调用Cars类方法,实现回调。

 

JNI里使用java类的某些方法,分为以下几个步骤:

1. 查找该类:

   jclass xxx = (*env)->FindClass(env, "Lclass_name;");或是GetObjectClass();

2. 查找该类的初始化方法:

   jmethodID xxx = (*env)->GetMethodID(env, jclass, "<init>", "(M)N");

3. 查找需要调用的该类的方法:

   jmethodID xxx = (*env)->GetMethodID(env, jclass, "(M)N" );

4. 初始化该类的实例:  

   jobject xxx = (*env)->NewObject(env, jclass, jmethodID );

5. 调用实例的某方法: 

   (*env)->CallObjectMethod(env, jclass, jmethodID, [parameter1, parameter2,...] );

6. 释放实例: 

   (*env)->DeleteLocalRef(env, xxx);

 

将上类编译,然后生成头文件“MyCallback_Employee.h”用VisualC++实现该头文件中的shangBan方法,代码如下:

JNIEXPORT void JNICALL Java_MyCallback_Employee_shangBan

  (JNIEnv *env, jobject obj, jobject car)

{   

           jint i = 23;

jclass   cls   =     env->GetObjectClass(car);

           jmethodID   runId = env->GetMethodID(cls,"run","()V");

           jmethodID   numID = env->GetMethodID(cls,"run","(I)V");

jmethodID   getId = env->GetMethodID(cls,"get","()LmyCallback /Cars;");

           if   (runId == 0)  

                 return;

           jobject emp = env->NewObject(cls,getId);      

           env->CallVoidMethod(emp,runId);

           env->CallVoidMethod(emp,numID,i);

}

上面方法中,声明一个jclass cls用来接收传进的参数“car”。由env调用GetObjectClasscar)来获取传进来的对象。然后调用GetMethodIDcls,”run”,”()V”)来获取run函数的ID值,同理获取get()函数的ID值。再由NewObject新建一个类的实例emp,然后由CallVoidMethod(emp,runId)回调Cars实现的run函数。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在ExoPlayer中,JNIJava Native Interface)用于与底层的原生代码进行交互。通过JNI,我们可以在Java代码中调用C/C++编写的底层库。为了在ExoPlayer中使用JNI,需要进行以下步骤: 1. 首先,在项目的build.gradle文件中添加ExoPlayer的依赖项。根据引用,可以添加以下依赖项: ``` implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3' implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3' ``` 2. 接下来,需要在Java代码中加载JNI库。通常,在启动应用程序时加载JNI库是一个好的做法。可以使用`System.loadLibrary()`方法加载库文件。具体来说,可以使用以下代码加载ExoPlayer的JNI库: ``` System.loadLibrary("exoplayer"); ``` 这将加载名为"exoplayer"的JNI库。 3. 一旦JNI库加载成功,就可以在Java代码中使用JNI接口。具体的JNI用法取决于你想要与底层代码交互的功能和需求。你可以使用JNI调用底层方法、传递参数以及处理返回值。 总之,通过在项目中引入ExoPlayer的依赖项并加载JNI库,你就可以在Java代码中使用JNI接口与ExoPlayer的底层代码进行交互。请注意,在使用JNI时需要谨慎处理内存管理和错误处理,以确保代码的健壮性和稳定性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [音视频开发之旅(44)-ExoPlayer介绍及简单使用](https://blog.csdn.net/u011570979/article/details/116861272)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值