Android进阶-NDK技术

这篇具有很好参考价值的文章主要介绍了Android进阶-NDK技术。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.介绍

  1.什么是ndk技术?

    在学习ndk技术前,我们需要先了解一下JNI(Java Native Interface)技术,JNI技术是一种实现Java代码和C/C++代码之间交互的技术,它提供了一组编程接口,使得Java程序可以调用C/C++代码并与其进行通信。通过JNI技术,开发者可以将C/C++代码嵌入Java项目中,并在Java代码中调用这些C/C++函数。那么,NDK技术和它有什么关系呢?NDK是一种用于开发Android应用程序的工具集,它允许开发者使用C/C++编写部分或全部的Android应用程序代码,以便提高性能和访问底层系统功能。这样看起来NDK技术和JNI技术是一回事,就是为了实现Java调用C/C++或C/C++调用Java。确实如此,用一句话概括它们之间的关系就是:开发者使用NDK技术在Android应用程序中编写C/C++代码,并将其编译成共享库(如.so文件),然后使用JNI技术在Java代码中加载并与这些C/C++代码进行交互。

  2.为什么要学习ndk?

    第一点的话就是提高性能了,这个显而易见,C/C++的性能肯定比Java高,如果有些功能用Java实现性能不行,就可以把这部分代码用C/C++实现。第二点的话就是C/C++语言可以直接访问底层系统功能和硬件资源,如摄像头和传感器等,这是Java做不到的。最后一点是保密性,Java代码是编译成字节码,而C/C++代码是直接编译成机器码,反编译的难度比Java大的多。所以,如果哪部分功能需要保密,也可以用C/C++来实现。

  3.编写C/C++代码并编译出.so文件

    我们要在Android项目中调用C/C++代码,首先要将写好的C/C++代码编译成.so共享库,下面我会以Android Studio 2021来详细讲解编译出.so文件的过程。

    第一步:打开Android Studio,新建一个Native C++项目,如下图所示:

          Android进阶-NDK技术

    Android进阶-NDK技术

    Android进阶-NDK技术

     项目新建完成后是下面这个样子:

    Android进阶-NDK技术

 

     我们可以看到main目录下面有一个cpp目录,这里就是我们编写C++代码的地方,我们先来看一下自动生成的CmakeLists.txt文件,代码如下:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)   //cmake的最低版本是3.18.1

# Declares and names the project.

project("ndkstudy")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        ndkstudy         //生成的库的名称

        # Sets the library as a shared library.
        SHARED     //设置生成的库为共享库.so

        # Provides a relative path to your source file(s).
        native-lib.cpp     //c++源文件的相对路径
) # Searches
for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) //使用find_library来查找log库,并把找到的log库存储在变量log-lib中 # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. ndkstudy # Links the target library to the log library # included in the NDK. ${log-lib}) //将ndkstudy库和log库进行链接

    然后,我们再来看一下自动生成的native-lib.cpp文件,代码如下:

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

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkstudy_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}//函数的功能就是返回一个"Hello from C++"字符串

    我们发现这个函数名特别长,其实就是对应java目录下com.example.ndkstudy包下MainActivity类下的stringFromJNI()这个函数。了解了这些之后,我们只需要Make Project即可,如下图所示:    

    Android进阶-NDK技术

    然后就可以看到所生成的.so文件了,如果没有的话,可以刷新一下项目  

    Android进阶-NDK技术

    接下来,我们建一个新的项目,然后把上面所生成的不同CPU架构的.so文件复制到新项目的main/jniLibs目录,jniLibs目录需要自己新建。

    Android进阶-NDK技术

     然后在app/build.gradle文件下添加以下的代码:

