Android Studio NDK 完全指南

一、環境配置

1、NDK簡介

Android NDK(Native Development Kit),即Android本地開發工具包,是Google為了允許開發者使用C/C++編寫本地代碼並在Android設備上運行而提供的一份工具和支持庫。NDK提供了一系列系統頭文件、函數庫、調試器等工具,同時也提供了支持C++代碼的STL等庫文件。使用NDK可以加速CPU密集型任務和讓現有C/C++代碼可以在Android系統上運行。

2、安裝NDK

首先,要在Android Studio中下載NDK,打開新建工程中的Gradle Scripts->build.gradle(Project:為你的項目名),將「classpath ‘com.android.tools.build:gradle:x.x.x’」中的「x.x.x」修改成你的AS版本所需的gradle版本,然後再在app->build.gradle文件中添加以下內容:

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                ...
                //指向 CMakeLists.txt 文件所在的外部編譯目錄的絕對路徑
                path "src/main/cpp/CMakeLists.txt"
            }
        }
    }
    buildTypes {
        ...
        externalNativeBuild {
            cmake {
                ...
                //指向 CMakeLists.txt 文件所在的外部編譯目錄的絕對路徑
                path "src/main/cpp/CMakeLists.txt"
            }
        }
    }
    externalNativeBuild {
        // 定義ndk-build編譯腳本的庫
        ndkBuild {
            //指向Android.mk文件所在的絕對路徑
            path "src/main/jni/Android.mk"
        }
    }
}

這裡以使用CMake為編譯系統為例,在app->build.gradle文件中添加以下內容:

android {
    ...
    defaultConfig {
        ...
        // 指定支持abi
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
        }
    }
    externalNativeBuild {
        cmake {
            //指向 CMakeLists.txt 文件所在的外部編譯目錄的絕對路徑
            path file('src/main/cpp/CMakeLists.txt')
        }
    }
}

3、配置CMakeLists.txt

CMake是一個跨平台的編譯系統,Android NDK支持使用CMake_build原生代碼。打開Android項目中的app目錄,在cpp文件夾下創建一個新的CMakeLists.txt文件。以下是最簡單的示例CMakeLists.txt:

cmake_minimum_required(VERSION 3.4.1)
 
# 創建一個動態庫
add_library(
    hello     # 庫名稱
    SHARED     # 庫類型:SHARED表示動態庫
    hello.c)  # 庫源文件
 
# 導入log庫
find_library(
    log-lib     # 定義log庫名稱
    log)        # 指定庫名稱在系統中的實際文件名稱
 
# 將log庫鏈接到hello庫中
target_link_libraries(
    hello       # 目標庫名稱
    ${log-lib}) # 鏈接庫名稱

二、JNI開發

1、JNI簡介

Java Native Interface (JNI) 是一個Java平台編程接口,允許Java代碼和其他語言如C/C++程序進行交互。Java虛擬機提供一個標準的JNI接口,它可以被C/C++編譯器所使用,可以將Java垃圾回收機制與C/C++語言靈活地融合在一起。

2、JNI基礎

在Java中使用JNI調用本地代碼,需要在Java中定義native函數,並生成頭文件。以下是一個Java類中定義native函數的示例:

public class NdkJniUtils {
    static {
        System.loadLibrary("ndkJniLib");
    }
 
    public native String sayHello(String name);
}

使用以下命令生成頭文件:

javah -d jni -classpath {Java類的.class文件路徑} {Java全限定類名(包括包名)}

3、JNI實踐

以調用hello.c函數為例,先編寫.h文件:

#include <jni.h>
 
JNIEXPORT jstring JNICALL Java_com_example_ndkdemo_MainActivity_hello(JNIEnv *env, jobject instance){
    return (*env)->NewStringUTF(env, (const char *)"Hello NDK!");
}

在.c文件中實現hello.c函數:

#include <stdio.h>
#include <jni.h>
 
