日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android NDK学习笔记1:基础

發(fā)布時(shí)間:2024/9/30 Android 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android NDK学习笔记1:基础 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/zhaoyanjun6/article/details/119005718
本文出自【趙彥軍的博客】

文章目錄

  • (一)什么是Android NDK
  • (二)下載NDK
  • (三)什么JNI?
  • (四) JNI的三個(gè)角色
  • (五) JNI的命名規(guī)則
  • (六) 如何實(shí)現(xiàn)JNI
  • (七)JNI原理
    • JNIEnv是什么?
    • JNIEnv和JavaVM的區(qū)別
    • JNIEnv的作用
    • JNIEnv與線程
    • JNIEnv結(jié)構(gòu)
    • 創(chuàng)建Java類中的String對象
  • (八)ABI
  • (九)JNI的引用
    • 1、局部引用(Local Reference)
    • 2、全局引用(Global Reference)
    • 3、弱全局引用(Weak Global Reference)
  • (十)示例
  • (十一)查看Android 項(xiàng)目so
  • (十二)CMakeLists.txt
  • (十三)如何編譯多個(gè)平臺(tái)so

(一)什么是Android NDK

NDK 官網(wǎng):https://developer.android.google.cn/ndk/index.html

Android NDK 是一個(gè)工具集,可讓您使用 C 和 C++ 等語言以原生代碼實(shí)現(xiàn)應(yīng)用的各個(gè)部分。對于特定類型的應(yīng)用,這可以幫助您重復(fù)使用以這些語言編寫的代碼庫。

(二)下載NDK

1、下載 NDK ,下載完成后,下載的目錄在 android sdk 目錄里面

/Users/xmly/Library/Android/sdk/ndk


除此之外,你還可以自由下載其他版本的 ndk ,https://developer.android.com/ndk/downloads/revision_history

2、下載 CMake 工具

3、在 Android Studio local.properties 配置 ndk 目錄,示例如下:

sdk.dir=/Users/xmly/Library/Android/sdk ndk.dir=/Users/xmly/Library/Android/ndk-r19c

(三)什么JNI?

Java調(diào)用C/C++在Java語言里面本來就有的,并非Android自創(chuàng)的,即JNI。JNI就是Java調(diào)用C++的規(guī)范。當(dāng)然,一般的Java程序使用的JNI標(biāo)準(zhǔn)可能和android不一樣,Android的JNI更簡單。

JNI,全稱為Java Native Interface,即Java本地接口,JNI是Java調(diào)用Native 語言的一種特性。通過JNI可以使得Java與C/C++機(jī)型交互。即可以在Java代碼中調(diào)用C/C++等語言的代碼或者在C/C++代碼中調(diào)用Java代碼。

由于JNI是JVM規(guī)范的一部分,因此可以將我們寫的JNI的程序在任何實(shí)現(xiàn)了JNI規(guī)范的Java虛擬機(jī)中運(yùn)行。

同時(shí),這個(gè)特性使我們可以復(fù)用以前用C/C++寫的大量代碼JNI是一種在Java虛擬機(jī)機(jī)制下的執(zhí)行代碼的標(biāo)準(zhǔn)機(jī)制。

代碼被編寫成匯編程序或者C/C++程序,并組裝為動(dòng)態(tài)庫。也就允許非靜態(tài)綁定用法。這提供了一個(gè)在Java平臺(tái)上調(diào)用C/C++的一種途徑,反之亦然。

PS:
開發(fā)JNI程序會(huì)受到系統(tǒng)環(huán)境限制,因?yàn)橛肅/C++ 語言寫出來的代碼或模塊,編譯過程當(dāng)中要依賴當(dāng)前操作系統(tǒng)環(huán)境所提供的一些庫函數(shù),并和本地庫鏈接在一起。而且編譯后生成的二進(jìn)制代碼只能在本地操作系統(tǒng)環(huán)境下運(yùn)行,因?yàn)椴煌牟僮飨到y(tǒng)環(huán)境,有自己的本地庫和CPU指令集,而且各個(gè)平臺(tái)對標(biāo)準(zhǔn)C/C++的規(guī)范和標(biāo)準(zhǔn)庫函數(shù)實(shí)現(xiàn)方式也有所區(qū)別。這就造成了各個(gè)平臺(tái)使用JNI接口的Java程序,不再像以前那樣自由的跨平臺(tái)。

