Skip to content

sp与wp智能指针

追踪 libutils sp/wp 的复制、移动、清理、弱引用提升和线程安全边界。

基于android-17.0.0_r1
AndroidBinderspwpNative框架源码阅读

sp与wp智能指针 ​

sp<> 和 wp<> 是 Android RefBase 的两个句柄层:sp 表示强所有权,wp 只保存对象指针和控制块,必须通过 promote() 才能尝试取得强引用。它们的复制、移动、赋值和析构都直接调用 incStrong/decStrong 或 incWeak/decWeak;所以“把裸指针包进 sp”不是普通指针转换,而是一次所有权变更。

本文面向已经读过 RefBase引用计数、BpBinder代理对象 和 BBinder本地对象 的读者。本文专门讲模板句柄如何消费 RefBase,不重复控制块算法,也不把 sp/wp 与 std::shared_ptr 的语义混为一谈。

1. 强句柄 ​

1.1 构造策略 ​

源码文件:system/core/libutils/include/utils/StrongPointer.h

cpp
template <typename... Args>
static inline sp<T> make(Args&&... args);

sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
~sp();

源码推荐 sp<T>::make(),不推荐 sp<T> p = new T 的隐式构造,因为裸指针可能已经由其他 owner 管理,形成 double ownership。

1.2 复制移动 ​

cpp
template<typename T>
sp<T>::sp(const sp<T>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr) m_ptr->incStrong(this);
}

template<typename T>
sp<T>::sp(sp<T>&& other) noexcept
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}

复制增加强引用;移动只转移指针并把源置空,不增加计数。

1.3 清理 ​

cpp
template<typename T>
sp<T>::~sp() {
    if (m_ptr) m_ptr->decStrong(this);
}

template<typename T>
void sp<T>::clear() {
    T* oldPtr = m_ptr;
    if (oldPtr) {
        oldPtr->decStrong(this);
        m_ptr = nullptr;
    }
}

析构和 clear() 都可能触发对象析构以及 Binder 的最后 strong 回调。

2. 弱句柄 ​

2.1 构造复制 ​

源码文件:system/core/libutils/binder/include/utils/RefBase.h

cpp
template<typename T>
wp<T>::wp(const sp<T>& other)
        : m_ptr(other.m_ptr) {
    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}

template<typename T>
wp<T>::wp(const wp<T>& other)
        : m_ptr(other.m_ptr), m_refs(other.m_refs) {
    if (m_ptr) m_refs->incWeak(this);
}

从 sp 构造 wp 会创建弱引用;复制 wp 只增加控制块弱计数。

2.2 清理 ​

cpp
template<typename T>
wp<T>::~wp() {
    if (m_ptr) m_refs->decWeak(this);
}

template<typename T>
void wp<T>::clear() {
    if (m_ptr) {
        m_refs->decWeak(this);
        m_refs = nullptr;
        m_ptr = nullptr;
    }
}

wp 清理不直接删除对象,只减少弱引用。

2.3 比较语义 ​

空 sp/wp 的指针和控制块都为空;弱指针比较使用控制块身份,避免旧对象销毁后同一地址复用造成错误相等。

3. 弱引用提升 ​

3.1 promote ​

cpp
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;
}

promote() 返回新的 sp 或空值;调用者只有在返回非空时才能访问对象。ProcessState 的 handle 表则通过 attemptIncWeak() 判断代理控制块是否仍可复用。

3.2 fromExisting ​

sp<T>::fromExisting() 和 wp<T>::fromExisting() 只用于已知已有引用的内部场景;后者没有现存弱引用会 fatal,不是普通业务构造器。

4. 赋值与线程 ​

4.1 sp赋值 ​

cpp
template<typename T>
sp<T>& sp<T>::operator=(const sp<T>& other) {
    T* oldPtr = m_ptr;
    T* otherPtr = other.m_ptr;
    if (otherPtr) otherPtr->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

先增加新对象再减少旧对象,避免自赋值时短暂归零。

4.2 线程安全 ​

不同 sp<>/wp<> 实例可并发指向同一对象;同一个实例存在写入时不具备线程安全。引用计数原子化不保护 m_ptr、业务字段或析构后的访问。

5. 测试边界 ​

5.1 移动与弱复制 ​

源码文件:system/core/libutils/binder/RefBase_test.cpp

StrongMoves 断言移动 sp 不增加强计数且源句柄置空;WeakCopies 断言复制 wp 增减弱计数,并验证清理边界。

5.2 提升与地址复用 ​

NoStrongCountPromoteFromWeak 验证弱引用提升;ReplacedComparison 让旧对象销毁后在同一地址创建新对象,断言旧 wp 不等于新对象。

5.3 可执行阅读 ​

bash
rg -n "class sp|sp<T>::sp|sp<T>::~sp|sp<T>::operator=|sp<T>::clear" \
  system/core/libutils/include/utils/StrongPointer.h

rg -n "class wp|wp<T>::wp|wp<T>::~wp|wp<T>::promote|wp<T>::clear" \
  system/core/libutils/binder/include/utils/RefBase.h

rg -n "StrongMoves|WeakCopies|NoStrongCountPromoteFromWeak|ReplacedComparison" \
  system/core/libutils/binder/RefBase_test.cpp

排查对象提前释放时,先看是 sp 强引用归零还是 wp 控制块归零,再确认是否错误使用裸指针、release() 或 fromExisting();安全访问弱对象应调用 promote(),不要使用 unsafe_get() 解引用。