android底层库libutils之string8,string16研究

JNI使用的一些细节和用法 本文章分二部分: 一.首先看如何绑定JNI和JAVA的方法 我们要特别留意下JNI_OnLoad,百度下之后: 当Android的VM(Virtual Machine)执行到C组件(即so档)里的System.loadLibrary()函数时, 首先会去执行C组件里的JNI_OnLoad()函数。 它的用途有二: . 告诉VM此C组件使用那一个JNI版本。 如果你的.so档没有提供JNI_On... 阅读详情
AI摘要

本文详细解析了Android系统中String8类与SharedBuffer类的关系,介绍了String8如何利用SharedBuffer进行内存管理,以及SharedBuffer类的内部实现细节。

一、sharedBuffer&string如何牵手的      

在之前看android camera framework代码时,发现代码中大量使用了string8、string16类。由于之前学习的是C#,java等高级面向对象语言唯独没有认真研究过C++。所以对C++中的string比较陌生。虽然他们的用法都是一样的,但是这里还是想认真研究一下底层实现原理。

     string8和string16原理基本上是一样的,这里我们就分析常用的string8了,说到string8,我们就能想到和它有千丝万缕的sharedbuffer类。string8的内存就是通过sharebuffer类来管理的。每个string8对象对应一个sharebuffer的对象,sharedBuffer对象的下面就是我们的string8的内存空间。string8类中有一个mString的私有域就是指向sharedBuffer下面开始处的,如下图所示。



    为了说明string8对象就是sharedBuffer下面的内存空间,我们来看看shredBuffer对象的的的alloc方法。可以看到在申请了sizeof(SharedBuffer)+size大小的空间,其中size就是我们字符串大小。

SharedBuffer* SharedBuffer::alloc(size_t size)
{
    SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
    if (sb) {
        sb->mRefs = 1;
        sb->mSize = size;
    }
    return sb;
}

上面的sharedBuffer类我们只看到确实是多申请了Buffer,没看到是不是string8真的指向sharedBuffer对象下面的空间呢。我们来看看String8常用构造函数,下面的调用了allocFromUTF8方法。我们继续来到allocFromUTF8 慌然看到了sharedBuffer::alloc()方法,而且申请的大小是len+1,大家想想为啥还要+1.考虑一会...................

时间到了,大家在学习C语言的时候,都要在最后加一个'\n'换行符,也就是下面的str[len]=0;这点我思考了10分钟。哎。我们在看看这行代码char* str = (char*)buf->data();针对sharedBuffer的data方法,我们在下面会看到返回的是this+1.由于this指针的类型是sharedBuffer类型,所以+1就意味这加了sizeof(sharedBuffer)大小,加上这么大的大小就指向了紧挨着sharedBuffer的空间了,就是上图所示那样。不知道大家明白了没有。

String8::String8(const char* o)
    : mString(allocFromUTF8(o, strlen(o)))  //调用<span style="font-family:SimSun;">下面的函数</span>
{
    if (mString == NULL) {
        mString = getEmptyString();
    }
}

static char* allocFromUTF8(const char* in, size_t len)
{
    if (len > 0) {
        SharedBuffer* buf = SharedBuffer::alloc(len+1); //为什么<span style="font-family:SimSun;">多+1???</span>
        ALOG_ASSERT(buf, "Unable to allocate shared buffer");
        if (buf) {
            char* str = (char*)buf->data();  //笔锋转到下面的data方法
            memcpy(str, in, len);
            str[len] = 0;
            return str;
        }
        return NULL;
    }

    return getEmptyString();
}

//shardBuffer的data方法
const void* SharedBuffer::data() const {
    return this + 1;
}

二、sharedBuffer类介绍 

  下图是sharedBuffer类的全部属性和部分方法。从名字上来看,该类视乎和共享buffer有关系,其实仔细看看代码,它就只是对底层标准malloc等c库函数的再一次封装。我们就来给大家介绍几个我自认为重要的方法吧。




