您的位置:

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库已经被压缩。

Android Studio NDK 完全指南

2023-05-17
Android Studio 设置NDK路径

2023-05-14
Android Studio环境变量完全指南

2023-05-20
Android Studio NDK详解:加速移动应用的原生

移动应用开发中,各种开发框架和语言多种多样,常用的有Java、Kotlin和Swift等。但有时候,我们需要使用C或C++语言编写一些性能更高的原生代码,以获取更好的用户体验。Android Stud

2023-12-08
Android Studio SVN使用指南

一、SVN简介 1、Subversion(缩写为SVN),是一个开放源代码的版本控制系统。 2、SVN的功能是,记录文件版本的更改情况,包括谁对文件作出更改,什么时间作出更改,以及从上一个版本到当前版

2023-12-08
安装Android Studio的完整步骤和指南

2023-05-14
Android Studio APK快速创建与发布流程详解

2023-05-14
Android Studio 卸载指南

2023-05-22
如何完全卸载Android Studio

2023-05-14
印象笔记记录java学习(Java成长笔记)

2022-11-12
Android Studio: 完整使用指南

2023-05-14
深入理解Android NDK开发核心

2023-05-14
Android Studio和Ubuntu:用Python从

在移动应用市场的激烈竞争下,拥有一款完整而稳定的移动应用程序变得越来越重要。而在Android Studio和Ubuntu平台上使用Python语言构建完整的移动应用程序是一种非常有效的方法,本文将从

2023-12-08
Android Studio和Ubuntu:用Python从

在移动应用市场的激烈竞争下,拥有一款完整而稳定的移动应用程序变得越来越重要。而在Android Studio和Ubuntu平台上使用Python语言构建完整的移动应用程序是一种非常有效的方法,本文将从

2023-12-08
Android Studio代理全面介绍

2023-05-22
提高开发效率的必备工具——Android Studio 下载

一、Android Studio 是什么? Android Studio是一款官方的Android应用开发集成开发环境,由谷歌推出并提供免费下载使用的。 Android Studio的特点: 提供完整

2023-12-08
安卓开发必备:Android Studio 中文版的使用指南

2023-05-14
Android Studio SDK安装指南

一、下载Android Studio SDK 1、首先,你需要访问Android官网下载Android Studio SDK。下载过程可能需要一些时间,取决于你的网络速度。 2、一旦下载完成,解压文件

2023-12-08
Android SDK:让开发更高效、更便捷

2023-05-14
Android Studio打包APK指南

2023-05-19