Android NDK基礎介紹及例子

来源:https://www.cnblogs.com/zsc02/archive/2023/08/24/17654719.html
-Advertisement-
Play Games

# [TOC] [Android開發中的NDK到底是什麼?(詳細解析+案例) - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/415536928) # NDK介紹 **(1)簡介** **定義:**`Native Development Kit`,是 ` ...


目錄

Android開發中的NDK到底是什麼?(詳細解析+案例) - 知乎 (zhihu.com)

NDK介紹

(1)簡介

定義:Native Development Kit,是 Android的一個工具開發包

  • NDK是屬於 Android 的,與Java並無直接關係

作用:快速開發CC++的動態庫,並自動將so和應用一起打包成 APK

  • 即可通過NDKAndroid中 使用JNI與本地代碼(如C、C++)交互

應用場景:Android的場景下 使用JNI

  • Android開發的功能需要本地代碼(C/C++)實現

java調用c的步驟

  1. package com.fs.test;
    public class HelloWorld {
    //本地方法
    	private native void show();
    	public static void main(String[] args)
    	{
    		System.loadL ibrary("wgr");
    		new HelloWorld().show();
    	}
    }
    

    1.System.loadL ibrary("wgr");載入動態庫

    2.private native void show();聲明一個動態庫對應的本地方法調用libwgr.so中的函數
    *void Java_ com_ fs_ test HelloWorld_ show(JNIEnv env, jobject thiz)

    3.直接調用show函數(動態庫中與show對應的函數)

一、NDK/JNI

NDK

NDK(Native Development Kit)-原生開發工具包,使得能夠在Android上
去使用C/C++代碼;

JNI

JNI即Java Native Interface,Java和Native介面,就是Java和C/C++之間通訊的橋梁; 為什麼要有JNI,因為Java和C/C++之間是無法直接相互調用的,也就是無法直接通訊,就和Java和JS之間也不能直接相互調用,中間需要翻譯者來處理,JNI就是為了實現Java和C/C++之間這兩種不同語言之間的通訊的;
JNI並不是Android中的技術,它是Java本身就具有的功能,是由JVM支持JNI功能

-那麼為什麼需要在Android上去使用C/C++代碼?

因為我們知道開發Android我們是使用Java/Kotlin語言進行開發的,那麼不論是Java還是Kotlin,最終都是編譯成位元組碼文件.dex文件,然而位元組碼文件是Java/Kotlin經過一定語義編譯之後的產物,並不是完全可以運行的機器碼指令;所以運行應用時還是需要進一步的編譯成機器碼才能夠執行;
而C/C++是完全的編譯型語言,編譯之後是機器碼文件,運行應用時,可以直接執行的,所以效率肯定是要比Java/Kotlin要高的;所以一些對一些比較耗性能的操作,如音視頻編解碼,圖像處理等操作,最好還是交由C/C++代碼中去執行,這樣能夠大大提高應用的執行效率;

NDK開發

新建一個Native項目

Android Studio 新建一個項目,新建項目時,項目模板選擇Native C++
新建項目成功後,我們可以看一下項目的目錄結構;

在src/main目錄下 有一個cpp目錄,與java目錄同級,cpp目錄下有一個 CMakeLists.txt文件和native-lib.cpp
CMkeLists.txt就是CMake腳本文件
native-lib.cpp 是C++源文件

app Module build.gradle中會有一些NDk相關的配置:

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.example.nativec'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.nativec"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.22.1'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

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

設置調試模式

在Run/Edit Configuration/Debug Configurations中 Debugger 調試有四個選項
Auto、 自動檢測
Java、 只調試Java代碼
Native、 只調試Native代碼
Dual、 Java+Native兩種都用
註意不要選擇Java和Native,選擇Auto或者Dual

編寫編譯腳本文件CMakeLists.txt

