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

一、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下面开始处的,如下图所示。

f99ca47eb5218ad4b6d92f63bce0b086.png

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

SharedBuffer* SharedBuffer::alloc(size_t size)

{

SharedBuffer* sb = static_cast(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))) //调用下面的函数

{

if (mString == NULL) {

mString = getEmptyString();

}

}

static char* allocFromUTF8(const char* in, size_t len)

{

if (len > 0) {

SharedBuffer* buf = SharedBuffer::alloc(len+1); //为什么多+1???

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库函数的再一次封装。我们就来给大家介绍几个我自认为重要的方法吧。

db9ce7728a4a7c0a9e7b6ddd45ae6b7d.png

sharedBuffer::editResize()方法就是重新设置对象的大小。它的实现相对来说还是很简单的(其实就没难的地方,呵呵)。此方法在string8::lockBuffer()方法中有用到,在调用lockbuffer时,如果只有对象本身在用自己的buffer,那么就把自己的内存空间变小了(自我解脱了),如果还有别的引用对象在使用这片buffer的话,可以使用该引用对象调用lockbuffer(),那么它就会重新申请内存空间,并拷贝我们指定大小的空间,保证了源对象不会别破坏。好了,我们来说说editResize()函数吧,它的字面意思就是重新定义对象的大小了,具体它的注释,我就写在代码中了。如果想看代码,可以下载 附件。

SharedBuffer* SharedBuffer::editResize(size_t newSize) const

{

if (onlyOwner()) { //这里判断是不是只有owner在用这个sharedBuffer对象,具体代码请看附件吧

SharedBuffer* buf = const_cast(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(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全部的属性和部分方法,同样这里我也只贴出部分我自认为重要的方法,来分析一下,详细伙计们还是看源码吧。

8f89f03b7d273e4509e831fab0ef5f2e.png

下面这个方法一看大家都明白,就是为当前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: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函数不能包含在命名空间中,要不然编译会出问题的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值