详解AndroidStudio JNI +Gradle3.0以上JNI爬坑之旅

yizhihongxing

下面我将详细讲解“详解AndroidStudio JNI +Gradle3.0以上JNI爬坑之旅”的完整攻略。

什么是JNI

JNI(Java Native Interface),即Java本地接口,是Java提供的一种机制,允许Java代码和本地代码(如C/C++代码)进行交互。可以让Java程序调用C/C++函数,也可以让C/C++程序调用Java函数。在Android开发中,JNI可以用于实现一些对性能要求比较高的操作。

JNI的开发流程

JNI的开发流程主要包括以下几个步骤:

  1. 定义Java接口和实现类
  2. 编写对应的C/C++接口和实现代码
  3. 使用Java Native Interface Development Kit (JDK)提供的工具将C/C++代码编译成可以与Java代码交互的动态库
  4. 在Java代码中加载动态库并调用对应的C/C++接口

接下来,我将详细介绍如何在Android Studio中使用JNI。

Android Studio中使用JNI

Android Studio自带的Native Development Kit(NDK)可以让我们很方便地开发JNI代码。

配置NDK

我们需要在Android Studio配置NDK,以便能够编译C/C++代码。具体步骤如下:

  1. 打开build.gradle文件,在defaultConfig节点下添加如下配置,指定最低的支持的ndk版本:

ndk {
abiFilters "armeabi-v7a", "x86"
// 支持ndk版本
ndkVersion "21.3.6528147"
}

  1. 在local.properties文件中添加以下NDK路径配置:

ndk.dir=/path/to/ndk

  1. 在gradle.properties文件中配置下面这一行,表示编译时使用C++11:

android.useDeprecatedNdk=true

新建JNI项目

在Android Studio中新建一个Android项目后,我们可以直接在该项目中使用JNI,并调用C/C++代码。

  1. 在src/main目录下新建一个jni目录。这个目录中的文件将会被编译成动态库。

  2. 在jni目录下创建一个名为Android.mk的文件。这个文件是用来描述我们需要用NDK编译的源文件的。如下:

```
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := native-lib
LOCAL_SRC_FILES := native-lib.cpp
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)
```

以上是一个简单的Android.mk文件的内容。它告诉NDK编译器我们需要编译一个动态库(LOCAL_MODULE),源文件是native-lib.cpp(LOCAL_SRC_FILES),并且需要连接log库(LOCAL_LDLIBS)。

  1. 在jni目录下创建一个名为Application.mk的文件。这个文件用来描述我们需要使用的NDK版本和CPU架构。如下:

APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-14

以上的Application.mk文件告诉NDK编译器编译出来的动态库可以运行在armeabi-v7a和x86架构的CPU上,并且需要使用Android 4.0以上版本的API。

  1. 在build.gradle文件中,添加以下配置:

```
android {
...
defaultConfig {
...
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}
}

   externalNativeBuild {
       ndkBuild {
           path "src/main/jni/Android.mk"
       }
   }

}
```

使用JNI

在使用JNI时,需要通过JNI接口来实现Java代码与C/C++代码之间的交互。

我们可以通过Java中的native关键字声明一个本地方法。如下:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    public native String stringFromJNI();

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

        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }
}

以上代码是在MainActivity中声明了一个native方法stringFromJNI(),该方法返回一个String类型的值。在Activity的onCreate方法中调用了这个方法,并将返回值设置到TextView中。

接下来,我们需要在native-lib.cpp文件中实现该方法。具体代码如下:

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

以上代码中,在文件头部包含了jni.h和string头文件。接着,外部C函数Java_com_example_myapplication_MainActivity_stringFromJNI实现了该方法,并返回了一个字符串。我们可以在该函数中实现一些对C/C++更友好的操作。

示例1:复制文件

下面我们来看一个更加实际的例子,假设我们有一个文件在Android设备上,我们要把这个文件复制到另一个位置。虽然Android本身也提供了一些操作文件的API,但是这些API在处理大文件时可能会存在性能瓶颈。这时候,我们可以使用C/C++代码来实现文件复制操作。

