Android 13(T) Media框架 - 智能指针

Android有一套自己的智能指针管理办法,并且将其运用在源码的各个角落,所以学习Media框架之前,我们有必要先了解下Android智能指针。
本节代码源自于Android 13(T),参考 (aospxref.com)

1 概述

与智能指针相关的总共有5个类,所以并不会很复杂,小白感觉有机会能看懂!
Android 13(T) Media框架 - 智能指针_第1张图片
RefBase.h一开始有一大段注释,我挑了一些进行了翻译:

  1. 一般来说,想要让指针可以使用sp<>和wp<>来管理,需要让指针类型继承于RefBase原型;
  2. 继承自RefBase的对象只有在引用减为0时才会被释放,不能通过其他方法被释放,所以这些对象不能在栈上创建,或者是直接将数据成员直接暴露给其他对象;
  3. 使用方法上,在一个相互包含的关系里推荐使用sp<>,没有相互包含的关系里用wp<>;两个对象不能同时拥有对方的sp<>成员,这将造成循环引用,导致内存无法被正确释放;
  4. wp<>可以看作是安全的指针,当对象被释放后promote方法会返回nullptr,这样可以避免空指针;
  5. 一般来说,当sp<>销毁后,其保存的对象也将销毁,但是有一部分相关的内存需要等到最后一个wp<>被销毁才会释放;extendObjectLifetime方法可以用来阻止“强引用计数为0而弱引用计数不为0”这种情况下,对象被释放;

可以看出智能指针的使用需要有很多注意点,了解智能指针,android源码才能理解的更加透彻。接下来就开始边学习边理解上述注意点的内容吧!


2 相关类型定义

2.1 引用计数

和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的计数成员来操作强引用计数,操作弱引用计数则需要通过调用其父类的方法来完成。


2.2 RefBase

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对象,而是要通过子类访问使用。


2.3 sp<>

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();
}

2.4 wp<>

一起来看看以下代码是否存在问题:

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尝试获取强指针:

  • 强引用计数不为0,那么直接返回一个sp对象,强引用计数加一
  • 强引用计数小于等于0,或者为初始值
    • flag为OBJECT_LIFETIME_STRONG,计数小于等于0则无法返回sp,计数为初始值(说明创建对象是时以wp存储)则返回一个sp,强引用计数加一
    • flag为OBJECT_LIFETIME_WEAK,强引用计数加一,返回一个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。这里以AHandlerALooper举个例子:ALooper中注册有AHandler对象,这里有包含关系所以使用spAHandler虽然持有ALooper,但是没有包含关系,所以使用wp

struct AHandler : public RefBase {
private:
	wp<ALooper> mLooper;
}

struct ALooper : public RefBase {
	handler_id registerHandler(const sp<AHandler> &handler);
}

以上就是我理解android智能指针的思路,到这里再看源码中形形色色的spwp,就知道他们是在传递什么,如何传递了。

你可能感兴趣的:(Android,android,c++)