Android有一套自己的智能指针管理办法,并且将其运用在源码的各个角落,所以学习Media框架之前,我们有必要先了解下Android智能指针。
本节代码源自于Android 13(T),参考 (aospxref.com)
与智能指针相关的总共有5个类,所以并不会很复杂,小白感觉有机会能看懂!
RefBase.h一开始有一大段注释,我挑了一些进行了翻译:
可以看出智能指针的使用需要有很多注意点,了解智能指针,android源码才能理解的更加透彻。接下来就开始边学习边理解上述注意点的内容吧!
和std::shared_ptr相同,android智能指针也采用引用计数来管理堆对象的自动释放。引用计数简单来说就是记录有多少使用者在使用同一个对象,每多一个使用者计数加一,每少一个计数减一,当计数为0时就释放该对象。
Android为我们提供引用计数管理的类为weakref_type
和其子类weakref_impl
,定义如下:
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
std::atomic<int32_t> mStrong;
std::atomic<int32_t> mWeak;
RefBase* const mBase;
std::atomic<int32_t> mFlags;
explicit weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(OBJECT_LIFETIME_STRONG){
}
}
class weakref_type
{
public:
RefBase* refBase() const;
void incWeak(const void* id);
void incWeakRequireWeak(const void* id);
void decWeak(const void* id);
bool attemptIncStrong(const void* id);
bool attemptIncWeak(const void* id);
int32_t getWeakCount() const;
}
weakref_impl
使用mStrong用于强引用计数,mWeak用于弱引用计数,mBase记录指针,mFlags作为标志,其他方法为空实现。RefBase通过直接访问weakref_impl
的计数成员来操作强引用计数,操作弱引用计数则需要通过调用其父类的方法来完成。
RefBase
类持有以上引用计数对象,并向外提供修改引用计数的接口。
class RefBase
{
public:
void incStrong(const void* id) const;
void incStrongRequireStrong(const void* id) const;
void decStrong(const void* id) const;
void forceIncStrong(const void* id) const;
int32_t getStrongCount() const;
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
private:
weakref_impl* const mRefs;
实例化RefBase时会创建一个weakref_impl
,并将RefBase本身传给weakref_impl
。
RefBase::RefBase()
: mRefs(new weakref_impl(this)){ }
RefBase
提供有三个增加强引用计数的方法:incStrong是在第一次创建RefBase对象时使用、incStrongRequireStrong是在有其他引用时调用,forceIncStrong似乎使用的比较少。
以RefBase::incStrong()方法为例,先调用weakref_type
的incWeak方法来增加一个弱引用计数mWeak,然后再直接修改mStrong增加强引用计数。
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id);
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
if (c != INITIAL_STRONG_VALUE) {
return;
}
int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
refs->mBase->onFirstRef();
}
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
const int32_t c __unused = impl->mWeak.fetch_add(1,
std::memory_order_relaxed);
}
我们可以观察到,强引用计数加一,弱引用计数就会对应加一,那么什么时候两种计数方法会出现不同呢?这里先留下一个疑问。
减少强引用计数只有一个接口decStrong,以下是简化的代码:
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
if (c == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
refs->mBase->onLastStrongRef(id);
int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
delete this;
}
}
refs->decWeak(id);
}
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
}
const_cast<weakref_impl*&>(mRefs) = nullptr;
}
当强引用计数为0时,将会去检查mFlag的值,如果是OBJECT_LIFETIME_STRONG,那么就释放当前对象,这边要注意的是析构函数中并没有释放mRefs。释放完对象之后还会将弱引用计数减一,当弱引用计数为0时,mRefs被释放。
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
if (c != 1) return;
atomic_thread_fence(std::memory_order_acquire);
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
if (impl->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
} else {
delete impl;
}
} else {
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
mFlag为OBJECT_LIFETIME_WEAK的情况下,如果强引用计数减为0,不会立即释放当前对象,需要等到弱引用计数为0,才会释放对象,释放对象的过程中会同步释放mRefs。
RefBase
暂时就先了解这么多,最后有一点要注意的是,RefBase
的构造函数和析构函数的访问权限均为protected,也就是说我们不能直接创建RefBase
对象,而是要通过子类访问使用。
sp
即StrongPinter,可以帮我们完成强指针引用计数的管理。创建对象或者有新增对象引用时自动帮我们执行incStrong:
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
m_ptr->incStrong(this);
}
对象使用完成时自动执行decStrong:
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
由于智能指针是用于管理堆上创建的对象的,所以sp
还会帮我们检查创建的对象是否在堆上。以MediaCodec为例,析构函数访问权限为protected,我们则不能创建栈上的对象,堆上的对象也不能用delete来释放,这样只能使用智能指针来管理,变得更加安全。
struct MediaCodec : public AHandler {
protected:
virtual ~MediaCodec();
}
一起来看看以下代码是否存在问题:
class B;
class A : public RefBase
{
public:
A() { std::cout << "create A"<< std::endl; }
void setPointer(sp<B> B) { p = B; }
protected:
~A() { std::cout << "delete A" << std::endl; }
private:
sp<B> p;
};
class B : public RefBase
{
public:
B() { std::cout << "create B " << std::endl; }
void setPointer(sp<A> A) { p = A; }
protected:
~B() { std::cout << "delete B "<< std::endl; }
private:
sp<A> p;
};
int main() {
{
sp<A> a(new A);
sp<B> b(new B);
printf("%d %d\n", a.getStrongCount(), b.getStrongCount());
a->setPointer(b);
b->setPointer(a);
printf("%d %d\n", a.getStrongCount(), b.getStrongCount());
}
/*
create A
create B
1 1
2 2
*/
}
可以看到代码执行结束时并没有打印delete A 和 delete B,A和B的内存没有被正确释放。b先出作用域,此时b的引用计数从二变成一,因此b不会被释放,b中存储的a也没有被释放;a后出作用域,a的引用计数从二变成一,因此a也没有被释放。以上代码被称为循环引用,会造成内存泄漏。Android提供了wp
来解决循环引用造成的问题。
wp
即WeakPointer,创建wp
对象时只会让弱引用计数加一,强引用计数并不会增加,这时候强弱引用计数就不相同了(这也就是上面疑问的答案)。由于强引用计数没有加一,因此也就不会形成循环引用。
template<typename T>
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
const int32_t c __unused = impl->mWeak.fetch_add(1,
std::memory_order_relaxed);
}
wp
可以避免出现循环引用,那我们要如何使用wp
呢?
下面是wp
的声明,由于没有重载*
、->
,所以并不能直接操作内部的指针成员,只有通过promote方法获得一个sp
来间接使用内部指针。
class wp
{
public:
sp<T> promote() const;
private:
T* m_ptr;
weakref_type* m_refs;
}
promote会调用attemptIncStrong尝试获取强指针:
sp
对象,强引用计数加一sp
,计数为初始值(说明创建对象是时以wp
存储)则返回一个sp
,强引用计数加一sp
我们可以对promote的返回结果判空,避免空指针的使用。
template<typename T>
sp<T> wp<T>::promote() const
{
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
return result;
}
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
if (curCount <= 0) {
decWeak(id);
return false;
}
while (curCount > 0) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
} else {
curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
impl->mBase->onLastStrongRef(id);
}
}
}
if (curCount == INITIAL_STRONG_VALUE) {
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
}
return true;
}
RefBase.h中已有说明,在一个相互包含的关系里推荐使用sp
,没有相互包含的关系里用wp
。这里以AHandler
和ALooper
举个例子:ALooper
中注册有AHandler
对象,这里有包含关系所以使用sp
,AHandler
虽然持有ALooper
,但是没有包含关系,所以使用wp
。
struct AHandler : public RefBase {
private:
wp<ALooper> mLooper;
}
struct ALooper : public RefBase {
handler_id registerHandler(const sp<AHandler> &handler);
}
以上就是我理解android智能指针的思路,到这里再看源码中形形色色的sp
、wp
,就知道他们是在传递什么,如何传递了。