一、JNA和JNI的对比

  1.JNI的调用流程
  Android应用开发中要实现Java和C,C++层交互时,想必首先想到的是JNI,但是JNI的使用过程十分繁琐,需要自己再封装一层JNI接口进行转换(使用SUN规定的数据结构去替代C语言的数据结构),包名、函数名等都要匹配,难以阅读和更新。
  如下图是通过JNI实现Java调用C层的方法流程:
  

  2.什么是JNA?与JNI有什么差异?

  JNA(Java Native Access):建立在JNI之上的Java开源框架,SUN主导开发,用来调用C、C++代码,尤其是底层库文件(windows中叫dll文件,linux下是so【shared object】文件)。JNA简化了Java调用原生函数的过程,原理是提供了一个动态的C语言编写的转发器(实际上也是一个动态链接库,在Linux-i386中文件名是:libjnidispatch.so)可以自动实现Java与C之间的数据类型映射。
  相比JNI,JNA只需要导入一个.jar和一个.so,然后就可以在Java中直接声明so中公开的函数并调用,十分方便。而JNA有两个小缺点:(1)性能上会比通过JNI调用动态链接库要稍低,但总体影响不大,因为JNA也避免了JNI的一些平台配置的开销。(2)因为JNA调用是直接在Java层实现,所以反过来C层无法直接通过JNA调用Java方法,但是Java层可以把方法模拟函数指针传到c层进行回调。
 
  

 二、JNA实践
 
  1. jna.jar包和libjnidispatch.so库文件下载
  
     JNA项目的Git Hub地址:https://github.com/java-native-access/jnalibjnidispatch.so 包含在android-armv7.jar中,以及jna.jar 都在 dist 目录下。
  
  2.导入jar包和so到Android studio工程
  (1)目录结构
    

  (2)build.gradle 配置

apply plugin: 'com.android.library'