如果要實(shí)現(xiàn)跨平臺(tái), 就必須將本地代碼在不同的操作系統(tǒng)平臺(tái)下編譯出相應(yīng)的動(dòng)態(tài)庫。比如在Android環(huán)境下,編譯成Android平臺(tái)的 .so 庫。在 Ios 環(huán)境下,需要編譯成 Ios 平臺(tái)的動(dòng)態(tài)庫。

(四) JNI的三個(gè)角色

三者的關(guān)系

JNI下一共涉及到三個(gè)角色:C/C++代碼、本地方法接口類、Java層中具體業(yè)務(wù)類。

JNI簡要流程

(五) JNI的命名規(guī)則

隨便舉例如下:

JNIExport jstring JNICALL Java_com_example_hellojni_MainActivity_stringFromJNI( JNIEnv* env,jobject thiz )
  • jstring 是返回值類型
  • Java_com_example_hellojni 是包名
  • MainActivity 是類名
  • stringFromJNI 是方法名

其中JNIExport和JNICALL是不固定保留的關(guān)鍵字不要修改

定義 native 方法:

java 類

native String stringFromJNI();

kotlin 類

external fun stringFromJNI(): String

external 相當(dāng)于 native

(六) 如何實(shí)現(xiàn)JNI

JNI開發(fā)流程的步驟:

  • 第1步:在Java中先聲明一個(gè)native方法
  • 第2步:編譯Java源文件javac得到.class文件
  • 第3步:通過javah -jni命令導(dǎo)出JNI的.h頭文件
  • 第4步:使用Java需要交互的本地代碼,實(shí)現(xiàn)在Java中聲明的Native方法(如果Java需要與C++交互,那么就用C++實(shí)現(xiàn)Java的Native方法。)
  • 第5步:將本地代碼編譯成動(dòng)態(tài)庫(Windows系統(tǒng)下是.dll文件,如果是Linux系統(tǒng)下是.so文件,如果是Mac系統(tǒng)下是.jnilib)
  • 第6步:通過Java命令執(zhí)行Java程序,最終實(shí)現(xiàn)Java調(diào)用本地代碼。

(七)JNI原理

JNIEnv是什么?

JNIEnv是一個(gè)線程相關(guān)的結(jié)構(gòu)體,該結(jié)構(gòu)體代表了Java在本線程的執(zhí)行環(huán)境

JNIEnv和JavaVM的區(qū)別

  • JavaVM:JavaVM是Java虛擬機(jī)在JNI層的代表,JNI全局僅僅有一個(gè)
  • JNIEnv:JavaVM 在線程中的代碼,每個(gè)線程都有一個(gè),JNI可能有非常多個(gè)JNIEnv;

JNIEnv的作用

  • 調(diào)用Java 函數(shù):JNIEnv代表了Java執(zhí)行環(huán)境,能夠使用JNIEnv調(diào)用Java中的代碼
  • 操作Java代碼:Java對象傳入JNI層就是jobject對象,需要使用JNIEnv來操作這個(gè)Java對象

JNIEnv與線程

JNIEnv是線程相關(guān)的,即在每一個(gè)線程中都有一個(gè)JNIEnv指針,每個(gè)JNIEnv都是線程專有的,其他線程不能使用本線程中的JNIEnv,即線程A不能調(diào)用線程B的JNIEnv。所以JNIEnv不能跨線程。

  • JNIEnv只在當(dāng)前線程有效:JNIEnv僅僅在當(dāng)前線程有效,JNIEnv不能在線程之間進(jìn)行傳遞,在同一個(gè)線程中,多次調(diào)用JNI層方便,傳入的JNIEnv是同樣的
  • 本地方法匹配多個(gè)JNIEnv:在Java層定義的本地方法,能夠在不同的線程調(diào)用,因此能夠接受不同的JNIEnv

JNIEnv結(jié)構(gòu)

JNIEnv是一個(gè)指針,指向一個(gè)線程相關(guān)的結(jié)構(gòu),線程相關(guān)結(jié)構(gòu),線程相關(guān)結(jié)構(gòu)指向JNI函數(shù)指針數(shù)組,這個(gè)數(shù)組中存放了大量的JNI函數(shù)指針,這些指針指向了詳細(xì)的JNI函數(shù)。

