研究Android的时候,经常会遇到sp、wp的东西,网上一搜,原来是android封装了c++中对象回收机制。
说明:
1. 如果一个类想使用智能指针,那么必须满足下面两个条件:
    a. 该类是虚基类RefBase的子类或间接子类
    b. 该类必须定义虚构造函数。如virtual ~MyClass();

 

2. 本文以类BBinder来进行说明,其余类使用sp或wp的情况类似
3. 代码路径:frameworks/base/libs/utils/RefBase.cpp
       frameworks/base/include/utils/RefBase.h

一、calss BBinder类说明
      class RefBase
      class IBinder
 class BpBinder   class BBinder
 class BBinder : public IBinder
 {
 ...
 protected:
     virtual             ~BBinder();
 ...
 }
 class IBinder : public virtual RefBase
 {
 ...
 protected:
     inline virtual      ~IBinder() { }
 ...
 }
 由上,可以看出BBinder和IBinder都是以public的方式继承于虚基类RefBase的。

二、sp wp对象的建立过程
 解析:sp<BBinder>  BB_ptr(new BBinder);
 这是一条定义sp指针BB_ptr的语句,他只想的对象是一个BBinder对象。
 如图所示。
 

1》首先看一下new BBinder时都做了什么,特别是和该机制相关的初始化。
   c++中创建一个对象时,需要调用去构造函数,对于继承类,则是先调用其父类的构造函数,然后才会调用本身的
   构造函数。这里new一个BBinder对象时,顺序调用了:
    RefBase::RefBase() : mRefs(new weakref_impl(this)) {}
    inline   IBinder() {}
    BBinder::BBinder() : mExtras(NULL){}
   主要关注的是RefBase的构造函数,
   可以看出他是通过new weakref_impl(this)的结果来初始化私有成员mRefs
   这里的this指向BBinder对象自身,class weakref_impl继承于类RefBase的内嵌类weakref_type,然后该类
   weakref_impl又被类RefBase引用。类weakref_impl的构造函数如下:
   weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)    // 1 << 28
        , mWeak(0)
        , mBase(base)             // new BBinder指针
        , mFlags(0)
        , mStrongRefs(NULL)          // sp引用链表指针
        , mWeakRefs(NULL)           // wp引用链表指针
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) // 1
        , mRetain(false) {}
   
 2》new BBinder返回的是BBinder对象的指针,如:sp<BBinder>  BB_ptr(0x????????);
   sp实际上是一个类模板,这条语句最终是要建立一个sp的实例化对象,叫模板类BB_ptr
   这里生成BB_ptr对象所调用的构造函数是:
   template<typename T>
   sp<T>::sp(T* other)
       : m_ptr(other)
   {
       if (other) other->incStrong(this);
   }
   BB_ptr对象的私有指针指向刚刚前面生成的BBinder对象。
   接着调用函数incStrong(),该函数是RefBase类的成员函数,在子类中没有被重载,所以这里
   other->incStrong(this)的调用实际上是调用基类成员函数incStrong(this),这个this值是指向sp对象
   BB_ptr的指针。现在转去查看该成员函数的实现。
   
   void RefBase::incStrong(const void* id) const
   {
       weakref_impl* const refs = mRefs;
       /* 取得BBinder对象基类中的私有只读指针mRefs */
       refs->addWeakRef(id);
       /* 调用weakref_impl类定义时实现的成员函数addWeakRef, 见下注释1*/
       refs->incWeak(id);
       /* 调用weakref_impl类的基类weakref_type成员函数incWeak, 见下注释2*/
       
       refs->addStrongRef(id);
       // 调用weakref_impl类定义时实现的成员函数addStrongRef, 见下注释1
       const int32_t c = Android_atomic_inc(&refs->mStrong); 
     /* 该函数实际将refs->mStrong值加1,也就是增加强引用计数值。但是返回值为refs->mStrong-1 */
       LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
   #if PRINT_REFS
       LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
   #endif
       if (c != INITIAL_STRONG_VALUE)  {
           return;
       }
     /* c = INITIAL_STRONG_VALUE, 第一个强引用产生的时候才会出现这个情况 */
       Android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
     /* 返回值为INITIAL_STRONG_VALUE,refs->mStrong值变成1 */
       const_cast<RefBase*>(this)->onFirstRef();
   }
   