JNIEXPORT jstring JNICALL Java_com_example_ndkdemo_MainActivity_hello(JNIEnv *env, jobject instance){
    return (*env)->NewStringUTF(env, "Hello NDK!");
}

4、編譯

使用以下命令編譯:

gcc -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux hello.c -o libhello.so

將生成的libhello.so庫文件拷貝到app/src/main/jniLibs/armeabi-v7a/目錄下,通過System.loadLibrary()方法加載即可。

三、混合編程

1、混合編程簡介

將Java和C++混合編程的方式,稱之為混合編程。混合編程可以讓我們在Java層面上方便的使用C++的特性,也可以讓我們利用Java的強大庫來進行快速開發。

2、JNI編程注意點

在JNI編程中,有幾個重要的規則需要遵循:

  • 盡量不要執行耗時任務
  • 盡量避免崩潰或錯誤
  • 避免資源泄露
  • 鎖定Global Reference和Local Reference

3、示例代碼

代碼示例中將展示如何從Java層面調用C++庫。在Java文件中聲明native方法:

public static native int add(int a, int b);

在C++文件中實現add()函數,並將其包裝在JNICaller類中:

#include <jni.h>
using namespace std;
class JNICaller
{
public:
    static void init(JNIEnv *env);//初始化

    static int add(int a,int b); //導出給Java層面的native方法
};
 
void JNICaller::init(JNIEnv *env)
{
    jclass jniCaller_clazz=env->FindClass("com/example/ndkdemo/JNICaller");//獲取Java層面的類的引用
    jmethodID construct=jniEnv->GetMethodID(jniCaller_clazz,<init>,"()V");//獲取Java層面的構造函數的引用
 
    //註冊native方法
    JNINativeMethod methods[] = 
    {
        {"add", "(II)I", (void *) JNICaller::add}
    };
    jniEnv->RegisterNatives(jniCaller_clazz, methods, sizeof(methods) / sizeof(methods[0]));
 
    printf("init JNI caller success.\n");
}
 
int JNICaller::add(int a,int b)
{
    return a+b;
}

在Java層面中調用add()方法:

JNICaller.init();//初始化JNICaller
int result = JNICaller.add(a, b);//在Java層面中調用JNICaller的add()方法

四、NDK調試

1、NDK調試簡介

NDK調試可以讓我們在本地開發環境中調試C++層代碼,可以極大的提高開發效率。NDK調試可以幫助我們在開發過程中及時發現bug並進行修復。NDK調試分為兩種類型:GDB和LLDB

2、NDK調試步驟

將以下代碼添加到app->build.gradle文件中:

externalNativeBuild {
    cmake {
        cmake {
            // -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=libs
            // 指定編譯生成文件的目錄
            arguments "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${project.buildDir}/libs/${outputPath}"
            // -DCMAKE_BUILD_TYPE=Debug
            // 指定編譯類型為Debug模式
            arguments "-DCMAKE_BUILD_TYPE=Debug"
            // -DANDROID_TOOLCHAIN=clang
            // 指定編譯器為Clang編譯器
            arguments "-DANDROID_TOOLCHAIN=clang"
            // -DANDROID_STL=c++_static
            // 指定STL庫為c++_static
            arguments "-DANDROID_STL=c++_static"
        }
    }
    ndkBuild {
        //同上
    }
}

在Android Studio中打開「Edit Configurations」菜單,在「Debugger」選項卡中設置:

  • Debugger Type為Auto
  • Debug Type為Native

點擊「Run」菜單中的「Debug 'app'」進行調試

五、NDK壓縮

在開發完成之後,需要將so庫打包成apk文件。程序中使用的Shared Library(.so文件)原則上都應當打包進APK里。這樣的做法直接導致.apk文件的大小增加,不過現在主流的壓縮工具如upx等均對.so文件有壓縮能力。毫無疑問,對於 .so 文件而言,應該利用好這樣的能力來縮減APK包大小。