sharedBuffer::editResize()方法就是重新设置对象的大小。它的实现相对来说还是很简单的(其实就没难的地方,呵呵)。此方法在string8::lockBuffer()方法中有用到,在调用lockbuffer时,如果只有对象本身在用自己的buffer,那么就把自己的内存空间变小了(自我解脱了),如果还有别的引用对象在使用这片buffer的话,可以使用该引用对象调用lockbuffer(),那么它就会重新申请内存空间,并拷贝我们指定大小的空间,保证了源对象不会别破坏。好了,我们来说说editResize()函数吧,它的字面意思就是重新定义对象的大小了,具体它的注释,我就写在代码中了。如果想看代码,可以下载 附件。
SharedBuffer* SharedBuffer::editResize(size_t newSize) const
{
    if (onlyOwner()) { //这里判断是不是只有owner在用这个sharedBuffer对象,具体代码请看附件吧
        SharedBuffer* buf = const_cast<SharedBuffer*>(this);//拿到当前对象的指针
        if (buf->mSize == newSize) return buf;
        buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); //重新申请一个sharedBuffer对象,realloc会申请制定大小的内存并拷贝
        if (buf != NULL) {
            buf->mSize = newSize;
            return buf;  //返回这个新建的sharedBuffer对象,看来这只能在sharedBuffer类中使用。
        }
    }
    SharedBuffer* sb = alloc(newSize); //如果不只有一个拥有者,存在引用对象,那么就重新申请一个sharedBuffer对象,防止对源数据的影响。
    if (sb) {
        const size_t mySize = mSize;
        memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); //拷贝数据
        release(); //释放当前对象了。
    }
    return sb;//返回新创建的sharedBuffer对象
}
这个函数就是根据当前string对象,找到它的知己sharedBuffer对象,奇怪的地方就是代码中为什么-1,
SharedBuffer* SharedBuffer::bufferFromData(void* data) {
    return data ? static_cast<SharedBuffer *>(data)-1 : 0;  //为什么要-1,减1就指向了sharedBuffer对象地址了。
}
size_t SharedBuffer::sizeFromData(const void* data) {
    return data ? bufferFromData(data)->mSize : 0; //这是给sring类调用,用于返回string对象内存大小
}

bool SharedBuffer::onlyOwner() const {
    return (mRefs == 1); //引用计数器为1时,只有创建者自己在使用此对象。
}

三、string8类介绍

下图是string8全部的属性和部分方法,同样这里我也只贴出部分我自认为重要的方法,来分析一下,详细伙计们还是看源码吧。



下面这个方法一看大家都明白,就是为当前string对象设置数据了
status_t String8::setTo(const char* other)
{
    const char *newString = allocFromUTF8(other, strlen(other)); //申请新的内存空间,其实生成了新的sharedBuffer对象
    SharedBuffer::bufferFromData(mString)->release(); //释放旧的内存空间
    mString = newString;
    if (mString) return NO_ERROR;

    mString = getEmptyString();
    return NO_MEMORY;
}

void String8::setTo(const String8& other)
{
    SharedBuffer::bufferFromData(other.mString)->acquire(); //被引用的string8对象,引用计数+1.
    SharedBuffer::bufferFromData(mString)->release();
    mString = other.mString;
}
status_t String8::append(const char* other) //在原有数据基础上,添加对应的数据,还是看源码吧^_^
{
    return append(other, strlen(other));
}
此函数有点不太好理解,我在本地调试时,试了很多情况,结果也没变化。后来发现原来是引用计数+1,-1原子操作时存在问题。当一个对象存在多个引用对象时,调用lockBuffer()方法,会返回一个新的string对象,以保护源数据。
char* String8::lockBuffer(size_t size)
{
    SharedBuffer* buf = SharedBuffer::bufferFromData(mString) //找到当前string对象的好基友sharedBuffer对象
        ->editResize(size+1);
    if (buf) {
        char* str = (char*)buf->data();
        mString = str;
        return str;
    }
    return NULL;
}

四、测试代码