CMake是一個跨平臺的C++編譯工具,所謂編譯就是把C++代碼編譯成靜態庫或者動態庫
CMakeLists.txt就是CMake的編譯腳本文件,
CMake編譯C++是通過CMakeLists.txt編譯腳本來進行編譯的;
CMakeLists.txt常用編譯腳本

 (1)include_directories(dir_path) 

   //設置頭文件的查找目錄,可以使用多次include_directories設置多個頭文件

                         //查找目錄

   舉例: include_directories(${CMAKE_CURRENT_SOURCE_DIR}/giflib)
   
   (2)add_library(編譯生成的庫名,

                  編譯生成庫的類型(STATIC/SHARED),

                  編譯的庫所使用的C/C++源文件(.c/.cpp文件)(可能會有很多) 
                  .
                  .
                  .

                  )  //指定編譯生成庫

      舉例:add_library(

             native-lib

             SHARED

             native-lib.cpp)   
      
            上面這個add_library就指定了編譯生成的庫的名字為native-lib,庫的類型為

            動態庫,庫所使用或者依賴的所以C/C++源文件只有一個native-lib.cpp

            這裡編譯native-lib庫只使用了一個C/C++源文件native-lib.cpp,但是大多情況下,
            
            編譯庫,可能會使用依賴到很多C/C++源文件;               
            
            如:

            add_library(

             native-lib

             SHARED

             native-lib.cpp,

             a.cpp,

             b.cpp
             ....

             ) 

            這樣把所有使用到的C/C++源文件一個個寫進來是可以的,但是當非常多的時候,就很容易寫漏,

            而且也很麻煩; 可以使用file() 這個腳本,來設置C/C++的查找路徑的別名,最終使用這個查找路徑

            別名,就可以替代寫這個路徑下的所有C/C++源文件了
            

    (3) file(GLOB 別名 某路徑下某種類型文件) 查找某路徑下所有某種類型文件
        
        舉例:

        #設置查找GifLib庫實現源文件(.c)文件目錄
        file(GLOB giflib_C_DIR ${CMAKE_CURRENT_SOURCE_DIR}/giflib/*.c)

        #設置自己編寫的C++實現源文件(.CPP)查找目錄
        file(GLOB CSUTOM_CPP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)

        然後上面說到的add_library()設置編譯生成庫依賴很多C/C++源文件的是偶,就可以使用

        file()設置C/C++源文件查找別名

         add_library(

             native-lib

             SHARED

             ${CSUTOM_CPP_DIR}
             
             ${giflib_C_DIR}

             ) 

  (4) target_link_libraries(生成庫的名字,

      生成庫所鏈接的或者說是使用到的其他動態庫或者靜態庫

      ) //設置生成庫使用到的所有其他動態庫或者靜態庫

      舉例:
      
      target_link_libraries(
        
        native-lib

        a

        b
        ....
        
        )       
      
      生成庫使用到了哪些其他動態庫或者靜態庫都要寫進來,如果很多的情況下一個個寫就很麻煩了

      和add_library()寫使用到的所有C/C++源文件一樣,也可以藉助file()來設置使用到的動態庫或者

      靜態庫文件查找路徑別名;

      #查找所有so庫
      file(GLOB SO_DIR ../../../libs/${CMAKE_ANDROID_ARCH_ABI}/*.so)

      target_link_libraries( # Specifies the target library.

                       native-lib

                       ${SO_DIR}
                       
                        )

 (5) set(變數名  變數值) //用於定義一個變數指代後面的變數值

     比如但我們需要使用一個目錄路徑的時候,這個目錄路徑很長,並且在CMakeLists.txt中

     多個地方使用到,我們可以通過set()來定義一個變數指代這個目錄路徑,後面在使用到這個

     路徑的時候,使用這個簡單的變數來替代;
     
     set(LINK_DIR ../../../../libs/${CMAKE_ANDROID_ARCH_ABI})

     file(GLOB SO_DIR ${LINK_DIR}/*.so)


  (6)find_library(庫的別名 庫名) //從系統查找依賴庫

      find_library( 
        log-lib
        log)

      find_library(
        jnigraphics-lib
        jnigraphics
      ) 

       target_link_libraries( # Specifies the target library.

                       native-lib

                       ${SO_DIR}

                       ${log-lib}
                       
                       ${jnigraphics}
                       
                        )

     log和jnigraphics都是NDK中自帶的動態鏈接庫

新建Native項目後,CMakeLists.txt預設已經有了一些一些基本配置,可以讓我們正常編譯使用native-lib.cpp了;

# 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.22.1)

# Declares and names the project.

project("nativec")

# 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.
        nativec

        # 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.
        nativec

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

載入C/C++庫

Android 系統 API提供了兩種載入C/C++庫的方式:
一種是直接載入APK中的C/C++庫文件;
第二種是通過文件地址來載入C/C++庫文件;

(1)載入APK中的C/C++庫文件

庫文件可能是動態庫so庫,或者是靜態庫.a,或者是C/C++源文件
載入方式:

 static {
        System.loadLibrary("native-lib"); //註意不要寫庫文件擴展名
   }

(2)載入外部的C/C++ so庫

如SD卡下的C/C++庫 ,這裡要註意的是,如果是載入外部的庫文件,只能載入動態庫so庫,並且這個庫文件的位置只能在/system/lib目錄下,或者是在應用的安裝包目錄下/data/data/packagename/,
而且這兩個路勁又是有許可權保護的不能直接訪問,所以一般我們是將庫文件複製到我們應用自己的應用目錄下/data/data/packagename/com.xxx.xxx

  System.load(" C/C++庫文件的絕對路徑(註意不需要寫庫文件擴展名)");

實現Java和C++互相調用

因為我們新建的項目模板是Native C++,所以項目中已經自動為我們創建了一個native-lib.cpp,C++源代碼文件,MainActivtiy中也聲明瞭一個native方法與native-lib.cpp中對應的native方法對應,並且在MainActivtiy中調用了native方法;
來演示Java中調用C/C++方法

下麵我們來分析一下native方法:

Native方法中的包名要和Java方法中的包名對應上,Native層中JNI方法命名格式

為Java_包名類名方法名 之間全部是下劃線分隔 :

Java_com_nado_jniproject_MainActivity_stringFromJNI

參數:JNIEnv *env和jobject thiz

這個jobject類型的thiz就是對應外層的Java對象 jobject就是Java中的Object

Java類型 C類型 以及 JNI別名 對應如下:

Java 類型	JNI 別名  	    C 類型
boolean	    jboolean	    unsigned char
byte	    jbyte	        signed char
char	    jchar	        unsigned short
short	    jshort	        short
int		    jint             int
long	    jlong	        long
float	    jfloat	        float
double	    jdouble	        double
String	    jstring	        char*
Class	    jclass	        /
Object	    jobject	        /

由於Java和C語言之間是無法直接調用的,兩種語言的基本類型是不一樣的,例如Java中有boolean類型,而在C中就沒有這種類型,但是C語言還是有if-else判斷的,那怎麼判斷true或者false? C中使用char類型,當char的值是0就是false非0就是true;基於這種情況,JNI重新定義了一些類型,以便C和Java對應上;

運行應用,我們可以在MainActivity中成功的顯示從調用C/C++方法獲取到的字元串

上面我們學習了Java中調用C/C++方法,為了清楚的瞭解Java和C之間的互相調用,還需要學習一下Native中調用Java方法

生成so文件

build一下,在這裡找到生成的so文件

參考

https://blog.csdn.net/mq2856992713/article/details/118090046


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 記錄一次EF實體跟蹤錯誤 # 前言 在我寫文章編輯介面的,出現了一個實體跟蹤的錯誤,詳情如下 > System.InvalidOperationException: The instance of entity type 'Tag' cannot be tracked because anoth ...
  • # UGUI的Image(圖片)組件的介紹及使用 ## 1. 什麼是UGUI的Image(圖片)組件? UGUI的Image(圖片)組件是Unity引擎中的一種UI組件,用於顯示2D圖像。它提供了一種簡單而靈活的方式來在游戲中載入和顯示圖片。 ## 2. 為什麼要使用UGUI的Image(圖片)組件 ...
  • # 記錄http請求 ## 環境 * .net7 ## 一、過濾器(Filter) 這個過程用的的是操作過濾器(`ActionFilter`) ## 二、 ### 2.1 繼承`IAsyncActionFilter` ### 2.2 重寫`OnActionExecutionAsync` `OnAct ...
  • [toc] # Linux運維工程師面試題(2) > 祝各位小伙伴們早日找到自己心儀的工作。 > 持續學習才不會被淘汰。 > 地球不爆炸,我們不放假。 > 機會總是留給有有準備的人的。 > 加油,打工人! ## 1 訪問一個網站的流程 1. 打開瀏覽器,輸入網址。首先查找本地緩存,如果有就打開頁面, ...
  • 操作系統是電腦不可或缺的一部分,它連接著硬體和應用程式。內核是操作系統的核心,負責管理進程和線程、記憶體、硬體設備以及提供系統調用介面。電腦啟動過程中,ROM負責載入並執行BIOS程式,而RAM用於存儲運行中的程式和數據。系統調用是操作系統提供給應用程式的介面,通過系統調用可以訪問操作系統的功能。... ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230822120346228-1599813347.png) # 1. 不需要考慮排除任何列 ## 1.1. 清除數據表中所有的內容 ## 1.2. 暫存新數據倉庫的數據 # ...
  • [數據治理](https://www.dtstack.com/?src=szsm)是推動大型集團企業轉型升級、提升競爭優勢、實現高質量發展的重要引擎。 通過搭建[大數據平臺](https://www.dtstack.com/?src=szsm),實現對業務系統數據的採集、清理、建模、整合,建立一個符 ...
  • HP DP(Data Protector Manager)上一個剛剛遷移升級的資料庫備份作業失敗,具體失敗信息如下 .................................RMAN-08503: piece handle=c-1684727642-20230822-00 comment=A ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...