在Android上从Java调用C ++类方法或函数,而无需在每次调用时重新创建类/变量

时间:2017-03-01 09:12:45

标签: java android c++ android-ndk java-native-interface

我有一个非常复杂的在C ++上编写的NLP代码,现在我想在我的Android应用程序中使用它。我的想法是使用客户端 - 服务器架构构建应用程序,其中客户端是android java应用程序,服务器是C ++面向对象的代码。但对我来说主要的问题是C ++组件非常繁重(标记器,矢量化器,语料库数据等)并且我不能在每次调用时重新创建它们,我需要的是创建它们一次(在第一次请求时),然后将它们放在内存中,当应用程序处于活动状态时,来自java端的handlind请求。

你能不能给我一些如何实现这一点的指导,因为我之前从未使用过Android的C ++?提前致谢。

Java和C ++代码不是远程的,没有涉及http或其他远程请求,它是相同的应用程序。但是为了减少Java / C ++通信间的开销,我不想在java代码中有一个单一的联系点,比如NlpEngine.sendRequest(string textToProcess, int processingWorkflowId),然后是基于processingWorkflowId的C ++代码,它应该是什么做文字。

1 个答案:

答案 0 :(得分:4)

Assumtion

我假设您使用Android NDK等工具集为Android编译了C ++代码,并且前端Java应用程序与后端C ++代码在同一设备上运行。

Java本机呼叫和会话状态

无论您使用以下三个界面中的哪一个。加载C / C ++库并在Java应用程序运行的整个时间内保留在内存中。图书馆的状态将一直保存。我没有清楚地了解你对初始化的担忧。它不像在CLI上启动命令,其他应用程序一次又一次地启动和停止。

  

...但对我来说主要的问题是,C ++组件非常繁重(标记器,矢量化器,语料库数据等),我无法在每次调用时重新创建它们......

使用Java加载本机库

有一些方法可以用Java加载本机库,我想指出三点:

  • JNI - Java Native Interface

    JNI是一个与Java本身一样古老的工具。来自SWT的系统调用是使用JNI创建的。 JNI不是简单的方法,您必须将要在Java中创建的函数/方法定义为本机,将它们编译为C / C ++标头,编写用于在此中间库中调用库的代码。为每个ABI平台(There are 7 Androids ABIs according to NDK)编译此代码。在Java程序中,您找到了调用ABI合适的中间库的平台。 JNI还有许多其他陷阱。

  • JNA - Java Native Access

    [JNA]是一个通过反射动态加载C / C ++库的java库。 Java调用的定义是在纯Java中完成的,没有额外的工具。易用性在性能上有所折衷,调用函数的开销比JNI慢10倍。但是,如果你经常调用极短的C / C ++库函数/方法,那只会起作用。在你的情况下,听起来你只需要调用一些函数,这些函数将需要时间来运行。 JNA由Android开发人员使用。

  • JNR-FFI - Java Native Runtime

    JNR-FFI是最新的候选人。 JNR-FFI是用于调用库的部分。其他部分是JNR-FFI的基线。它受到了JNA的启发。使用JNA和JNR-FFI开发的Java代码看起来非常相似。 JNR-FFI开发的原因是性能。一般来说,JNR-FFI比JNI慢一点(对于getpid大约15%)但在某些情况下它可能比JNI快。对于C / C ++端的长运行时,这种开销并不重要。 JNR被建议在Java 9中作为标准库JEP 191: Foreign Function Interface。警告:JNR-FFI接缝会出现Android问题。

个人经历

我必须从Java实现一些本机库调用。我从JNI开始,经过一周的测试研究后,只有很少的演示线有效。每次尝试都需要很长时间,不同的工具,很长的建设时间。我找到了关于JNI陷阱的演示文稿以及必须考虑的副作用。 ......

我找到了JNA。在几个小时内,我的库的第一个演示代码工作,并与JNI相比,它是如此轻量级和快速开发。我切换到JNR-FFI,因为它是JNA的现代版本。对于我所做的,性能不是转换的原因,这只是一个积极的副作用。

我没有使用Android下的JNA或JNR-FFI进行开发。但在你的情况下,我会尝试JNA,因为它更好地支持Android。我希望JNR-FFI能够在Java 9中实现,从JNA代码切换到JNR-FFI代码很容易。

实施例

我添加了JNR-FFI的示例,让您了解易用性。没有什么比实现这几行更能够调用本机c库函数了。

JNR-FFI取自Java Native Runtime - The Missing Link by Charles Oliver Nutter

的例子
public class GetPidJNRExample {
    public interface GetPid {
        @IgnoreError
        long getpid();
    }

    public static void main( String[] args ) {
        LibraryLoader<GetPid> loader =
            FFIProvider
                .getSystemProvider()
                .createLibraryLoader(GetPid.class);

        GetPid getpid = loader.load("c");

        getpid.getpid();
    }
}