下面只列出了main函数的实现内容,完整的代码大家可以下载 附件,这里就是演示一下常用的函数。
int main(int argc, char **argv)
{
	android::initialize_string8();
	android::initialize_string16();
// start of your code

	String8 str1("armwind");
	String8 str2(" is a good man! ^_^");
	String8 add8 = str1 + str2; //这个在类中有重载运算符的实现
	cout<<"name:"<<add8.string()<<endl;
	add8.toUpper();
	cout<<"toUpper:"<<add8.string()<<endl;
        String8 &str4 =add8; //下面3行都是在后来添加的
        str4.setTo(str3); //add8的引用计数会+1
        str4.setTo(str3);
        cout<<"lockBuffer:"<<add8.lockBuffer(8)<<endl;//这里调用lockBuffer,会返回一个字符串,显示的只是我们想要访问的8字节。
	String8 str3 = add8;
	cout<<"lockBuffer_test:"<<str3.string()<<endl;

	add8.setTo("hello");//重新设置成某个字符串
	cout<<"name:"<<add8.string()<<endl;

	add8.append(" is a good boy!"); //扩展
	cout<<"name:"<<add8.string()<<endl;

	add8.clear();
	cout<<"clear:"<<add8.string()<<endl;
	cout<<"format:"<<String8::format("hello %c","world").string()<<endl;

//end of your code
	android::terminate_string16();
	android::terminate_string8();

    return kPassed;
}

打印结果:
name:armwind is a good man! ^_^
toUpper:ARMWIND IS A GOOD MAN! ^_^
lockBuffer:ARMWIND IS A GOOD MAN! ^_^
lockBuffer_test:ARMWIND IS A GOOD MAN! ^_^
name:hello
name:hello is a good boy!
clear:
format:hello p

分析:上面的代码只是部分main函数,完整的代码大家可以下载 附件,除了initialize_string()和terminate_string()函数,其它的函数都是在探究一下string8的特性。目前在调用format函数时,输出的结果并不是我们意向中的“hello world”,这里可能在移植到x86下,一些数据类型不匹配导致的吧。具体原因我就不找了,如果大家找到原因了,可以贴到评论上^_^,此外示例源码中android_atomic_add()函数也存在问题,即在执行+1,-1操作时,没有将修改后的值赋给引用计数器(大家下载下来自己动手改改吧,我就不上传了)。这样就会导致后面lockbuffer()时探索遇到了点问题。

开始和结尾的init代码(如下所示),本来是在static.cpp代码中调用的,这里由于不想在加一个源文件,就直接在这里调用了。
    android::initialize_string8();
    android::initialize_string16();

    android::terminate_string16();
    android::terminate_string8();
下面可以看到,在类的构造函数和析构函数中,调用了相应的初始化函数和终结函数。而在类声明的下方就定义了LibUtilsFirstStatics 对象 gFirstStatics 该对象其它文件中也没调用,也许就是为了调用init函数和终结函数吧。
class LibUtilsFirstStatics
{
public:
    LibUtilsFirstStatics()
    {
        initialize_string8();
        initialize_string16();
    }
    
    ~LibUtilsFirstStatics()
    {
        terminate_string16();
        terminate_string8();
    }
};

static LibUtilsFirstStatics gFirstStatics;

五、调试过程中遇到的问题

1) 在调试过程,由于命名空间的使用问题,导致一些方法找不到,编译不过。解决办法就是添加对应的命名空间

2) 假如在类A中使用了类B,在没有包含类B时,编译时提示找不到类B相应的方法。解决办法就是包含对应的类,如直接包含class String8; 结构体也可以这样包含。

3) main函数不能包含在命名空间中,要不然编译会出问题的。



android app 十六进制字符串 String byte数组互转 在app的开发中,对于数据传输的相关功能中 byte数组和字符串互转的功能也是常见功能 byte数组 转十六进制字符串 十六进制字符串转换成字符串 的相关功能 2.2 java版实现功能的相关代码 3.总结 对于相关方法的说明,在代码中做了注释说明,按照说明理解相关代码就可以了...... 阅读详情

相关推荐

android字符串和16进制转换

