Android native层的线程分析(C++),以及堆栈打印调试
文章目录
- Android native层的线程分析(C++),多线程实现
- 1.native线程的创建
- 第一部分:android_thread模块
- 第二部分:linux_thread模块
- 2.测试linux_thread模块
- 3.Android native的Thread类
- 3.1源码分析
- 4.native层堆栈调试方法
Android native层的线程分析(C++),多线程实现
1.native线程的创建
pthread_t //表示线程ID
pthread_equal (pthread_t __thread1, pthread_t __thread2);//比较线程ID
pthread_t pthread_self (void);//用户返回线程ID
pthread_create()//线程创建
编写Android.mk文件,
这个MK文件是一个Android.mk构建脚本,用于指导Android Native Development Kit (NDK)如何编译和链接两个可执行模块:android_thread 和 linux_thread。下面是该脚本的详细解析:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)LOCAL_SRC_FILES := MyThread.cpp \Main.cpp \LOCAL_SHARED_LIBRARIES :=libandroid_runtime \libcutils \libutils \liblog LOCAL_MODULE := android_threadLOCAL_PRELINK_MODULE := falseinclude $(BUILD_EXECUTABLE)include $(CLEAR_VARS)LOCAL_SRC_FILES := thread_posix.c LOCAL_MODULE := linux_thread
LOCAL_SHARED_LIBRARIES :=liblog LOCAL_PRELINK_MODULE := falseinclude $(BUILD_EXECUTABLE)//把thread_posix.c 文件编译(BUILD_EXECUTABLE)成为一个二进制的可执行文件,这个二进制可执行文件的名字是linux_thread。
第一部分:android_thread模块
- LOCAL_PATH: 这一行定义了当前目录的路径,通过
my-dir函数自动获取,作为查找其他文件(如源代码文件)的相对路径基础。 - include $(CLEAR_VARS): 这行代码包含了清除所有之前定义的LOCAL变量的脚本,确保为新模块提供一个干净的构建环境。
- LOCAL_SRC_FILES: 指定了要编译的源文件列表。在这个例子中,包括
MyThread.cpp和Main.cpp。 - LOCAL_SHARED_LIBRARIES: 列出了该模块需要链接的共享库,包括
libandroid_runtime,libcutils,libutils, 和liblog。这些都是Android系统提供的库,用于支持Android运行时功能、实用工具函数、日志输出等功能。 - LOCAL_MODULE: 定义了模块的名称,这里是
android_thread。 - LOCAL_PRELINK_MODULE: 设定为
false,表示该模块在预链接阶段不会被处理。预链接是一个可选步骤,通常用于减少应用启动时间,但在这里不适用。 - include $(BUILD_EXECUTABLE): 告诉NDK构建系统,根据前面定义的规则,将这些源文件编译成一个可执行文件。
第二部分:linux_thread模块
这部分结构与第一部分相似,但是针对另一个模块linux_thread:
- LOCAL_SRC_FILES 只包含一个源文件:
thread_posix.c,意味着这是一个基于POSIX线程标准实现的模块。 - LOCAL_SHARED_LIBRARIES 只列出了
liblog,说明这个模块依赖于日志库来记录日志信息。 - 其他如
LOCAL_MODULE、LOCAL_PRELINK_MODULE以及最后的include $(BUILD_EXECUTABLE)指令用法与android_thread模块相同,用于构建名为linux_thread的独立可执行模块。
综上所述,这个MK文件配置了两个C++/C语言编写的可执行模块的构建过程,一个是与Android系统紧密结合的android_thread,另一个是使用POSIX线程API的linux_thread,两者都将作为独立的可执行文件生成。
thread_posix.c
这段代码是一个简单的C语言程序,演示了如何在Linux或类Unix系统中使用POSIX线程库(pthread.h)创建并管理一个线程。同时,它也使用了Android的日志系统(通过utils/Log.h头文件)来记录日志信息。以下是代码的详细解释:
#include <pthread.h> // 包含POSIX线程库头文件
#include <stdlib.h> // 用于exit函数
#include <stdio.h> // 用于printf函数
#include <utils/Log.h> // 包含Android日志系统头文件// 定义线程执行的函数
void *thread_posix_function(void *arg) {(void*)arg; // 忽略传入的参数,这里没有使用int i;for (i = 0; i < 30; i++) {printf("hello thread i = %d\n", i); // 打印到标准输出ALOGD("hello thread i = %d\n", i); // 使用Android日志系统打印DEBUG级别日志sleep(1); // 线程暂停1秒}return NULL; // 线程函数结束,返回空指针
}int main(void) {pthread_t mythread; // 定义一个线程标识符// 创建一个新的线程,执行thread_posix_function函数,传入参数为NULLif (pthread_create(&mythread, NULL, thread_posix_function, NULL)) {ALOGD("error creating thread."); // 如果创建失败,记录错误日志abort(); // 终止程序执行}// 主线程等待mythread线程结束if (pthread_join(mythread, NULL)) {ALOGD("error joining thread."); // 如果等待失败,记录错误日志abort(); // 终止程序执行}ALOGD("hello thread has run end exit\n"); // 记录日志,表明线程已正确执行完毕exit(0); // 主程序正常退出
}
这段代码首先定义了一个线程执行的函数thread_posix_function,该函数每隔一秒打印一次消息到控制台和Android日志系统,共打印30次。在main函数中,它创建了一个新的线程并执行thread_posix_function,然后主线程等待这个新线程完成其任务后才退出。整个过程中,还利用了Android的日志系统来报告错误或提供执行状态信息。
2.测试linux_thread模块