在Java层,我们可以定义如下方法:

public native int copyFile(String inputFile, String outputFile);

在native-lib.cpp文件中,我们可以实现这个方法:

#include <jni.h>
#include <string>
#include <fstream>
#include <android/log.h>

#define TAG "copyFile"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_copyFile(
        JNIEnv *env,
        jobject thiz,
        jstring j_input_file,
        jstring j_output_file) {

    const char *inputFile = env->GetStringUTFChars(j_input_file, NULL);
    const char *outputFile = env->GetStringUTFChars(j_output_file, NULL);

    std::ifstream input(inputFile, std::ios::binary);
    std::ofstream output(outputFile, std::ios::binary);

    if (!input.is_open()) {
        LOGE("Failed to open input file: %s", inputFile);
        return -1;
    }

    if (!output.is_open()) {
        LOGE("Failed to open output file: %s", outputFile);
        input.close();
        return -2;
    }

    char buf[1024*1024];
    while(input) {
        input.read(buf, sizeof(buf));
        output.write(buf, input.gcount());
    }

    input.close();
    output.close();

    env->ReleaseStringUTFChars(j_input_file, inputFile);
    env->ReleaseStringUTFChars(j_output_file, outputFile);

    return 0;
}

以上代码中,我们在native函数中使用了标准的C++文件操作API,将一个文件复制到另一个文件中,并返回操作结果。

示例2:OpenCV图像处理

使用JNI可以方便地将OpenCV库映射到Java层,从而方便进行图像处理。

在Java层中,我们可以定义如下方法:

public native void imageProcess(Bitmap bmpIn, Bitmap bmpOut, int threshold);

其中,bmpIn是输入的Bitmap对象,bmpOut是输出的Bitmap对象,threshold是图像处理中的一个阈值,表示灰度图像中的阈值。在实际使用时,我们需要遍历Bitmap对象中的每个像素进行图像处理。

在native-lib.cpp文件中,我们可以实现这个方法:

#include <jni.h>
#include <opencv2/opencv.hpp>
#include <android/log.h>
#include <android/bitmap.h>

#define TAG "imageProcess"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_imageProcess(
        JNIEnv* env,
        jobject thiz,
        jobject bmp_input,
        jobject bmp_output,
        jint threshold) {

    // 将输入和输出的Bitmap转换成Mat对象
    AndroidBitmapInfo info_in = {0};
    void* pixels_in;
    if (AndroidBitmap_getInfo(env, bmp_input, &info_in) < 0) {
        LOGE("Failed to get input bitmap info");
        return;
    }
    if (AndroidBitmap_lockPixels(env, bmp_input, &pixels_in) < 0) {
        LOGE("Failed to lock input bitmap pixels");
        return;
    }
    cv::Mat mat_in(info_in.height, info_in.width,
                   info_in.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ?
                   CV_8UC4 : CV_8UC2,
                   pixels_in);

    AndroidBitmapInfo info_out = {0};
    void* pixels_out;
    if (AndroidBitmap_getInfo(env, bmp_output, &info_out) < 0) {
        LOGE("Failed to get output bitmap info");
        return;
    }
    if (AndroidBitmap_lockPixels(env, bmp_output, &pixels_out) < 0) {
        LOGE("Failed to lock output bitmap pixels");
        return;
    }
    cv::Mat mat_out(info_out.height, info_out.width,
                    info_out.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ?
                    CV_8UC4 : CV_8UC2,
                    pixels_out);

    // 图像处理
    cv::cvtColor(mat_in, mat_out, cv::COLOR_RGBA2GRAY);
    cv::threshold(mat_out, mat_out, threshold, 255, cv::THRESH_BINARY);

    // 解锁Bitmap
    AndroidBitmap_unlockPixels(env, bmp_output);
    AndroidBitmap_unlockPixels(env, bmp_input);
}

以上代码中,我们使用OpenCV库对输入的Bitmap进行灰度化和二值化处理,并将处理后的结果保存到输出的Bitmap中。

