Android NDK开发之JNI简单应用

JNI

JNI概念:

JNI全称Java Native Interface,Java本地化接口,可以通过JNI调用系统提供的API。操作系统,无论是Linux,Windows还是Mac OS,或者一些汇编语言写的底层硬件驱动都是C/C++写的。Java和C/C++不同,它不会直接编译成平台机器码,而是编译成虚拟机可运行的Java字节码的.class文件。通过JIT技术即时编译成本地机器码,所以有效率就比不上C/C++代码,JNI技术就解决了这一痛点。JNI可以说是C语言和Java语言交流的配适器,中间件。
这里写图片描述

JNI技术通过JVM调用到各个平台的API,虽然JNI可以调用C/C++,但是JNI调用还是比C/C++编写的原生应用还是要慢一些。

JNI与NDK的区别:

JNI:是一套编程接口,用来实现Java代码与本地C/C++代码进行交互
NDK:NDK是Google开发的一套开发和编译工具集,可以生产动态链接库,主要用于Android的JNI开发。

JNI的作用:

  • 扩展:JNI扩展了JVM能力,驱动开发,例如开发一个wifi驱动,可以将手机设置为无限路由;
  • 高效: 本地代码效率高,游戏渲染,音频视频处理等方面使用JNI调用本地代码,C语言可以灵活操作内存;
  • 复用: 在文件压缩算法 7zip开源代码库,机器视觉 OpenCV开放算法库等方面可以复用C平台上的代码,不必在开发一套完整的Java体系,避免重复发明轮子;
  • 特殊: 产品的核心技术一般也采用JNI开发,不易破解;

JNI在Android中作用:

JNI可以调用本地代码库(即C/C++代码),并通过 Dalvik 虚拟机与应用层和应用框架层进行交互,Android中JNI代码主要位于应用层和应用框架层;

  • 应用层: 该层是由JNI开发,主要使用标准JNI编程模型;
  • 应用框架层: 使用的是Android中自定义的一套JNI编程模型,该自定义的JNI编程模型弥补了标准JNI编程模型的不足;

在Android中使用JNI

要使用它,就要先配置它:
首先在SDK Manager中下载NDK,CMake,LLDB
这里写图片描述

然后创建一个JNIUtil的类:

public class JNIUtil {

    static {
        System.loadLibrary("JNITestSample");
    }

    public static native String getWorld();
}

然后在Build菜单栏中,make Project一下
完成之后,会在app/build/intermediates下出现一个classes文件夹。

然后通过Terminal进入classes文件夹下的debug文件夹下,在终端运行:

cd app/build/intermediates/classes/debug/
javah com.example.asus1.learnjniandndk.JNIUtil

//记得找到自己对应的包

如果无法使用javah,提示说javah既不是内部命令也不是外部命令,就是java的环境变量没有配置,配置好java JDK的环境变量后,重启AS或者电脑,就可以用了

运行成功之后打开app/build/intermediates/classes/debug/ 即可找到编译出的头文件"com.example.asus1.learnjniandndk.JNIUtil.h",不难发现头文件名是有原报名+类名组成。

创建jni开发的文件夹
  • 点击app文件夹,New → Folder → JNI Folder, 选择在main文件夹下即可,生成成功后main目录下会出现一个jni的文件夹
  • 找到刚才生成到头文件,复制到jni文件夹下(记得关闭刚才使用的终端,否则无法复制)
  • 头文件有了,现在在jni目录下创建一个C++文件用于开发使用,命名与头文件相同,后缀为.cpp
  • 编写C++文件中定义函数的代码
#include "com_example_asus1_learnjniandndk_JNIUtil.h"
JNIEXPORT jstring JNICALL Java_com_example_asus1_learnjniandndk_JNIUtil_getWorld
        (JNIEnv *env, jclass) {
    // new 一个字符串,返回Hello World
    return env -> NewStringUTF("Hello World ZZZZZ");
}

然后在jni文件中创建Android.mk,和Application.mk这个文件:

//Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNITestSample
LOCAL_SRC_FILES := com_example_asus1_learnjniandndk_JNIUtil.cpp
include $(BUILD_SHARED_LIBRARY)
//application.mk

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions -std=c++0x
APP_ABI := armeabi
APP_ABI := armeabi-v7a
APP_PLATFORM := android-18

然后在终端cd到jni这个文件夹,执行ndk-build

执行完后,会生成,os文件

这里写图片描述

然后在build.gradle中加下面几句:

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.asus1.learnjniandndk"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk {
            moduleName "JNITestSample"
            abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种平台下的so库
            ldLibs "log", "jnigraphics", "android"    //jni中需要用到的其它库
            stl "gnustl_shared"

        }

        sourceSets {
            main {
                jni.srcDirs = []
                jniLibs.srcDirs 'src/main/libs'
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

应该自己会在local.properties下自动添加ndk的路径:

## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Mon Jun 11 13:10:14 CST 2018
ndk.dir=D\:\\SDK\\ndk-bundle
sdk.dir=D\:\\SDK

然后在MainActivity中:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView view = findViewById(R.id.test);
        view.setText(""+new JNIUtil().getWorld());
    }
}

我们就会得到:

这里写图片描述

知识补充:

JNI类型映射:
这里写图片描述

引用数据类型:
这里写图片描述

需要注意的是,

1)引用类型不能直接在 Native 层使用,需要根据 JNI 函数进行类型的转化后,才能使用;
2)多维数组(含二维数组)都是引用类型,需要使用 jobjectArray 类型存取其值;

参考:
https://www.jianshu.com/p/ac00d59993aa
http://www.jcodecraeer.com/plus/view.php?aid=7769

猜你喜欢

转载自blog.csdn.net/qq_36391075/article/details/80657743