Android - JNI 指南

3

我想将一个小型、精简和高效的基于C语言的解析器集成到我的Android项目中。我以前做过JNI编程,但没有在Android上进行任何类型的本地(C)开发。我的计划是将C库编译为SO,并创建JNI包装器来使用它。这样做可以吗?第二个也是最重要的问题-我该如何将.so文件包含到我的APK中?它应该放在哪里?

4个回答

5
根据传递的数据量和频率,我认为Java/JNI/C执行效率比本地java实现要快。如果将除“Java Int”以外的任何内容传递给“C long”,则会调用JNI数据转换程序,这些程序并不高效。因此,除非你的JNI程序遵循以下模式: - 传递少量数据。 - 在C中做大量的工作。 - 返回小型结果集。
否则,你的执行速度将远远慢于本地java实现。如果你坚持使用基本的“C”类java操作(+、-、*、==、>)并使用“native”数据类型(int、char、String),避免使用复杂的库,则Java的性能几乎与C相当。Java性能问题的另一个点是启动JVM和使其运行所需的时间,但由于从Java程序开始,这不是问题。Java性能较慢的另一个原因是人们坚持使用不必要的工厂、容器、xml bean等等,而简单的方法可能更好地完成任务。

3
在您的Android根应用程序文件夹(其中包含src、res)中创建一个JNI文件夹。将代码(1)放在那里,并将其命名为someshared-lib.c。
(1)
Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jobject thiz)
{


//your c code , the JNI will act as a wrapper for it

return (*env)->NewStringUTF(env, "<string to pass or you can mention jchar * type string >");

}

(2)在Java文件中

package YourPackageName;

public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{

    public native String  NativeFuntionName();
    String returnValue = NativeFuntionName();


}

(3) 在 Android.mk 文件中进行以下操作:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := someshared-lib // 注意库名称与 c 文件名相同

LOCAL_SRC_FILES := someshared-lib.c // 这是您放置代码的文件 (1)

include $(BUILD_SHARED_LIBRARY)

导出您的 ndk-build(执行 export PATH=:$PATH)并进入上面创建的 JNI 文件夹,执行 ndk-build 命令。这样将会在应用程序根目录中的 lib 文件夹中生成一个名为someshared-lib的库。构建和运行该应用程序时,该库将与 apk 捆绑并安装到设备中。要验证这一点,请前往

/data/data/your_package_name/lib 文件夹。

应用程序在 /data/data/your_package_name/lib(也包括 /system/lib)文件夹中查找此库,并将其用于从 Android 应用程序进行的动态调用(JNI)。如果要返回除字符串以外的任何内容,则必须更改 c 文件中的上述方法声明方式:

Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jclass obj,jobject thiz)
{
jfieldID fid;
jboolean enable_flag;

//Pass the class object having all the variable from the android app to  the JNI in the jclass obj and access its members using the field ID and using Get and Set firld ID.

clazz = (*env)->GetObjectClass(env, info);
fid = (*env)->GetFieldID(env,clazz,"some_variable","X"); //"X" - type of variable for boolean it //is Z , for INtezer it is I, for double it is D,

请参考此文档,了解详细说明。

//for getting teh value fomr the JNI
 enable_flag = (*env)->GetBooleanField(env, **thiz**, fid);


//for setting the value 

fid = (*env)->GetFieldID(env,clazz,"other_float_variable","D");
(*env)->SetDoubleField(env,**thiz**,fid,other_float_variable);



}

在 Android 应用程序中,你需要传递结构体的 Class 对象。 例如: (2)现在变成: 包 YourPackageName;
public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{

    public native String  NativeFuntionName();
    String returnValue = NativeFuntionName( exampleStruct exst);

例子结构体:

public class exampleStruct {

(其中含有HTML标签)
protected boolean  some_variable = 0;//no log saving by default 
protected float  other_float_variable   = 0;

}

}

希望这能帮到你。

2

使用Android NDK
下载 n 文档 Android NDK 1.6

这将使您无需编写 JNI 层来处理 lib,并且将应用程序安装在应用程序数据文件夹的 lib 文件夹中。


你能否评论一下“不编写JNI层”?我需要从我的Java代码中使用C库方法,你是说NDK会自动为我创建一个包装器吗? - Bostone
是的,使用“native”关键字就可以了。这个关键字用于你的库中的函数。它还有一些非常简单的示例。 - bhatt4982

2

现在有点过时了。现在很多东西都更简单了。他将JNI与C#进行比较,但这在Android上并不有用。 - user146043

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接