/************************注释1********************************/
void addWeakRef(const void* id)
{
    addRef(&mWeakRefs, id, mWeak);
}
void addStrongRef(const void* id)
{
    addRef(&mStrongRefs, id, mStrong);
}
addRef()是类weakref_impl的私有成员函数,addWeakRef()函数引用的是public成员变量,而addRef()函数可以操作私有数据。

struct ref_entry
    {
        ref_entry* next;
        const void* id;
        int32_t ref;
    };
    
void addRef(ref_entry** refs, const void* id, int32_t mRef)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);
            ref_entry* ref = new ref_entry;
            ref->ref = mRef;
            ref->id = id;
            
            ref->next = *refs;
            *refs = ref;
   /*
   新出现的ref_entry结构体加入到链表头上,如果有n个sp指针指向同一个目标对象
   那么这里就有n个ref_entry结构体加入到这个单链表中,该结构体记录着如下数据
   1. id域记录着对应的sp强指针类对象的this值
   2. ref域记录的是当前sp强指针类对象是第几个引用目标对象的指针
   3. next域指向下一个指向目标对象的sp强指针对应的ref_entry结构体
   
   类RefBase的嵌套类weakref_type的子类的私有数据mRefs的私有二级指针成员mWeakRefs指向的是
   最后一个sp强指针对应的ref_entry结构体指针。

总结一下:
   一个目标对象,可能被n个sp强指针指向,那么就存在n个class sp对象,同时每一个sp
   对象在目标对象的虚基类对象的成员类mRefs的私有二级指针成员mWeakRefs登记了一个
   ref_entry结构体,这些ref_entry结构体的地址都是由该链表管理,每一个
   ref_entry结构体和哪一个sp对象对应,也由该链表管理。同时链接数就是该链表节点的
   个数
   */
        }
    }
/************************注释1********************************/

/************************注释2********************************/
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    // 强制类型转换,将基类指针转换成子类指针
    impl->addWeakRef(id); 
    // 调用类weakref_impl成员函数addWeakRef(),产生一个ref_entry结构体挂载mWeakRefs链表上
    const int32_t c = Android_atomic_inc(&impl->mWeak);
  /* impl->mWeak加1,表示已存在一个weak引用。但返回值c为操作前的结果 */
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
/************************注释2********************************/

3》上面是定义一个sp指针,下面看看定义一个wp指针式如何实现的。
     wp<BBinder>  BB_wp_ptr(BB_ptr);
    下面是wp类对应上面定义类型的构造函数
   template<typename T>
   wp<T>::wp(const sp<T>& other)
       : m_ptr(other.m_ptr)
   {
       if (m_ptr) {
           m_refs = m_ptr->createWeak(this);
       }
   }
   this指针是指向wp对象的。createWeak()函数是RefBase类的成员函数。
   RefBase::weakref_type* RefBase::createWeak(const void* id) const
   {
       mRefs->incWeak(id);
       return mRefs;
   }
   mRefs指向的是第二步骤中产生的weakref_impl对象,调用基类weakref_type的成员函数incWeak()
   void RefBase::weakref_type::incWeak(const void* id)
   {
       weakref_impl* const impl = static_cast<weakref_impl*>(this);
       impl->addWeakRef(id);
       const int32_t c = Android_atomic_inc(&impl->mWeak);
     /* impl->mWeak有加1,但返回值为操作前的结果 */
       LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
   }