android {
......
sourceSets{
main{
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
......
} dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
......
}
   3.代码实现
     (1)新建一个Java接口直接加载第三方动态库 libnanovoice.so

    其中libnanovoice.so对于应的头文件(.h)内容如下:

#ifndef __JB_NANOSIC__H__
#define __JB_NANOSIC__H__ #ifdef __cplusplus
extern "C" {
#endif typedef void (*AppCallback) (char* data,int datalen); int nano_open(AppCallback cb);
int nano_close(void); #ifdef __cplusplus
}
#endif #endif

    根据动态库提供的头文件里函数声明,编写JnaNanovoice.java 内容如下:

package com.lxl.nanosic.voice;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer; public interface JnaNanovoice extends Library {
public static final String JNA_LIBRARY_NAME = "nanovoice";
public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(JnaNanovoice.JNA_LIBRARY_NAME);
public static final JnaNanovoice INSTANCE = (JnaNanovoice)Native.loadLibrary(JnaNanovoice.JNA_LIBRARY_NAME, JnaNanovoice.class); //直接加载第三方动态库 //定义接口AppCallback,继承自com.sun.jna.Callback
public interface AppCallback extends Callback {
void dataReceived(Pointer data, int datalen);
} //动态库的函数声明
int nano_open(AppCallback cb);
int nano_close();
}

  c层和JNA层变量类型有所差异,需要转换:

  推荐一个转换工具 :jnaeratorStudio.jar   使用方法:java -jar jnaeratorStudio.jar

  (2)调用c库函数,其中Java回调接口需要进一步实现, NanoVoiceRecord.java 中JNA调用部分代码截取如下:

package com.lxl.nanosic.voice;

import android.util.Log;
import com.sun.jna.Pointer;
import java.util.LinkedList; public class NanoVoiceRecord { private final String TAG = "NanoVoiceRecord";/**
* 回调函数
*/
private static JnaNanovoice.AppCallback mDataCallback; ......   /** API 2:开始录音 **/
public void start() {
  ......
    // JNA接口
mDataCallback = new JnaNanovoice.AppCallback() {
// 实现接口中的回调
public void dataReceived (Pointer data, int datalen){
if(null != data){
byte[] buffer = data.getByteArray(0, datalen);
int res = OnDataReceived(buffer, datalen);
Log.w(TAG, "...OnDataReceived = " + res + "m_in_q.size=" + m_in_q.size());
} else {
Log.e(TAG, "...Callback data is null !!!");
}
}
};
// 开始录音并传入回调
JnaNanovoice.INSTANCE.nano_open(mDataCallback);
} /** API 3:停止录音 **/
public void stop()
{
JnaNanovoice.INSTANCE.nano_close(); //调用JNA接口退出录音
isRecording=false;
} ......   /**
* 处理c层回调上来的遥控器语音数据方法
*/
public int OnDataReceived(byte[] buffer, int size) {
byte[] bytes_pkg;
int bufferLength; if(m_in_q==null){
return 0;
} bytes_pkg = buffer.clone(); if (m_in_q.size() > 6) { //最多缓存6个包
m_in_q.removeFirst();
}
m_in_q.add(bytes_pkg); ......
}

  

  至此便实现了Java层通过JNA对c层函数的直接调用:

   JNA实践Demo(包含转换jar工具)已传至Git Hub : https://github.com/dragonforgithub/JnaDemo
 
 

Android:JNA实践(附Demo)的更多相关文章

  1. winserver的consul部署实践与.net core客户端使用(附demo源码)

    winserver的consul部署实践与.net core客户端使用(附demo源码)   前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的 ...

  2. Android 浮动窗口进阶——画中画,浮动视频(附Demo)

    今天继续上一篇Android顶层窗口.浮动窗口的进阶应用.上一篇主要讲解了WindowManager服务和如何使用WindowManager编写一个顶层窗口.今天主要是讲讲如何在顶层窗口里面播放视频, ...

  3. 开源分享:谷歌大佬联合打造《高级Kotlin强化实战(附Demo)》

    Kotlin 以其简洁的特性而闻名,而在我们的实践中,更加简洁就意味着更加高效.事实上,在使用 Kotlin 的专业 Android 开发者中,有多达 67% 的人表示 Kotlin 已经帮助他们提升 ...

  4. fir.im Weekly - 2016 年 Android 最佳实践列表

    2016 年已经过去一半,你在年初制定的成长计划都实现了吗? 学海无涯,技术成长不是一簇而就的事情.本期 fir.im Weekly 推荐 王下邀月熊_Chevalier的 我的编程之路--知识管理与 ...

  5. 学习笔记TF066:TensorFlow移动端应用,iOS、Android系统实践

    TensorFlow对Android.iOS.树莓派都提供移动端支持. 移动端应用原理.移动端.嵌入式设备应用深度学习方式,一模型运行在云端服务器,向服务器发送请求,接收服务器响应:二在本地运行模型, ...

  6. Atitit.嵌入式web 服务器 java android最佳实践

    Atitit.嵌入式web 服务器 java android最佳实践 1. Android4.4.21 2. 自己的webserver1 3. CyberHTTP for Java  cybergar ...

  7. 基于socket的客户端和服务端聊天简单使用 附Demo

    功能使用 服务端 分离一个不停接受客户端请求的线程 接受不客户端请求的线程中,再分离就收消息的线程 几大对象分别是 IPEndPoint IP终结点 服务端Socket,绑定终结点Bind,启动监听L ...

  8. Android最佳性能实践(三)——高性能编码优化

    在前两篇文章当中,我们主要学习了Android内存方面的相关知识,包括如何合理地使用内存,以及当发生内存泄露时如何定位出问题的原因.那么关于内存的知识就讨论到这里,今天开始我们将学习一些性能编码优化的 ...

  9. Android最佳性能实践(二)——分析内存的使用情况

    由于Android是为移动设备开发的操作系统,我们在开发应用程序的时候应当始终把内存问题充分考虑在内.虽然Android系统拥有垃圾自动回收机制,但这并不意味着我们就可以完全忽略何时去分配或释放内存. ...

随机推荐

  1. mORMot学习笔记 (一)

    官方网站:https://synopse.info/fossil/wiki/Synopse+OpenSource 下载地址:https://synopse.info/fossil/wiki?name= ...

  2. 【坑】在使用EL表达式时表达式无法获取数值

    错误描述: 使用EL表达式 前台原样输出表达式而不输出值 错误环境: idea 2017.1.2 错误原因: jsp页面默认会忽略el表达式,需要设置为不忽略 解决方案 设置<%@ page i ...

  3. python并发编程之线程(二):死锁和递归锁&信号量&定时器&线程queue&事件evevt

    一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将 ...

  4. 启动Tomcat报错:A child container failed during start

    在之前的工作和学习当中,有碰到过很多未知的,在当时看来十分令人疑惑的问题.当时的解决手段简单粗暴,就是直接百度,搜到对应的解决方案,直接抄过来试试,不行再换,直到问题消失,程序可以正常跑了,立马就不管 ...

  5. Spring 注解无效 空指针异常

    对于Java开发,现在Spring已成为一种生态,使用Spring成为一种享受,Spring的使用让开发变得更加便捷. 而Spring好用归好用,若不清楚里面的工作原理,使用的时候难免会踩到一些坑. ...

  6. shell字符串处理

    一.字符串切片: ${#var}:返回字符串变量var的长度${var:offset}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offse ...

  7. mysql5.7安装中的问题(服务无法启动。服务没有报告任何错误。排查方法)

    1.拒绝访问的问题 权限不够,必须以管理员身份启动命令行 2.MySQL 服务无法启动.服务没有报告任何错误. 进入到你的mysql安装目录,C:\Program Files\MySQL\MySQL ...

  8. iOS native plugin 的代码sample

    https://bitbucket.org/Unity-Technologies/iosnativecodesamples/src/stable/ 不在这个stable版本 在2017 dev这个版本

  9. nodejs解析url参数的三种方法

    要解析的url:http://127.0.0.1:8090/?name=cpc&age=21 利用js字符串操作函数进行解析 const myserver = require("ht ...

  10. mysql 主从复制不一致,不停库不锁表恢复主从同步

    注意: 进行此操作时,确认在之前已经开启了MySQL的bin-log日志,如果没有则无法实现 为了安全考虑,我们授权一个用户进行数据备份: [root@7con ] mysql -uroot -p m ...