1、UPX

UPX是一個免費的、便攜式的、可擴展的、高性能的文件壓縮器,它是一個通用性的可執行文件壓縮器。UPX支持Linux、MacOS X和近年出現的Windows安裝包壓縮,UPX完全符合ELF, PE, MZ, COFF, NLM和OMF格式標準。在NDK中,我們使用UPX來對.so庫進行壓縮。

2、NDK壓縮步驟

在app/build.gradle文件中添加以下配置:

splits {
    abi {
        enable true
        //This property should be set only to true for publishing part of your app as APK. 
        reset()
        include "armeabi-v7a", "x86"
        universalApk true
    }
}
apply from: "../../tools/JniLibsCompression.gradle"

為了使用UPX,需要拷貝tools目錄中的JniLibsCompression.gradle,然後添加以下代碼到app/build.gradle文件:

apply from: "../../tools/JniLibsCompression.gradle"
 
project.ext.upx_enabled = true
project.ext.upx_options = "best --lzma"
 
android {
   ...
}

最後執行gradlew clean assembleRelease命令打包APK文件,生成的APK文件中的so庫已經被壓縮。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/301976.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-30 16:10
下一篇 2024-12-30 16:10

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • 運維Python和GO應用實踐指南

    本文將從多個角度詳細闡述運維Python和GO的實際應用,包括監控、管理、自動化、部署、持續集成等方面。 一、監控 運維中的監控是保證系統穩定性的重要手段。Python和GO都有強…

    編程 2025-04-29
  • Python應用程序的全面指南

    Python是一種功能強大而簡單易學的編程語言,適用於多種應用場景。本篇文章將從多個方面介紹Python如何應用於開發應用程序。 一、Web應用程序 目前,基於Python的Web…

    編程 2025-04-29
  • Python wordcloud入門指南

    如何在Python中使用wordcloud庫生成文字雲? 一、安裝和導入wordcloud庫 在使用wordcloud前,需要保證庫已經安裝並導入: !pip install wo…

    編程 2025-04-29
  • Python小波分解入門指南

    本文將介紹Python小波分解的概念、基本原理和實現方法,幫助初學者掌握相關技能。 一、小波變換概述 小波分解是一種廣泛應用於數字信號處理和圖像處理的方法,可以將信號分解成多個具有…

    編程 2025-04-29
  • Python字符轉列表指南

    Python是一個極為流行的腳本語言,在數據處理、數據分析、人工智能等領域廣泛應用。在很多場景下需要將字符串轉換為列表,以便於操作和處理,本篇文章將從多個方面對Python字符轉列…

    編程 2025-04-29
  • Python初學者指南:第一個Python程序安裝步驟

    在本篇指南中,我們將通過以下方式來詳細講解第一個Python程序安裝步驟: Python的安裝和環境配置 在命令行中編寫和運行第一個Python程序 使用IDE編寫和運行第一個Py…

    編程 2025-04-29
  • FusionMaps應用指南

    FusionMaps是一款基於JavaScript和Flash的交互式地圖可視化工具。它提供了一種簡單易用的方式,將複雜的數據可視化為地圖。本文將從基礎的配置開始講解,到如何定製和…

    編程 2025-04-29
  • Python起筆落筆全能開發指南

    Python起筆落筆是指在編寫Python代碼時的編寫習慣。一個好的起筆落筆習慣可以提高代碼的可讀性、可維護性和可擴展性,本文將從多個方面進行詳細闡述。 一、變量命名 變量命名是起…

    編程 2025-04-29
  • Python中文版下載官網的完整指南

    Python是一種廣泛使用的編程語言,具有簡潔、易讀易寫等特點。Python中文版下載官網是Python學習和使用過程中的重要資源,本文將從多個方面對Python中文版下載官網進行…

    編程 2025-04-29

發表回復

登錄後才能評論