在安卓源码的根目录下创建一个文件夹,并写上mk文件,提供编译的脚本。
然后单独编译模块名即可 ===== LOCAL_MODULE

把编译后的可执行二进制文件push到设备中

执行一下,可以看到线程在打印输出。

3.Android native的Thread类
Android native的Thread类是Android提供的一个基础类
system\core\libutils\include\utils\Thread.h
system\core\libutils\Threads.cpp
智能指针,主要用来释放和控制内存。

virtual void onFirstRef();
第一次这个类被创建就会执行这个智能指针的这个方法,在这个方法里面我们就可以做一些事情了。执行线程创建并启动运行un方法,status_t run(const char* name, int32_t priority, size_t stack);,先执行readyToRun(),创建完成后,通过调用threadLoop()函数,线程请求退出方法,实现requestExit()函数。
3.1源码分析
run()方法,可以看到它也是用的pthread那套线程api,Android的native层的线程就是基于linux的pthread方案进行封装的。

进入_threadLoop
int Thread::_threadLoop(void* user)
{Thread* const self = static_cast<Thread*>(user);sp<Thread> strong(self->mHoldSelf);wp<Thread> weak(strong);self->mHoldSelf.clear();#if defined(__ANDROID__)// this is very useful for debugging with gdbself->mTid = gettid();
#endifbool first = true;do {bool result;if (first) {first = false;//我们一旦调用了线程的run方法之后首先执行的就是这个readyToRun方法。self->mStatus = self->readyToRun();result = (self->mStatus == OK);if (result && !self->exitPending()) {// Binder threads (and maybe others) rely on threadLoop// running at least once after a successful ::readyToRun()// (unless, of course, the thread has already been asked to exit// at that point).// This is because threads are essentially used like this:// (new ThreadSubclass())->run();// The caller therefore does not retain a strong reference to// the thread and the thread would simply disappear after the// successful ::readyToRun() call instead of entering the// threadLoop at least once.result = self->threadLoop();}} else {result = self->threadLoop();}// establish a scope for mLock{Mutex::Autolock _l(self->mLock);if (result == false || self->mExitPending) {self->mExitPending = true;self->mRunning = false;// clear thread ID so that requestExitAndWait() does not exit if// called by a new thread using the same thread ID as this one.self->mThread = thread_id_t(-1);// note that interested observers blocked in requestExitAndWait are// awoken by broadcast, but blocked on mLock until break exits scopeself->mThreadExitedCondition.broadcast();break;}}// Release our strong reference, to let a chance to the thread// to die a peaceful death.strong.clear();// And immediately, re-acquire a strong reference for the next loopstrong = weak.promote();} while(strong != nullptr);return 0;
}
测试
#ifndef _MYTHREAD_H
#define _MYTHREAD_H#include <utils/threads.h>namespace android {class MyThread: public Thread {
public:MyThread();virtual void onFirstRef();virtual status_t readyToRun();virtual bool threadLoop();virtual void requestExit();
private:int hasRunCount = 0;
};}
#endif
#define LOG_TAG "MyThread"#include <utils/Log.h>
#include "MyThread.h"namespace android {MyThread::MyThread() :Thread(false) {ALOGD("MyThread");}bool MyThread::threadLoop() {ALOGD("threadLoop hasRunCount = %d",hasRunCount);hasRunCount++;if (hasRunCount == 10) {return false; }return true;}void MyThread::onFirstRef() {ALOGD("onFirstRef");}status_t MyThread::readyToRun() {ALOGD("readyToRun");return 0;}void MyThread::requestExit() {ALOGD("requestExit");}
}
同上linux_thread操作编译push即可。
4.native层堆栈调试方法
android::CallStack()。所在线程的堆栈调用打印出来。
进入对应的cpp文件,解开注释,并且修改值为1