創(chuàng)建Java類中的String對象

jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len)

通過Unicode字符的數(shù)組來創(chuàng)建一個(gè)新的String對象。
env是JNI接口指針;unicodeChars是指向Unicode字符串的指針;len是Unicode字符串的長度。返回值是Java字符串對象,如果無法構(gòu)造該字符串,則為null。

那有沒有一個(gè)直接直接new一個(gè)utf-8的字符串的方法呢?答案是有的

就是jstring NewStringUTF(JNIEnv *env, const char *bytes)這個(gè)方法就是直接new一個(gè)編碼為utf-8的字符串。

其他方法:

  • NewArray Routines 返回值:Array Type
  • NewBooleanArray() 返回值:jbooleanArray
  • NewByteArray() 返回值:jbyteArray
  • NewCharArray() 返回值:jcharArray
  • NewShortArray() 返回值:jshortArray
  • NewIntArray() 返回值:jintArray
  • NewLongArray() 返回值:jlongArray
  • NewFloatArray() 返回值:jfloatArray
  • NewDoubleArray() 返回值:jdoubleArray

(八)ABI

(九)JNI的引用

Java內(nèi)存管理這塊是完全透明的,new一個(gè)實(shí)例時(shí),只知道創(chuàng)建這個(gè)類的實(shí)例后,會(huì)返回這個(gè)實(shí)例的一個(gè)引用,然后拿著這個(gè)引用去訪問它的成員(屬性、方法),完全不用管JVM內(nèi)部是怎么實(shí)現(xiàn)的,如何為新建的對象申請內(nèi)存,使用完之后如何釋放內(nèi)存,只需要知道有個(gè)垃圾回收器在處理這些事情就行了,然而,從Java虛擬機(jī)創(chuàng)建的對象傳到C/C++代碼就會(huì)產(chǎn)生引用,根據(jù)Java的垃圾回收機(jī)制,只要有引用存在就不會(huì)觸發(fā)該該引用所指向Java對象的垃圾回收。

在JNI規(guī)范中定義了三種引用:局部引用(Local Reference)、全局引用(Global Reference)、弱全局引用(Weak Global Reference)。區(qū)別如下:

1、局部引用(Local Reference)

局部引用,也成本地引用,通常是在函數(shù)中創(chuàng)建并使用。會(huì)阻止GC回收所有引用對象。

最常見的引用類型,基本上通過JNI返回來的引用都是局部引用,例如使用NewObject,就會(huì)返回創(chuàng)建出來的實(shí)例的局部引用,局部引用值在該native函數(shù)有效,所有在該函數(shù)中產(chǎn)生的局部引用,都會(huì)在函數(shù)返回的時(shí)候自動(dòng)釋放(freed),也可以使用DeleteLocalRef函數(shù)手動(dòng)釋放該應(yīng)用。

之所以使用DeleteLocalRef函數(shù):實(shí)際上局部引用存在,就會(huì)防止其指向?qū)ο蟊焕厥掌诨厥?#xff0c;尤其是當(dāng)一個(gè)局部變量引用指向一個(gè)很龐大的對象,或是在一個(gè)循環(huán)中生成一個(gè)局部引用,最好的做法就是在使用完該對象后,或在該循環(huán)尾部把這個(gè)引用是釋放掉,以確保在垃圾回收器被觸發(fā)的時(shí)候被回收。在局部引用的有效期中,可以傳遞到別的本地函數(shù)中,要強(qiáng)調(diào)的是它的有效期仍然只是在第一次的Java本地函數(shù)調(diào)用中,所以千萬不能用C++全部變量保存它或是把它定義為C++靜態(tài)局部變量。

2、全局引用(Global Reference)

全局引用可以跨方法、跨線程使用,直到被開發(fā)者顯式釋放。類似局部引用,一個(gè)全局引用在被釋放前保證引用對象不被GC回收。和局部應(yīng)用不同的是,沒有那么多函數(shù)能夠創(chuàng)建全局引用。能創(chuàng)建全部引用的函數(shù)只有NewGlobalRef,而釋放它需要使用ReleaseGlobalRef函數(shù)