android 的字符串 ascII码 16进制的相互转换

字符串类String8

仿Android 工具类String8写的 /************************************************************************* > File Name: string8.h > Author: hsz > Mail: > Created Time: 2021年04月11日 星期日 12时24分52秒 *******************************************

DefiniteGoal的博客 2704

测试人必看!正交表与Allpairs工具对比:如何选择最适合你的组合测试方案?

本文深入对比了正交表、PICT与Allpairs三种主流组合测试方案,帮助测试人员根据项目场景做出最佳选择。文章分析了各自的核心原理、优劣势及适用场景,特别强调了在涉及复杂约束时PICT工具的强大功能,并提供了从原理学习到CI/CD集成的实战选型指南。

weixin_29224309的博客 267

C++实现字符串类型(String16/String8/const char/char)转换

C++实现字符串类型(String16/String8/const char/char)转换     最近在C++开发的时候遇到这么个问题,需要对函数参数中传来的String16参数进行大小写统一的转换,并且转换后的数据类型还是String16.下面就介绍一下各类数据类型的转换。     首先是String16到String8的转换: String16 name16 = St

☃Clay's♂Blogs 2万+

Mixer-First Receiver适用于宽带阵列应用的接收机技术1

Mixer-First Receiver适用于宽带阵列应用的接收机技术1技术的背景单元级数字化阵列传统链路到Mixer-First链路的逻辑考虑三种场景小结 技术的背景 在文章《吊打全球的顶级毫米波数字阵列项目-MIDAS》中提到了DARPA在MIDAS项目中在毫米波实现宽带单元级数字化阵列用到了一个关键的叫做N-Path filter的技术,而这个技术同样出现在了《让微波射频工程师颤抖的芯片-MATRICs》的MATRICs芯片中。 为什么DARPA的各个合作研究机构选择这种技术?这篇来自Universi

ICONO的博客 2167

RiscV运行zephyr和Linux的方法

riscv基金会搞了一个网站,用于介绍如何使用riscv指令集进行开发。 对于运行zephyr和linux以及Qemu有非常大的帮助。 可以作为riscv开发的相关参考。 RISC-V - Getting Started Guide — RISC-V - Getting Started Guidehttps://risc-v-getting-started-guide.readthedocs.io/en/latest/index.html RISC-V - Getting Started Gu.

闲人的专栏 736

android string16,android底层库libutils之string8,string16研究

一、sharedBuffer&string如何牵手的在之前看androidcameraframework代码时,发现代码中大量使用了string8、string16类。由于之前学习的是C#,java等高级面向对象语言唯独没有认真研究过C++。所以对C++中的string比较陌生。虽然他们的用法都是一样的,但是这里还是想认真研究一下底层实现原理。string8和string16原理基本上...

weixin_35399360的博客 1695

【ComfyUI】Flux 图像扩展

本文介绍了一个基于Flux扩图技术的ComfyUI工作流,能够智能扩展图像尺寸并保持内容连贯性。该流程通过扩散模型与条件编码的结合,对图像边缘进行语义填充生成,适用于艺术创作、海报设计等场景。工作流包含图像加载、外扩填充、文本编码、扩散采样等核心节点,采用Flux1-fill-dev等专业模型确保生成质量。演示效果显示,系统能自然延伸原图内容,保持风格统一。该技术可广泛应用于插画扩展、摄影增强、游戏场景构建等领域,显著提升创意工作效率。

Mr数据杨 927

android string16,android底層庫libutils之string8,string16研究

一、sharedBuffer&string如何牽手的在之前看androidcameraframework代碼時,發現代碼中大量使用了string8、string16類。由於之前學習的是C#,java等高級面向對象語言唯獨沒有認真研究過C++。所以對C++中的string比較陌生。雖然他們的用法都是一樣的,但是這里還是想認真研究一下底層實現原理。string8和string16原理基本上...

weixin_29113371的博客 1105

【软考高级架构】案例题考前突击6——大数据架构

前一类的数据主要由流量方以接口的方式提供(比如对接的腾讯广点通平台),后一类则是某网特有的数据,通过买家的浏览、下单、付款日志算出来。1. 在数据层面只维护一张包含所有指标的MySQL表,表中的stday字段作为索引,stday=当天的保存实时数据,stday<当天的保存离线数据。典型的大数据架构,除了Lambda架构之外,还有kappa架构,这两个架构的区别如下标所示,请补充表中空(1)-(4)第二,实时数据只对接了内部的kafka消息,没有实时的获取第三方的曝光、点击、浏览数据。

qq837993702的博客 66

使用Python爬虫获取Facebook帖子和评论数据

Web爬虫是用于自动抓取网页数据的程序。它通过发送HTTP请求,获取网页的HTML源代码,再对源代码进行解析,提取所需的内容。爬虫常用于数据收集、信息提取、数据分析等应用。

2201_76125261的博客 1998

Android的底层库libutils介绍

Android的底层库libutils介绍   第一部分 libutils概述   libutils是Android的底层库,这个库以C++实现,它提供的API也是C++的。Android的层次的C语言程序和库,大都基于libutils开发。   libutils中的头文件如下所示:   frameworks/base/include/utils   libutils的源文件:   f

奔跑的蜗牛 5702

工艺边与定位孔设计经验规则总结

工艺边是在SMT焊接时,为了PCB和导轨接触预留的PCB边。工艺边和PCB的接触部分一般需要开槽,或者使用邮票孔,以方便后期去除工艺边。

fydar的博客 1982

C++ String16/String8与const char*及char*与vector相互转换(五十一)

1.String16转const char* String16 name16 = String16("Hello World"); String8 name8 = String8(name16); const char *buffer = name8.string(); 验证: printf("buffer = %s\n",buffer); 注意: String16.string()其实是c...

Android系统攻城狮 8762

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

android::String8与Java String对比

【代码】android::String8与Java String对比。

performance 1117

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++ string(八)

#include const int SIZE = 5; using namespace std; void stringTest(); int main(int argc, const char * argv[]) { stringTest(); return 0; } void stringTest() {

爱技术....爱生活! 781

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C++ string类(上)

拷贝构造函数。

XBHJBH的博客 491

Android String8.h

/frameworks/native/include/utils/String8.h

w6028819321的专栏 8239

【定制Android系统】String16 打印、 String16 转 int 、 int 转 String16

String16 转 int 其实一切的一切,只需要一件事,那就是。借助 android::String8。 查看 String16 的头文件,其中有个private 的函数。 inline std::string String16::std_string(const String16&amp; str) { return std::string(String8(str).strin...

我是一只小小小小鸟 7094

字符串按8位截取输出

输入描述:连续输入字符串(输入2次,每个字符串长度小于100) 输出描述:输出到长度为8的新字符串数组 示例1输入abc 123456789输出abc00000 12345678 90000000这个解法厉害了,牛客上的解法 ,自己写的特别复杂 还只过了80% 。充分利用了String 的substring方法。 直接println(s);就可以了 不用什么charAt(i);s.substrin...

qq_33608638的博客 3639

C++ string类

先看begin和end他们是一个获取首位置,以获取尾位置,对于end来说它获取的位置为有效位的下一个。

2401_83191546的博客 780

Android系统的问题分析笔记(7) - Android上的C++底层库都有啥 ?

在Android系统中,Bionic是最基础的C库,但它仅仅是基础库。对于开发者而已,工具库才是真正在编写Native程序时更多被使用到的。主要的工具库在系统源码的/system/core目录中,比如:liblog、libcutils、libutils等。!defined。

Android系统工程师--小馬佩德罗 1500

string8 string16 char*

jstring ifaceStr, jstring opPackageNameStr String16(opPackageName.c_str()),client, String8(iface.c_str())2.String16name16=String16("HellOThEWrolD"); String8name8=String8...

weixin_34273046的博客 1070
上一篇: C++中结构体和类
下一篇: Android Camera之CameraMetadata分析
armwind
博客等级 码龄12年 406粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值