一、JNI
1.1 创建工程
用 DevEco Studio 创建一个新的工程,我的 DevEco Studio 版本为2.1。创建工程第一步的时候,需要选择模版,请选择 Native C++ 这个模版。这个模版为我们提供了JNI开发的环境,我们在这个模版里可以更容易的使用JNI 去调用C语言代码。
(1) 创建工程第一步:选择 Native C++ 模版,如下图所示:
1.2 编写C语言代码
接下来,我们要做一个简单的计算器,实现最基本的加法、减法、乘法、除法。加减乘除的计算功能用C语言实现,界面控件布局等则用 Java语言实现,然后采用 JNI 的方式去调用C语言里的加减乘除方法。
(1) 在和 MainActivity 同级目录下新建一个 java 类,类名为:JNITools。
package com.example.jnidemo;
public class JNITools {
// 这个链接C语言文件的代码本来是在MainActivity里面,我挪到了这里来了,照做即可。
static {
System.loadLibrary("hello");
}
//自己添加加减乘除四个方法的声明,具体实现在native-lib.cpp文件里。
public static native int addNumber(int a, int b);
public static native int subNumber(int a, int b);
public static native int mulNumber(int a, int b);
public static native int divNumber(int a, int b);
}
(2) 在 hello.cpp 里面实现加减乘除方法。
敲黑板,重点来了。 到了这里,有的同学可能会感觉奇怪,为什么我在JNITools里面声明方法,方法的具体实现却跑到了hello.cpp文件里。因为仔细看上面的代码,是这句代码 System.loadLibrary("hello"); 把这两个文件关联到了一起。在iOS里面有头文件和源文件之分,JNITools.java 就相当于头文件,hello.cpp 就相当于源文件。
打开 hello.cpp,在方法体里面添加自己的业务逻辑代码:
Java_com_example_jnidemo_JNITools_addNumber 这个方法名由:Java+包名+类名+方法名 构成,一定要和自己的对应,否则程序会找不到方法的实现而闪退。
#include <jni.h>
#include <string>
#include <Hilog/log.h>
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_addNumber(JNIEnv* env, jobject obj, int a, int b) {
return a+b;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_subNumber(JNIEnv* env, jobject obj, int a, int b) {
return a-b;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_mulNumber(JNIEnv* env, jobject obj, int a, int b) {
return a*b;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_divNumber(JNIEnv* env, jobject obj, int a, int b) {
return a/b;
}
(2) 运行效果
运行之后,可以在模拟器的屏幕上看到:30
1.4 生成SO库
前面就提到过hello.cpp最终会被编译成so库,提供给普通的鸿蒙程序使用。那么怎么将hello.cpp编译成so库呢?其实当你编译运行之后,系统就自动生成了SO库。SO库的存放目录:entry->build->intermediates->libs。
二、SO库的使用
本篇主要介绍如何在普通的鸿蒙工程中使用编译好的SO库文件。首先,新建一个普通的鸿蒙项目工程,我的工程名叫SOTest。项目工程建好后,就将SO库引入到工程中。
2.1 将SO库引入工程中
DevEco Studio 默认创建了libs目录,所以将之前编译好的SO文件直接拷贝到libs目录中即可。
2.2 引入 JNITools 文件
SO库文件是一个动态库,无法打开,所以我们看不到里面有些什么,那么就有一个问题,如果我们把自己编译好的SO库给别人去用,别人又看不到SO里面的内容,那他怎么调SO库里面的方法呢?所以我们还需要提供一个配套的头文件给使用SO库的开发人员。这个JNITools就是头文件,当然你也可以取其他的名字。这个JNITools在我们上一节已经出现过了,这里还是把代码贴出来:
//这个包名要和原来创建SO库文件工程的包名一样,而不是现在新工程的包名
//因为SO库里面的包名也是 package com.example.jnidemo
package com.example.jnidemo;
public class JNITools {
static {
System.loadLibrary("hello");
}
public static native int addNumber(int a, int b);
public static native int subNumber(int a, int b);
public static native int mulNumber(int a, int b);
public static native int divNumber(int a, int b);
}
在com.example目录下再新建一个包,包名为:jnidemo,然后再引入JNITools文件。
2.3 调用SO文件中的代码
(1) MainAbilitySlice 里添加代码:
import com.example.jnidemo.JNITools;
import com.example.sotest.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Text;
import ohos.agp.utils.Color;
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Text text = (Text)findComponentById(ResourceTable.Id_text_helloworld);
text.setText(" "+ JNITools.addNumber(20,10));
text.setTextColor(Color.BLACK);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
(2) 运行效果
运行模拟器,屏幕上显示 30
至此,就完整的介绍了如何生成SO库文件,以及如何使用SO库文件,你学会了么。
如果你觉得本篇文章对你有用,请给小编一点鼓励,点赞或打赏,感谢。