如果您还有任何关于JNI或Android Studio的问题,欢迎在评论区留言。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解AndroidStudio JNI +Gradle3.0以上JNI爬坑之旅 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • 腾讯QQ8.4(18357)PC正式版发布:加入群日历、演示白板两个新功能

    腾讯QQ8.4(18357)PC正式版发布攻略 腾讯QQ8.4(18357)PC正式版发布了,这个版本加入了两个新功能:群日历和演示白板。下面是详细的攻略,让我们一起来了解吧! 群日历功能 群日历功能可以帮助你更好地组织和安排群内的活动和事件。你可以在群聊界面中找到群日历入口,点击进入后,你可以看到群内的所有活动和事件的安排。 示例说明1:创建群活动 你可以…

    other 2023年8月3日
    00
  • EXCEL坐标轴怎么自定义设置?

    EXCEL中的坐标轴可以自定义设置,包括调整坐标轴刻度、坐标轴标签、坐标轴位置等。下面,我们将提供详细的攻略指导。 一、自定义设置坐标轴 1.1 调整坐标轴刻度 首先,右键单击图表中的坐标轴,选择格式化坐标轴选项。在弹出的格式化轴选项中,可以调整刻度尺寸、主刻度和次刻度之间的间距等。 示例1:调整坐标轴主刻度和次刻度之间的间距 在图表中选择一个坐标轴,右键单…

    other 2023年6月25日
    00
  • 通过a标签(不丢失referrer)打开另一个窗口

    通过a标签(不丢失referrer)打开另一个窗口 在网站开发中,我们常常需要在页面中设置外链,让用户可以访问相关网站。但有时候我们又希望用户可以在不离开当前页面的情况下访问其他网站。这时候就需要使用a标签的目标属性(target)来控制链接的打开方式。 在a标签中可以设置target属性,该属性可以有以下几种不同的值: _blank:在新窗口中打开链接 _…

    其他 2023年3月28日
    00
  • ASP.NET DropDownList控件的使用方法

    ASP.NET DropDownList控件的使用方法 1. DropDownList控件简介 DropDownList控件是ASP.NET Web Forms中常用的控件之一,它可以创建类似HTML Select标记的下拉列表,并且可以与数据源绑定,使得下拉列表的选项由数据源提供。 2. DropDownList控件的基本用法 我们可以使用DropDown…

    other 2023年6月26日
    00
  • Objective-C中使用NSString类操作字符串的方法小结

    Objective-C中使用NSString类操作字符串的方法小结 Objective-C中的NSString类提供了许多方法来操作字符串。下面是一些常用的方法和示例说明: 1. 创建字符串 可以使用以下方法来创建字符串: NSString *str1 = @\"Hello, World!\"; // 直接使用字符串字面量创建 NSStr…

    other 2023年8月18日
    00
  • html如何禁止文本框输入

    HTML如何禁止文本框输入攻略 在HTML中,我们可以使用一些属性和JavaScript代码来禁止文本框输入。以下是一个完整的攻略,介绍如何中禁止文本框输入。 步骤1:使用readonly属性 我们可以使用readonly属性来禁止文本框输入。以下是一个示例: <input type="text" value="Hello…

    other 2023年5月9日
    00
  • Eclipse如何导入web项目 Eclipse导入web项目详细攻略教程

    下面是详细的攻略教程: 1. 下载并安装Eclipse 首先,你需要在官网上下载 Eclipse 安装包,下载地址为:https://www.eclipse.org/downloads/ 下载完成后,按照安装向导进行安装。 2. 创建动态Web项目 在 Eclipse 中,创建 Web 项目是非常简单的。打开 Eclipse 并选择“File” -> …

    other 2023年6月27日
    00
  • TCP/IP协议栈与数据包封装图文教程

    TCP/IP协议栈是计算机网络通信的基础协议之一,它定义了数据在网络中传输的规范和过程。相比较而言,数据包封装则是TCP/IP协议栈的基础,它描述了数据包在发送和接收过程中的封装过程。因此,如果你想深入理解计算机网络通信的相关规范和过程,那么你需要掌握TCP/IP协议栈和数据包封装的相关知识。本篇文章将为你详细讲解TCP/IP协议栈与数据包封装的完整攻略,同…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部