鸿蒙JNI开发实战 原创 精华

长沙火山
发布于 2021-7-16 10:56
浏览
4收藏

一、JNI

1.1 创建工程

用 DevEco Studio 创建一个新的工程,我的 DevEco Studio 版本为2.1。创建工程第一步的时候,需要选择模版,请选择 Native C++ 这个模版。这个模版为我们提供了JNI开发的环境,我们在这个模版里可以更容易的使用JNI 去调用C语言代码。

(1) 创建工程第一步:选择 Native C++ 模版,如下图所示:

鸿蒙JNI开发实战-鸿蒙开发者社区

(2) 创建工程第二步:填写项目名称、项目存放目录,如下图所示:

  • 建议填写project name和小编的一样,因为后面的一些配置和包名有关。

鸿蒙JNI开发实战-鸿蒙开发者社区

(3) 创建工程第三步:选择语言版本,选择默认的即可,如下图所示:

鸿蒙JNI开发实战-鸿蒙开发者社区

至此,项目创建完毕,项目中CPP目录就是编写C语言代码的地方。整个目录如下图所示:

鸿蒙JNI开发实战-鸿蒙开发者社区

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;
}

至此,我们的C语言代码就写好了。JNITools里的方法是提供给Java代码调用的,hello.cpp里面的代码最终会被编译成so库。

1.3 Java代码调用C语言

下面,就回到了Java这部分,这部分都是大家比较熟悉的,就加快进度直接上代码。

(1) MainAbilitySlice 里添加代码:

package com.example.jnidemo.slice;

import com.example.jnidemo.JNITools;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.PositionLayout;
import ohos.agp.components.Text;
import ohos.agp.utils.Color;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.ComponentContainer.LayoutConfig;

public class MainAbilitySlice extends AbilitySlice {


    private PositionLayout myLayout = new PositionLayout(this);

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        myLayout.setLayoutConfig(config);
        ShapeElement element = new ShapeElement();
        element.setShape(ShapeElement.RECTANGLE);
        element.setRgbColor(new RgbColor(255, 255, 255));
        myLayout.setBackground(element);
        Text text = new Text(this);

				//只有这一句代码需要自己写,测试一下加法的实现
        text.setText(String.format("%d", JNITools.addNumber(10, 20)));

        text.setTextColor(Color.BLACK);
        myLayout.addComponent(text);
        super.setUIContent(myLayout);
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    //public native String stringFromJNI();
}

(2) 运行效果

运行之后,可以在模拟器的屏幕上看到:30

1.4 生成SO库

前面就提到过hello.cpp最终会被编译成so库,提供给普通的鸿蒙程序使用。那么怎么将hello.cpp编译成so库呢?其实当你编译运行之后,系统就自动生成了SO库。SO库的存放目录:entry->build->intermediates->libs。
鸿蒙JNI开发实战-鸿蒙开发者社区

二、SO库的使用

本篇主要介绍如何在普通的鸿蒙工程中使用编译好的SO库文件。首先,新建一个普通的鸿蒙项目工程,我的工程名叫SOTest。项目工程建好后,就将SO库引入到工程中。

2.1 将SO库引入工程中

DevEco Studio 默认创建了libs目录,所以将之前编译好的SO文件直接拷贝到libs目录中即可。

鸿蒙JNI开发实战-鸿蒙开发者社区

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文件。

鸿蒙JNI开发实战-鸿蒙开发者社区

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库文件,你学会了么。

如果你觉得本篇文章对你有用,请给小编一点鼓励,点赞或打赏,感谢。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-7-16 11:06:36修改
4
收藏 4
回复
举报
1条回复
按时间正序
/
按时间倒序
Whyalone
Whyalone

JNI使用的人多么?

回复
2021-7-16 23:46:35
回复
    相关推荐