声明头文件

调用方法,

android::CallStack cs("zxx");cs.update();cs.log("zxx",ANDROID_LOG_ERROR,"=======================");
测试

相关文章:
Android native层的线程分析(C++),以及堆栈打印调试
文章目录 Android native层的线程分析(C),多线程实现1.native线程的创建第一部分:android_thread模块第二部分:linux_thread模块 2.测试linux_thread模块3.Android native的Thread类3.1源码分析 4.native层堆栈调试方法 Android native层的线…...
计算机科学:2024年高考生的明智之选?兴趣与趋势并重的决策指南
站在2024年这个时间节点上,计算机相关专业依然保持着其“万金油”地位,尽管面临一定的挑战,但其长期发展前景和就业潜力仍然乐观。以下是从不同身份角度出发的观点分析: 高考生视角: 如果你是今年的高考生࿰…...
跨界合作机会:通过淘宝数据挖掘潜在的合作伙伴与市场拓展方向
淘宝平台汇聚了众多商家和消费者,生成了大量的交易数据,这些数据为商家提供了挖掘跨界合作机会和市场拓展方向的丰富线索。以下是如何利用淘宝数据来寻找潜在的合作伙伴和探索新的市场机会的一些策略: 消费者行为分析:通过跟踪消费…...
如何利用智能家居打造一个“会呼吸的家”?一体化电动窗帘
如何利用智能家居打造一个“会呼吸的家”?一体化电动窗帘 史新华 隐藏式一体化智能电动窗帘与市面上其他窗帘不同的是,电机内置于轨道之中,一体化,美观、安静、滑动顺畅。 每次都会自动打开和关闭,相当漂亮。 众多家庭…...
PyTorch -- 最常见激活函数的选择
首先,简单复习下什么是梯度:梯度是偏微分的集合 举例说明:对于 z y 2 − x 2 : ∇ z ( ∂ z ∂ x , ∂ z ∂ y ) ( 2 x , 2 y ) z y^2-x^2: \nabla z (\frac{\partial z}{\partial x}, \frac{\partial z}{\partia…...
人工智能--制造业和农业
欢迎来到 Papicatch的博客 文章目录 🍉人工智能在制造业中的应用 🍈 应用场景及便利 🍍生产线自动化 🍍质量控制 🍍预测性维护 🍍供应链优化 🍈 技术实现及核心 🍍机器学习和…...
go语言,拼接字符串有哪些方式
目录 第一种方式: 使用加号"" 第二种方式: 使用fmt.Sprintf 第三种方式: 使用strings.Join 第四种方式: 使用strings.Builder 第五种方式: 使用bytes.Buffer go语言,拼接字符串的方式有…...
C++类型转换深度解析:从基础数据类型到字符串,再到基础数据类型的完美转换指南
前言 在 C 编程中,我们经常需要在基础数据类型(如 int、double、float、long、unsigned int 等)与 string 类型之间进行转换。这种转换对于处理用户输入、格式化输出、数据存储等场景至关重要。 本文将详细介绍如何在 C 中实现这些转换。 文…...
一文了解:渐进式web应用(PWA),原生应用还香吗?
前端开发是一个充满活力和不断演进的领域,各类技术层出不穷,PWA模式的出现就是想让web移动应用获得原生一样的体验,同时有大幅度降低开发成本,那么它到底能行吗?贝格前端工场带领大家了解一下。 一、什么是渐进式web应…...
SOLIDWORKS学生支持 可访问各种产品资源
你是不是一个热爱设计、追求创新的学生?你是不是在寻找一款能够帮助你实现设计梦想的工具?那么,SolidWorks学生支持是你的首要选择! SOLIDWORKS作为三维CAD设计软件,一直致力于为广大学生提供全方面的支持。无论你是初…...
VCS基本仿真
这里记录三种仿真方式: 第一种是将verilog文件一个一个敲在终端上进行仿真; 第二种是将多个verilog文件的文件路径整理在一个文件中,然后进行仿真; 第三种是利用makefile文件进行仿真; 以8位加法器为例: …...
Hbase中Rowkey的设计方法
Hbase中Rowkey的设计方法 过去对于Rowkey设计方法缺乏理解,最近结合多篇博主的文章,进行了学习。有不少心得体会。总结下来供后续学习和回顾。 一、设计Rowkey的三个原则 1.长度原则:长度不能太长,小于100个字节。可以偏端一些…...
Python基础总结之functools.wraps介绍与应用
Python基础总结之functools.wraps介绍与应用 在Python编程中,装饰器(decorator)是一种非常强大的工具,它允许开发者在不改变函数本身的情况下,动态地增加函数的功能。使用装饰器时,常常会用到 functools.wr…...
UE5基础1-下载安装
目录 一.下载 二.安装 三.安装引擎 四.其他 简介: UE5(Unreal Engine 5)是一款功能极其强大的游戏引擎。 它具有以下显著特点: 先进的图形技术:能够呈现出令人惊叹的逼真视觉效果,包括高逼真的光影、材…...
前端实现获取后端返回的文件流并下载
前端实现获取后端返回的文件流并下载 方法一:使用Axios实现文件流下载优点缺点 方法二:使用封装的Request工具实现文件流下载优点缺点 方法三:直接通过URL跳转下载优点缺点 结论 在前端开发中,有时需要从后端获取文件流࿰…...
Windows下对于Qt中带 / 的路径的处理
在Windows下,如果你想使用操作系统的分隔符显示用户的路径,请使用 toNativeSeparators()。 请看以下代码: void Player::on_playBtn_clicked() {if (this->m_url.isEmpty()) {openMedia();if (this->m_url.isEmpty())return;}qDebug(…...
[leetcode]swap-nodes-in-pairs
. - 力扣(LeetCode) class Solution { public:ListNode* swapPairs(ListNode* head) {ListNode* dummyHead new ListNode(0);dummyHead->next head;ListNode* temp dummyHead;while (temp->next ! nullptr && temp->next->next !…...
国思RDIF.vNext全新低代码快速开发框架平台6.1版本发布(支持vue2、vue3)
1、平台介绍 RDIF.vNext,全新低代码快速开发集成框架平台,给用户和开发者最佳的.Net框架平台方案,为企业快速构建跨平台、企业级的应用提供强大支持。 RDIF.vNext的前身是RDIFramework框架,RDIF(Rapid develop Integrate Framewor…...
中国地市分布图
原文链接https://mp.weixin.qq.com/s?__bizMzUyNzczMTI4Mg&mid2247693904&idx1&snb54884975272eaecb1d0564cafc128d3&chksmfa76a96dcd01207b939b8852a08eea9852eeffa8cc51a3af055dfca5c999e93301237e95901b&token1851596113&langzh_CN#rd...
HCIA11 网络安全之本地 AAA 配置实验
AAA 提供 Authentication(认证)、Authorization(授权)和 Accounting(计费)三种安全功能。 • 认证:验证用户是否可以获得网络访问权。 • 授权:授权用户可以使用哪些服务。 •…...
终极指南:ImagePicker资源解析机制如何高效处理图像资源
终极指南:ImagePicker资源解析机制如何高效处理图像资源 【免费下载链接】ImagePicker :camera: Reinventing the way ImagePicker works. 项目地址: https://gitcode.com/gh_mirrors/im/ImagePicker ImagePicker作为一款重新定义图片选择体验的工具…...
FlowScope:一款注重隐私的SQL数据血缘分析工具
最近团队接手了一个新的数据仓库项目,这个项目已经开发了很多年,包含了几百个表和几万行 ETL 存储过程代码。 目前我们经常面临的问题包括: 这个字段从哪里来?这张表被哪些存储过程用到了?修改这个字段会影响哪些 ET…...
【亲测有效】绕开收费陷阱!教你免费安装H.265/HEVC解码器,告别视频播放“绿屏”
最近在处理一些4K视频素材时,又遇到了老生常谈的问题——Windows 10/11无法播放H.265编码的视频,提示“缺少编解码器”。 大家都知道,解决办法是安装那个名为“HEVC 视频扩展”的微软官方插件。 然而,当我满怀信心地打开Microsof…...
EcomGPT-7B电商大模型Java八股文实践:面试级电商系统设计题解析
EcomGPT-7B电商大模型Java八股文实践:面试级电商系统设计题解析 最近在技术社区里,看到不少朋友在讨论一个挺有意思的电商大模型——EcomGPT-7B。它不像那些通用的聊天模型,而是专门针对电商领域训练出来的。我就在想,如果用它来…...
YOLOv9镜像实测:无需配置环境,快速实现目标检测全流程
YOLOv9镜像实测:无需配置环境,快速实现目标检测全流程 1. 开箱即用的YOLOv9体验 对于目标检测开发者来说,最头疼的往往不是算法本身,而是环境配置这个"拦路虎"。不同版本的CUDA、PyTorch、Python之间的兼容性问题&…...
OpenClaw 生态全景图——AI 助理如何改变工作方式
OpenClaw 生态全景图——AI 助理如何改变工作方式摘要:2026 年,AI 助理从"玩具"变成"工具"。本文带你了解 OpenClaw 生态系统的完整布局,看它如何连接微信、飞书、钉钉等主流平台,以及企业和个人如何利用它提…...
Nacos 2.2.0连接达梦数据库踩坑实录:从驱动版本到SQL脚本的完整避坑指南
Nacos 2.2.0与达梦数据库深度适配实战:从驱动选型到容器化部署的全链路解析 当微服务架构遇上国产数据库,技术适配的每个环节都可能成为关键战场。最近在将Nacos 2.2.0与达梦数据库进行生产级适配时,我经历了从驱动版本冲突到SQL脚本优化的完…...
如何用G-Helper实现CPU降压调优:华硕笔记本用户的散热与续航提升指南
如何用G-Helper实现CPU降压调优:华硕笔记本用户的散热与续航提升指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other mo…...
TM1651驱动LED条形图模块原理与嵌入式驱动开发
1. Whadda LED Bar Graph 模块技术解析与嵌入式驱动开发实践1.1 模块硬件架构与核心芯片特性Whadda WPI471 是一款基于 TM1651 驱动 IC 的 10 段 LED 条形图显示模块,广泛应用于嵌入式系统中的模拟量可视化指示场景,如电池电量、信号强度、温度梯度、音频…...
如何快速配置AdGuard广告拦截扩展:5分钟完成跨浏览器隐私保护的完整教程
如何快速配置AdGuard广告拦截扩展:5分钟完成跨浏览器隐私保护的完整教程 【免费下载链接】AdguardBrowserExtension AdGuard browser extension 项目地址: https://gitcode.com/gh_mirrors/ad/AdguardBrowserExtension AdGuard浏览器扩展是一款开源、高效的广…...