android {
    namespace 'com.example.ndkstudy'
    compileSdk 32

    defaultConfig {
        applicationId "com.example.ndkstudy"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        ndk {
            // 设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so)
            abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
        }//新增代码
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

    接下来,就可以调用so库中的C++函数了,这里也是特别容易出错的地方,我先贴出代码,然后再详细讲解。

public class MainActivity extends AppCompatActivity {
    private TextView tv_display;
    {
        System.loadLibrary("ndkstudy");//第一步,加载动态库,放到静态代码块里就行
    }

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_display=findViewById(R.id.tv_display);
        tv_display.setText(stringFromJNI());//第三步,调用本地函数,实际上调用的函数是Java_com_example_ndkstudy_MainActivity_stringFromJNI();
    } 
public native String stringFromJNI();//第二步,本地方法声明,也就是说这个方法由c++实现,这里作个声明
}

    一个特别需要注意的点是我们在加载动态库和作本地方法声明的时候,需要在com.example.ndkstudy包下,MainActivity类下进行操作,也就是要对应那个特别长的函数名。如果以上的步骤都没有错的话,就可以在手机屏幕上看到输出的"Hello from C++"字符串了。这只是jni的最基本用法,下面来讨论一下java类型与c类型的转换。

二.Java类型和C类型的转换

  在JNI开发中,Java类型和C/C++类型之间需要转换,因为二者之间的数据类型存在差异,转换的桥梁正是JNI类型。下面给出它们之间的对应关系:

Java类型 JNI类型 C/C++类型 大小
boolean jboolean uint8_t 无符号8位整型
byte jbyte int8_t 有符号8位整型
char jchar uint16_t 无符号16位整型
int jint int32_t 有符号32位整型
short jshort int16_t 有符号16位整型
long jlong int64_t 有符号64位整型
float jfloat float 32位单精度浮点型
double jdouble double 64位双精度浮点型

  这个只是java的基本数据类型和c/c++类型的对应关系,下面给出java的引用类型和c/c++的对应关系:

Java的引用类型 JNI的引用类型
java.lang.Object jobject
java.lang.String jstring
java.lang.Class jclass
Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray
java.lang.Throwable jthrowable
void void

  那么,什么时候需要将java类型转成c类型,什么时候又该将c类型转成java类型呢?当我们所声明的本地函数有形参的时候,我们需要将java类型转成c类型,因为我们在实际调用这个函数的时候,调用的是对应的c函数,所以需要进行转换。当我们调用的本地函数有返回值的时候,需要将c类型转成java类型,因为在调用这个函数之后,返回值需要return到java代码中,所以需要进行转换。下面举个例子来说明一下:

  比如,我们用native关键字声明了一个本地函数public native void javaToJni(byte a,boolean b,int c,short d,long e,char f,float g,double h);那么它实际对应的c函数是:

extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkstudy_MainActivity_javaToJni(JNIEnv *env, jobject thiz, jbyte a, jboolean b,
                                                 jint c, jshort d, jlong e, jchar f, jfloat g,
                                                 jdouble h) {

    //将jni类型转换成c类型
    int8_t c_a=a;
    uint8_t c_b=b;
    int32_t c_c=c;
    int16_t c_d=d;
    int64_t c_e=e;
    uint16_t c_f=f;
    float c_g=g;
    double c_h=h;
  //后续代码
}

  前面两个变量是固定的,env是指向jni环境的指针,thiz是这个函数所属的java对象的引用,后面的参数就是自己实际定义的参数。注意,这些jni变量需要转换成c类型才能进行后续的操作。

  如果本地函数的声明是这个样子呢?public native String test(String str);其实这个函数对应的c函数是:

extern "C" JNIEXPORT jstring JNICALL   //jstring表示返回值为jstring类型
Java_com_example_ndkstudy_MainActivity_test(JNIEnv *env, jobject thiz, jstring str) {
    // TODO: implement test()
    const char *p=env->GetStringUTFChars(str, nullptr);//将jstring类型转换成c中的const char *类型
    return env->NewStringUTF(p);//将const char *类型转换成jstring类型
}//所以这个函数的功能就是返回传进来的字符串

  如果返回值是其他的类型,也和这个类似。比如,如果要返回一个int16_t类型,则函数的返回值类型设为jshort即可。

 

 

 


  


     

 

 

  文章来源地址https://www.toymoban.com/news/detail-576607.html

到了这里,关于Android进阶-NDK技术的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 5G到来未来五年,做纯应用的Android开发者如何学习通过音视频破局?附《578页NDK七大模块学习宝典》(1)

    “只懂应用层,目前越来越多的企业要求音视频技术,我该怎么办?” “到底该不该学习音视频,怎么抉择?\\\" “想从事音视频开发,该怎么入门?如何进阶 ?” 如果你有这样的问题,从侧面反映了你是一个积极向上,想不断努力来提升自己的人。 先从一个简单的问题聊起

    2024年04月23日
    浏览(44)
  • 【Android进阶篇】Android中PreferenceScreen的作用和详细用法介绍

    1, PreferenceScreen的作用 在Android开发中,PreferenceScreen是一个非常重要的布局控件,主要用于创建设置界面(settings page)。它可以包含多个Preference子项,如CheckBoxPreference, ListPreference等,用于设置应用程序的各种选项。 以下是一些关于PreferenceScreen的详细使用说明: 1, 创建设

    2024年02月21日
    浏览(61)
  • 【Android -- JNI 和 NDK】认识 NDK

    定义 :Native Development Kit ,是 Android 的一个工具开发包。 NDK 是属于 Android 的,与 Java 并无直接关系 作用 :快速开发 C、 C++ 的动态库,并自动将 so 和应用一起打包成 APK 即可通过 NDK 在 Android 中 使用 JNI 与本地代码(如C、C++)交互 应用场景 :在 Android 的场景下使用 JNI 。

    2024年02月11日
    浏览(34)
  • Android基础到进阶UI祖父级 ViewGroup介绍+实用

    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果是warp_content情况下,记录宽和高 int width = 0; int height = 0; //记录每一行的宽度,width不断取最大宽度 int lineWidth = 0; //每一行的高度,累加至h

    2024年04月16日
    浏览(33)
  • Android架构进阶之高级UI系列(精编解析,值得收藏),Android开发面试技能介绍

    CallbackRecord callbacks; synchronized (mLock) { final long now = System.nanoTime(); // 根据指定的类型CallbackkQueue中查找到达执行时间的CallbackRecord callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; if (callbackType == Choreograph

    2024年04月13日
    浏览(33)
  • Android NDK开发详解之ndk-build 脚本

    ndk-build 脚本使用 NDK 的基于 Make 的构建系统构建项目。我们针对 ndk-build 使用的 Android.mk 和 Application.mk 配置提供了更具体的文档。 内部原理 运行 ndk-build 脚本相当于运行以下命令: $GNUMAKE 指向 GNU Make 3.81 或更高版本, 则指向 NDK 安装目录。您可以根据这些信息从其他 Shell 脚

    2024年02月06日
    浏览(32)
  • android使用ndk开发

    ndk sdk要单独下载和android sdk不同 https://developer.android.google.cn/ndk/downloads?hl=zh-cn 解压后添加ndk路径到path即可 gradle下载太慢使用国内镜像 distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-6.7.1-all.zip 执行gradlew.bat gradlew //检查环境,下载gradle依赖项 gradlew tasks //查看可执行任务 gr

    2024年01月17日
    浏览(36)
  • Android集成OpenCV(NDK)

    1.下载OpenCv的动态库(.so) OpenCv官网,这边下载的是4.6.0 2.解压opencv-4.6.0-android-sdk.zip 复制目录 opencv-4.6.0-android-sdkOpenCV-android-sdksdknativejni 的 include 文件夹,和 opencv-4.6.0-android-sdkOpenCV-android-sdksdknativelibsarmeabi-v7a 目录对应的cpu架构类型.项目没有对应目录,手动创建一下. 3.配置

    2024年02月06日
    浏览(24)
  • Android NDK开发详解之Android.mk探秘

    本页介绍了 ndk-build 所使用的 Android.mk 构建文件的语法。 概览 Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。它实际上是一个微小的 GNU makefile 片段,构建系统会将其解析一次或多次。Android.mk 文件用于定义 Application.mk、构建系统和环境变量

    2024年02月06日
    浏览(46)
  • Android NDK静态注册 动态注册

     1、jni:java native interface     作用:用于java代码和C++、c代码的交互(代码混编);     分类使用:Jni静态注册、jni动态注册 2、静态注册     1).绑定java方法和C/C++方法的方式之一;     2).java层操作和c层操作以及整体编译             1-定义被native修饰的方法        

    2024年02月16日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包