三、sp、wp释放过程 
  sp<BBinder> BB_SP_ptr(BB_ptr);
  实际上BB_SP_ptr和前面的BB_ptr一样,指向的是同一个BBinder对象。另外需要注意的时,调用sp构造函数:
  template<typename T>
  sp<T>::sp(const sp<T>& other)
      : m_ptr(other.m_ptr)
  {
      if (m_ptr) m_ptr->incStrong(this);
  }
  同样是需要调用BBinder对象的incStrong()函数,使用weakref_impl对象来管理新添加进来的强引用,同时增加一个
  ref_entry结构体到weakref_impl对象的mStrongRefs,增加2个ref_entry结构体到weakref_impl对象的mWeakRefs。
  如上图所示。
  
  现在来看看释放sp、wp指针的情况。
  delete BB_SP_ptr;
  将会调用如下形式的sp析构函数:
  template<typename T>
  sp<T>::~sp()
  {
      if (m_ptr) m_ptr->decStrong(this);
  }
  m_ptr指向的是前面生成的BBinder对象,调用其基类函数decStrong(this),this值是指向BB_SP_ptr对象。
  
  void RefBase::decStrong(const void* id) const
  {
      weakref_impl* const refs = mRefs;
      refs->removeStrongRef(id); // 注释3,移除mStrongRefs链表中和该sp对应的ref_entry结构体
      const int32_t c = Android_atomic_dec(&refs->mStrong);
    /* 强引用计数减1, 但返回的是操作之前的引用计数值 */
      LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
      if (c == 1) {
       /*  c == 1说明刚刚removeStrongRef之前,整个系统中只存在一个sp对象引用目标对象,现在的情况就是
          系统中没有任何强指针对象来引用目标对象了,此时目标对象就会被删除释放
        */
          const_cast<RefBase*>(this)->onLastStrongRef(id);
          if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
              delete this; // mFlags =0 ,条件成立,删除目标对象,这里就会删除前面new出来的BBinder对象
          }
      }// 如果此时还有其他指向该目标对象的sp指针存在的话,就不会删除目标对象
      
      refs->removeWeakRef(id);
      refs->decWeak(id);
      /* 删除新建目标对象sp指针时在mWeakRefs链表上增加的两个ref_entry结构体 */
  }
  /*********************************注释3*********************************/
  void removeStrongRef(const void* id)
    {
        if (!mRetain) // mRetain 初始化成 flase
            removeRef(&mStrongRefs, id); 
            /* 删除mStrongRefs链表中对应id的ref_entry一项 */
      /* 也就是取消了该sp对象和目标对象的联系 */
        else
            addRef(&mStrongRefs, id, -mStrong);
    }
  
  void removeRef(ref_entry** refs, const void* id)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);
            
            ref_entry* ref = *refs;
            while (ref != NULL) {
                if (ref->id == id) {
                    *refs = ref->next;
                    delete ref;
                    return;
                }
                refs = &ref->next;
                ref = *refs;
            }
        }
    }
  /*********************************注释3*********************************/
  
  delete BB_wp_ptr;
  这是删除目标对象的一个wp指针,会调用wp的析构函数:
  template<typename T>
  wp<T>::~wp()
  {
      if (m_ptr) m_refs->decWeak(this);
  }
  调用weakref_type类的decWeak()函数,如下:
  void RefBase::weakref_type::decWeak(const void* id)
  {
      weakref_impl* const impl = static_cast<weakref_impl*>(this);
      impl->removeWeakRef(id);// 移除weakref_impl对象mWeakRefs链表中对应id的ref_entry结构体
      const int32_t c = Android_atomic_dec(&impl->mWeak);// 引用计数减1
      LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
      if (c != 1) return; // c == 1, 说明这是系统中存在的指向目标对象的最后一个wp指针
      
      if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
          if (impl->mStrong == INITIAL_STRONG_VALUE)
              delete impl->mBase;
              // delete impl; 是不是应该加上这么一句,防止用户新建了wp后,不用,马上又删除的情况呢?
        /* 当目标对象的最后一个wp被析构时,如果目标对象还没有建立任何一个sp,那么目标对象被删除 */
          else {
              delete impl;
        /* 当目标对象的最后一个wp被析构时,但此时和目标对象相关的sp全部被析构,那么impl->mStrong = 0
            在最后一个sp被析构的时候,目标对象也被释放,所以此时只需要释放weakref_impl对象即可
        */
          }
      } else {
          impl->mBase->onLastWeakRef(id);
          if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
              delete impl->mBase;
          }
      }
  }

