Android NDK系列(一)手动搭建Native Project

    使用NDK编写的本地代码具有高性能等特性,在游戏、图形处理等领域有广泛应用,下面介绍如何手动搭建一个纯C++版的Android项目,通过该项目可以理解Android的项目结构。

一、创建settings.gradle

    Android项目是基于Gradle构建的,首先得有settings.gradle文件,正常情况下该文件主要用于配置子模块,但是这里没有子模块,只配置了插件管理和依赖的远程仓库,内容如下:


pluginManagement {repositories {gradlePluginPortal()google()mavenCentral()}
}dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()}
}

二、创建build.gradle

     build.gradle 是Gradle的构建脚本,它用于定义项目的构建配置和依赖关系,可以指定项目的编译版本、依赖库、插件等信息,以及定义构建任务和构建流程。本示例的build.gradle内容如下。

plugins {id 'com.android.application' version '7.2.2'
}android {compileSdk 29ndkVersion "25.1.8937393"buildToolsVersion = "30.0.3"namespace 'com.sino.nativesample'defaultConfig {applicationId "com.sino.nativesample"minSdkVersion 28 // for Vulkan, need at least 24versionCode 1versionName "1.0"externalNativeBuild {cmake {cppFlags '-std=c++17'arguments "-DANDROID_STL=c++_shared"abiFilters 'armeabi-v7a', 'arm64-v8a'}}}sourceSets {main {manifest.srcFile 'AndroidManifest.xml'res.srcDir 'res'}}buildTypes {release {minifyEnabled false}}externalNativeBuild {cmake {path "CMakeLists.txt"}}}

build.gradle主要内容如下:

1、使用插件com.android.application构建Android应用程序。

2、配置Android应用的构建信息,这里的信息大部分与Android Studio自动生成的信息,不一样的是sourceSets用于指定AndroidManifest.xml文件和resource目录,这里分别使用根目录的AndroidManifest.xml和res目录。

3、本示例使用使用C++语言开发,在externalNativeBuild指定CMakeLists.txt文件路径,这里使用根目录的CMakeLists.txt。

三、创建AndroidManifest.xml

    AndroidManifest.xml用于配置项目的权限及组件,本示例的AndroidManifest.xml包含的内容如下。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:installLocation="auto"android:versionCode="1"android:versionName="1.0"><applicationandroid:allowBackup="true"android:hasCode="false"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher"><activityandroid:name="android.app.NativeActivity"android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode|density"android:excludeFromRecents="false"android:launchMode="singleTask"android:resizeableActivity="false"android:screenOrientation="landscape"android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"android:exported="true"tools:ignore="NonResizeableActivity"><!-- Tell NativeActivity the name of the .so --><meta-dataandroid:name="android.app.lib_name"android:value="native" /><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

    AndroidManifest.xml主要内容解析如下。

1、在application标签中,指定了应用的名称和图标,这些信息依赖于res资源目录。

2、声明Activity组件,这里使用NativeActivity,这是框架提供的Activity,能直接于Native层交互,有了该Activity后,就可以不用在项目中自定义其它Activity了。NativeActivity需要指定加载Native层的库,需要在标签lib_name指定库名称,这里使用"native"作为库名称。

四、创建CMakeLists.txt

    定义Activity后,接下来是为它生成对应的库文件,库文件由CMakeLists.txt构建,内容如下。

cmake_minimum_required(VERSION 3.18.1)# Declares and names the project.project("Native")option(ENABLE_ASAN "enable address sanitizer" ON)add_definitions(-DXR_USE_PLATFORM_ANDROID-DXR_OS_ANDROID-D__ANDROID__) #define macroset(CXX_STANDARD "-std=c++17")if (MSVC)set(CXX_STANDARD "/std:c++latest")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
else()set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
endif()include_directories(${ANDROID_NDK}/sources/android/native_app_glue)add_library(native SHARED main.cpp${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)#android
set(CMAKE_SHARED_LINKER_FLAGS"${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate"
)#link all the module
target_link_libraries(native PRIVATE android log)

    在add_library指定库名称为native,正好于NativeActivity的lib_name对应起来,源文件为main.cpp,android_native_app_glue.c是NDK提供的文件,封装了NativeActivity的回调函数,main.cpp正是基于这些回调进行开发。

五、程序开发

    在main.cpp中可以开始基于C++进行程序开发了,这里的main.cpp的主要内容如下。


#include <android_native_app_glue.h>
#include <jni.h>
#include <exception>
#include <android/log.h>#define LOG_TAG "Native"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)struct AndroidAppState {ANativeWindow* NativeWindow = nullptr;bool Resumed = false;
};static void handleAppCmd(struct android_app* app, int32_t cmd) {AndroidAppState* appState = (AndroidAppState*)app->userData;switch (cmd) {case APP_CMD_START: {break;}case APP_CMD_RESUME: {appState->Resumed = true;break;}case APP_CMD_PAUSE: {appState->Resumed = false;break;}case APP_CMD_STOP: {break;}case APP_CMD_DESTROY: {appState->NativeWindow = NULL;break;}case APP_CMD_INIT_WINDOW: {appState->NativeWindow = app->window;break;}case APP_CMD_TERM_WINDOW: {appState->NativeWindow = NULL;break;}}
}static int32_t handleInputEvent(struct android_app* app, AInputEvent* event)
{LOGI("android_main handleInputEvent");if(AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION && AInputEvent_getSource(event) == AINPUT_SOURCE_TOUCHSCREEN){int32_t action = AMotionEvent_getAction(event);float x = AMotionEvent_getX(event,0);float y = AMotionEvent_getY(event,0);switch(action){case AMOTION_EVENT_ACTION_DOWN:LOGI("android_main AMOTION_EVENT_ACTION_DOWN (x %f, y %f)", x,y);break;case AMOTION_EVENT_ACTION_MOVE:LOGI("android_main AMOTION_EVENT_ACTION_MOVE");break;case AMOTION_EVENT_ACTION_UP:LOGI("android_main AMOTION_EVENT_ACTION_UP");break;default:break;}}return 0;
}void android_main(struct android_app* app)
{LOGI("android_main");try {JNIEnv* Env;app->activity->vm->AttachCurrentThread(&Env, nullptr);JavaVM *vm;Env->GetJavaVM(&vm);AndroidAppState appState = {};app->userData = &appState;app->onAppCmd = handleAppCmd;app->onInputEvent = handleInputEvent;while (app->destroyRequested == 0) {for (;;) {int events;struct android_poll_source *source;const int timeoutMilliseconds =(!appState.Resumed &&app->destroyRequested == 0) ? -1 : 0;if (ALooper_pollAll(timeoutMilliseconds, nullptr, &events, (void **) &source) < 0) {break;}if (source != nullptr) {source->process(app, source);}}if(appState.NativeWindow != nullptr){ANativeWindow_setBuffersGeometry(appState.NativeWindow, 100, 100, WINDOW_FORMAT_RGBA_8888);ANativeWindow_Buffer buffer;if (ANativeWindow_lock(appState.NativeWindow, &buffer, nullptr) == 0) {uint32_t* bits = static_cast<uint32_t*>(buffer.bits);for (int i = 0; i < buffer.stride * buffer.height; i++) {bits[i] = 0xFFFFFFFF;//ABGR }ANativeWindow_unlockAndPost(appState.NativeWindow);}}LOGI("android_main loop");//why is 16//handle}app->activity->vm->DetachCurrentThread();} catch (const std::exception& ex) {LOGE("%s",ex.what());} catch (...) {LOGE("Unknow Error");}
}

    main.cpp的主要方法如下:

1、android_main是入口函数,在该函数中主要定义循环,在循环中处理程序的核心流程。

2、handleAppCmd处理Activity的回调事件

3、handleInputEvent处理输入事件。

六、准备构建工具

    上面的文件准备好以后,下一步是准备构建工具,到Android Studio的工程中把gradle目录、gradle.properties、gradlew、gradlew.bat拷贝到当前工程的根目录,就可以使用gradlew命令构建工程了

七、构建工程

    本机已经准备好Android开发环境的情况下,在命令窗口切换到工程根目录,使用命令gradlew.bat assemble即可生成可以运行的apk

八、执行效果

   通过脚本构建得到apk后,安装到设备上即可在桌面上显示应用图标,点击图标可启动应用,下面是在模拟器上运行的效果,如下图所示。

    在图中显示的是白色背景,这是由于在循环中对窗口的图形缓存的每个像素点都赋值为白色,bits[i] = 0xFFFFFFFF;//ABGR 

九、完整工程路径

本示例的工程已上传到github,链接如下。

示例工程地址

上面是手动搭建的工程地址,下面是通过Android Studio生成的工程,也是基于NativeActivity

Android Studio自动生成的工程

本示例是一个基础工程,后续介绍的示例大部分基于上面的工程扩展而来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/843196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之链表(高级应用)

基本的算法实践在上一篇博文&#xff0c;这篇博文向大家详细展示一下数据结构的高级应用&#xff0c;可能有些难&#xff0c;但这是重点&#xff0c;实用性很强&#xff0c;而且用的好往往事半功倍&#xff0c;想获得力量吗&#xff0c;开整&#xff1a; 我把他们分为这几块&a…

Captura完全免费的电脑录屏软件

一、简介 1、Captura 是一款免费开源的电脑录屏软件&#xff0c;允许用户捕捉电脑屏幕上的任意区域、窗口、甚至是全屏画面&#xff0c;并将这些画面录制为视频文件。这款软件具有多种功能&#xff0c;例如可以设置是否显示鼠标、记录鼠标点击、键盘按键、计时器以及声音等。此…

JVM1.8分代的理论基础和简单测试

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

用PHP封装一个强大且通用的CURL方法

用PHP封装一个强大且通用的CURL方法 支持&#xff1a;get、post、put、delete、patch、options 直接上代码&#xff1a; /*** function 强大且通用的cURL请求库* param $url string 路径* param $method string 请求方式 如&#x…

【Javascript】Promise形象比喻

我们可以把 Promise 形象地比喻为一个“外卖订单”&#xff0c;它有三种状态&#xff1a;下单中、已送达、已取消。这个比喻可以帮助你理解 Promise 的工作机制。 比喻解释 Pending&#xff08;下单中&#xff09;&#xff1a; 当你在外卖平台上创建一个订单时&#xff0c;订…

c 函数指针的表现形式

1.概要 int&#xff08;*函数指针变量&#xff09;(int a,char b)&#xff0c;这里的函数指针变量就可以代码一个特定类型的函数指针 int&#xff08;*&#xff09;(int a,char b)就是这种函数指针的类型 2.代码 #include <iostream>void fun() {printf("fun\n&q…

海外仓系统哪家好?闭坑指南,擦亮眼睛选对系统

可以说现在的海外仓系统市场还是比较杂乱的&#xff0c;各种不同类型&#xff0c;不同收费标准的系统比比皆是&#xff0c;这让很多想引进海外仓系统的企业不知所措&#xff0c;不知道怎么选。 今天就聊一下在选择海外仓系统的时候应该如何考量&#xff0c;才能避免被坑&#…

JS分支语句

一 三元运算符 1 语法格式&#xff1a; 条件?满足条件执行代码1:不满足条件执行代码2 2 用途 三元运算符等价于简单的if else语句&#xff0c;常用于判断取值 二 switch语句 1 语法格式&#xff1a; swtich(数据){case 值1:执行语句breakcase 值2:执行语句breakcase 值…

C++之对象的使用

1、static成员 2、static成员优点 2、static成员函数 静态成员函数不能访问非静态成员原因&#xff1a;因为没有this指针。也不可以访问非静态成员函数。 可以通过对象来访问静态成员&#xff0c;但是不推荐这么使用&#xff0c;会让人误解成这个x_是属于对象的&#xff0c;但…

贪心算法典型题目

糖果 有n个小朋友&#xff0c;有m袋糖果&#xff0c;第i袋的糖果数量是a[i]。已知n<m<2n。 每个小朋友至少要有一袋糖果&#xff0c;至多只能要两袋糖果。 分配的目标是把这m袋糖果全部分完&#xff0c;而且要使得每个小朋友最终得到的糖果数量都是相等的。 如果可以…

“大一新生接单:专业打造单页HTML网站,让你的项目脱颖而出!”

我是大一新生&#xff0c;对HTML有浓厚的兴趣&#xff0c;并且已经掌握了一些基础知识。 学会了使用htmlcss的网页设计&#xff08;暂未学JavaScript&#xff09; 如果你需要代写的服务可以在本作品下方 评论留言

PyCharm基本配置内容

如何更换 Python 解释器 输入一段代码点击运行后&#xff0c;画面下方有一个路径如图中框中所示&#xff1a; 上面的路径为虚拟路径&#xff0c;可以改为我们自己设置的路径 点击设置&#xff0c;选择settings 选择Project&#xff1a;y002———》Python Interpreter&#…

C++课程设计实验杭州电子科技大学ACM题目(中)

题目四&#xff1a;2016.数据的交换输出 题目描述 Problem Description&#xff1a; 输入n(n<100)个数&#xff0c;找出其中最小的数&#xff0c;将它与最前面的数交换后输出这些数。 Input&#xff1a;输入数据有多组&#xff0c;每组占一行&#xff0c;每行的开始是一…

python爬虫之pandas库——数据清洗

安装pandas库 pip install pandas pandas库操作文件 已知在本地桌面有一名为Python开发岗位的csv文件(如果是excel文件可以做简单修改即可&#xff0c;道理是通用的) 打开文件&#xff1a; 打开文件并查看文件内容 from pandas import DataFrame import pandas as pd data_c…

【自动驾驶技术栈学习】2-软件《大话自动驾驶》| 综述要点总结 by.Akaxi

----------------------------------------------------------------------------------------------------------------- 致谢&#xff1a;感谢十一号线人老师的《大话自动驾驶》书籍&#xff0c;收获颇丰 链接&#xff1a;大话自动驾驶 (豆瓣) (douban.com) -------------…

nuxt3+Element Plus项目搭建过程记录

背景 本文只记录项目搭建过程中遇到的一些问题和关键点&#xff0c;nuxt框架的说明和API请参照官网学习 官网&#xff1a;https://nuxt.com/docs/getting-started/introduction 1. 初始化项目 指令如下: npx nuxilatest init <project-name>我在安装过程中出现报错&a…

本地源码方式部署启动MaxKB知识库问答系统,一篇文章搞定!

MaxKB 是一款基于 LLM 大语言模型的知识库问答系统。MaxKB Max Knowledge Base&#xff0c;旨在成为企业的最强大脑。 开箱即用&#xff1a;支持直接上传文档、自动爬取在线文档&#xff0c;支持文本自动拆分、向量化、RAG&#xff08;检索增强生成&#xff09;&#xff0c;智…

AI视频智能分析技术赋能营业厅:智慧化管理与效率新突破

一、方案背景 随着信息技术的快速发展&#xff0c;图像和视频分析技术已广泛应用于各行各业&#xff0c;特别是在营业厅场景中&#xff0c;该技术能够有效提升服务质量、优化客户体验&#xff0c;并提高安全保障水平。TSINGSEE青犀智慧营业厅视频管理方案旨在探讨视频监控和视…

七人拼购新模式:革新购物体验,共创价值

在数字时代&#xff0c;消费者的购物体验正经历着前所未有的变革。七人拼购模式作为一种新兴的购物方式&#xff0c;通过汇集消费者的力量&#xff0c;实现商品价格的最优化&#xff0c;让消费者享受到前所未有的实惠与便利。以下&#xff0c;我们将以一款标价499元的商品为例&…

消防体验馆升级,互动媒体点亮安全之路!

在当下这个科技日新月异的时代&#xff0c;多媒体互动技术已深深融入现代化消防体验馆的设计之中&#xff0c;它们不仅为这些场馆注入了前所未有的创意与活力&#xff0c;更通过其互动性、趣味性等独特优势&#xff0c;彻底革新了消防宣传教育的传统模式。如今&#xff0c;这种…