3、弱全局引用(Weak Global Reference)

是JDK 1.2新增加的功能,與全局引用類似,創(chuàng)建跟刪除都需要由編程人員來進(jìn)行,這種引用與全局引用一樣可以在多個(gè)本地方法有效,不一樣的是,弱引用將不會(huì)阻止垃圾回收期回收這個(gè)引用所指向的對象,所以在使用時(shí)需要多加小心,它所引用的對象可能是不存在的或者已經(jīng)被回收。

引用比較

在給定兩個(gè)引用,不管是什么引用,我們只需要調(diào)用IsSameObject函數(shù)來判斷他們是否是指向相同的對象。代碼如下:

(*env)->IsSameObject(env, obj1, obj2)

如果obj1和obj2指向相同的對象,則返回JNI_TRUE(或者1),否則返回JNI_FALSE(或者0)

(十)示例

Java代碼

package com.example.myapplication;public class JniBridge {native String stringFromJNI();native String stringFromJNI2(String message);native double doubleFromJNI(double value);native int sumFromJNI(int value); }

native-lib.cpp

#include <jni.h> #include <string> #include <float.h>extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_JniBridge_stringFromJNI(JNIEnv *env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str()); }extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_JniBridge_stringFromJNI2(JNIEnv *env, jobject thiz,jstring _message) {//獲取 c 語言層面的字符串,會(huì)分配內(nèi)存const char * str = env->GetStringUTFChars(_message, JNI_FALSE);//釋放c風(fēng)格的對象內(nèi)存,一旦釋放,str將沒有用env->ReleaseStringUTFChars(_message,str);//獲取字符串的長度int length = env->GetStringLength(_message);//獲取一段字符串,類似于java的截取char buffer[128];env->GetStringUTFRegion(_message,0,length-1,buffer);//c風(fēng)格的字符串轉(zhuǎn)換成jstringreturn env->NewStringUTF(""); }extern "C" JNIEXPORT jdouble JNICALL Java_com_example_myapplication_JniBridge_doubleFromJNI(JNIEnv *env, jobject thiz, jdouble value) {return value * 3; }extern "C" JNIEXPORT jint JNICALL Java_com_example_myapplication_JniBridge_sumFromJNI(JNIEnv *env, jobject thiz, jint value) {return value * 2; }

(十一)查看Android 項(xiàng)目so

如何查看編譯出來的 so 文件,有兩種方式

第一種在 build 文件夾中查看

第二種反編譯 .apk 文件

(十二)CMakeLists.txt

當(dāng)你創(chuàng)建了一個(gè) JNI Android 工程的時(shí)候,會(huì)自帶創(chuàng)建一個(gè) 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.10.2)# Declares and names the project.project("myapplication")# 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.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp )# 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 )# 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.native-lib# Links the target library to the log library# included in the NDK.${log-lib} )

ADD_LIBRARY()語法

add_library(<name> [STATIC | SHARED | MODULE][EXCLUDE_FROM_ALL]source1 [source2 ...])

:庫的名字,直接寫名字即可。
[STATIC | SHARED | MODULE] :類型有三種。

SHARED,動(dòng)態(tài)庫 STATIC,靜態(tài)庫 source, 資源的相對路徑

當(dāng)一個(gè)類中方法被一個(gè) cpp類實(shí)現(xiàn),source 選項(xiàng)只寫一個(gè)就行。如果是被 兩個(gè) cpp 實(shí)現(xiàn),就需要添加多個(gè) source

舉例:

public class JniBridge {//native2.cpp 實(shí)現(xiàn)native String stringFromJNI();native String stringFromJNI2(String message);native double doubleFromJNI(double value);native int sumFromJNI(int value);//native2.cpp 實(shí)現(xiàn)native int sum2(int value); }

添加多個(gè) source

add_library( # Sets the name of the library.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp native2.cpp )

(十三)如何編譯多個(gè)平臺(tái)so

defaultConfig {applicationId "com.example.myapplication"minSdkVersion 16targetSdkVersion 30versionCode 1versionName "1.0"externalNativeBuild {ndk {abiFilters 'armeabi-v7a', 'arm64-v8a'}}}

總結(jié)

以上是生活随笔為你收集整理的Android NDK学习笔记1:基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。