四、wp升级为sp的过程
  wp的定义包含了:sp<T> promote() const;
  template<typename T>
  sp<T> wp<T>::promote() const
  {
      return sp<T>(m_ptr, m_refs);
  }
  wp,sp互为友元类,这里promote就是以友元身份调用了sp<Binder>类的构造函数: sp(T* p, weakref_type* refs);
  template<typename T>
  sp<T>::sp(T* p, weakref_type* refs)
      : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
  {
  }
  这里如果升级成功,那么将会产生一个sp对象指向目标对象,原来的wp仍然存在。
  如果升级不成功,返回NULL
  看看关键函数refs->attemptIncStrong(this)

bool RefBase::weakref_type::attemptIncStrong(const void* id)
  {
      incWeak(id);
      
      weakref_impl* const impl = static_cast<weakref_impl*>(this);
      
      int32_t curCount = impl->mStrong;
      LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
                 this);
      while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
          if (Android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
              break;
          }
          curCount = impl->mStrong;
      }// 系统中还有其他sp指向目标对象的情况
      
      if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
          bool allow;
          if (curCount == INITIAL_STRONG_VALUE) {
          // 发现该目标对象还没有一个sp对象与之相关联的话,那么将会新建一个对目标对象的强引用
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                    || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          } else {
          /*
       发现系统中原来指向目标对象的sp全部被释放,最后一次sp释放也将目标对象释放了
      */
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                    && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          }
          if (!allow) {
              decWeak(id); // 目标对象已经不存在了,释放前面incWeak(id)产生的ref_entry结构体
              return false; 
          }
          curCount = Android_atomic_inc(&impl->mStrong);
  
          if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
              impl->mBase->onLastStrongRef(id);
          }
      }
      // 走完生成一个sp的必要过程,和前面介绍的是一样
      impl->addWeakRef(id);
      impl->addStrongRef(id);
  
      if (curCount == INITIAL_STRONG_VALUE) {
          Android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
          impl->mBase->onFirstRef();
      }
      
      return true; // 返回true
  }

五、总结:
  1. weakref_impl对象会随着目标对象的生成而产生,但不一定会随着目标对象的释放而释放。例如:如果目标对象被
    1个sp引用,但是同时被2个wp引用,那么在sp被删除的时候,删除了目标对象,但没有删除weakref_impl对象,
    只有在最后一个wp释放时,weakref_impl对象会被释放。
  2. 一个目标对象被多个sp指针引用,没有wp引用的情况下。释放这些sp的时候,delete会调用sp析构函数,
    然后调用RefBase类的成员函数decStrong(), 最后一个sp被释放时,weakref_impl对象数据成员mStrong会
    从1减到0(注意mStrong的初始化值为1<<28, 从这个值可以判断出该目标对象有没有被sp指针引用过),
    同时释放目标对象。
  3. 一个目标对象被多个wp指针引用,没有sp引用的情况下。delete这些wp的时候,会调用wp的析构函数,该函数会
    调用函数decWeak()。当删除最后一个wp的时候,代码中只是删除了目标对象,而没有释放weakref_impl对象,
    暂时没发现在哪里释放了它。
  4. 一个目标对象既有sp,又有wp来引用。如果sp先被删除光,那么最后一个sp删除的时候会释放掉目标对象,那么此时
    mStrong = 0。在后续最后一个wp的释放过程中,在decWeak()函数中就会判断出impl->mStrong != 
    INITIAL_STRONG_VALUE,而释放掉剩下的weakref_impl对象了。如果先所以的wp删除光,此时mWeak还等于剩余的sp
    的个数,所以此时的释放情况,同第2小点的说明。
  5. 从wp定义来看,wp是不能直接操作对象的,必须先升级为sp才行。这个升级的过程是依靠函数promote()来完成的。
    升级成功,返回新生成的sp对象指针,升级失败,返回NULL。需要注意的是,如果目标对象之前有过sp指向,但后来
    将所有的sp释放完之后,此时目标对象是不存在的,那么此时用户还想将指向该目标对象的wp升级为sp的话,
    此时就返回NULL。那么这个时候我们应该delete这些剩下的wp。

转自: http://www.cnblogs.com/wanqieddy/archive/2012/05/08/2490402.html

Android智能指针sp wp详解的更多相关文章

  1. Android智能指针SP WP使用方法介绍

    Android手机操作系统既然是开源的操作系统.那么在具体的文件夹中就会存放着各种相关功能的开源代码.我们在使用的时候可以根据这些源代码进行相应的修改就能轻松的完成我们所需的功能.在这里大家就一起来看 ...

  2. Android结构分析Android智能指针(两)

    笔者:刘蒿羽 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中,我们分析了Android智能指针中的强指针sp,本文我们来分析弱指 ...

  3. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  4. Android图片缓存之Bitmap详解

    前言: 最近准备研究一下图片缓存框架,基于这个想法觉得还是先了解有关图片缓存的基础知识,今天重点学习一下Bitmap.BitmapFactory这两个类. 图片缓存相关博客地址: Android图片缓 ...

  5. Android Design Support Library使用详解

    Android Design Support Library使用详解 Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的And ...

  6. Android 之窗口小部件详解--App Widget

    Android 之窗口小部件详解--App Widget  版本号 说明 作者 日期  1.0  添加App Widge介绍和示例  Sky Wang 2013/06/27        1 App ...

  7. Android不规则点击区域详解

    Android不规则点击区域详解 摘要 今天要和大家分享的是Android不规则点击区域,准确说是在视觉上不规则的图像点击响应区域分发. 其实这个问题比较简单,对于很多人来说根本不值得做为一篇博文写出 ...

  8. [Android新手区] SQLite 操作详解--SQL语法

    该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...

  9. Android中Service的使用详解和注意点(LocalService)

    Android中Service的使用详解和注意点(LocalService) 原文地址 开始,先稍稍讲一点android中Service的概念和用途吧~ Service分为本地服务(LocalServ ...

随机推荐

  1. 列式存储(二)JFinal如何处理从前台传回来的二维数组

    上一篇说到了列式存储,这一篇说它的存储问题,将每个模块的所有属性字段单独存到一张表中,新增页面时,所有的字段都去数据库请求,这样多个模块的新增功能可以共用一个jsp.由于每个模块的字段个数不一样,有的 ...

  2. IntelliJ IDEA 使用Git怎样记住密码和忘记密码的方法

    IntelliJ IDEA 使用Git怎样记住密码的方法 1.当使用Ctrl+T进行更新时,弹出密码框(此时不要输入任何字符),直接点"Cancel" 2.略等二三秒,会弹出新的密 ...

  3. js中Prototype属性解释及常用方法

    1.prototype的定义 javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用. 每一个构造函数都有一个属 ...

  4. ARC下的所有权修饰符

    ARC有效时,id类型必须加上所有权修饰符 下面为三种等效的声明,为了便于和二级指针的情况联系起来,采用第一种. NSError * __weak error = nil; NSError __wea ...

  5. Extjs插入图片

    Extjs并没有提供直接的组件来显示图片.网上通过设置textfield的InputType为Image然后再修改dom对象来实现,其实 image的属性值并没有包含在Extjs的官方文档当中.可以通 ...

  6. 从键盘上输入一个正整数n,请按照以下五行杨辉三角形的显示方式, 输出杨辉三角形的前n行。请采用循环控制语句来实现。

    Scanner sc=new Scanner(System.in); System.out.println("请输入一个正整数:"); int ss=sc.nextInt(); i ...

  7. SQL 笔记

    --查询某一列在哪个表里 SELECT name , object_id , type , type_desc FROM sys.objects WHERE object_id IN ( SELECT ...

  8. SQL server基础知识(表操作、数据约束、多表链接查询)

    SQL server基础知识 一.基础知识 (1).存储结构:数据库->表->数据 (2).管理数据库 增加:create database 数据库名称 删除:drop database ...

  9. EF Power Tools的Reverse Engineer Code First逆向生成Model时处理计算字段

    VS2013上使用EF Power Tools的Reverse Engineer Code First逆向生成Model时,没有处理计算字段.在保存实体时会出现错误. 可以通过修改Mapping.tt ...

  10. EF Power Tools 数据库逆向生成时T4模板修改

    VS2013上使用EF Power Tools的Reverse Engineer Code First逆向生成. 发现数据库中的decimal(18, 4)字段在生成的mapping类中没有精度和小数 ...