1. 程式人生 > >android中的sp和wp

android中的sp和wp

關於android sp和wp的實現原理,網上很多介紹的。但是為何android為何要設計這兩個智慧指標?使用過程中需要注意什麼?

為何設計智慧指標

首先sp和wp是針對c++而設計的,因為Java根本就沒有指標的概念,替使用者減少了很多不必要的麻煩。在指標的使用過程中,如果處理不妥當,甚至會出現程式的崩潰:
1. 指標沒有初始化;
2. new了物件後沒有delete;
3. ptr1 和 ptr2同時指向了物件A,當釋放了ptr1,同時ptr1=NULL後,ptr2並不知道它所指向的物件不存在了。
關於物件存在多個引用的問題, android設計了兩個引用計數相關的類,LightRefBase和RefBase。我們能夠想到物件的引用計數應該是儲存在物件中的,所以我們在使用引用計數類時,只需要讓普通類去繼承LightRefBase和RefBase即可,這樣普通類產生的物件中就自然而然擁有了引用計數。
首先是LightRefBase,使用mCount作為引用計數。

template <class T>
class LightRefBase
{
    //引用計數原子+1
   inline void incStrong(__attribute__((unused)) const void* id) const {
        android_atomic_inc(&mCount);
    }
    //引用計數原子-1,當引用計數為0時,銷燬物件
    inline void decStrong(__attribute__((unused)) const void* id) const {
        if (android_atomic_dec(&mCount) == 1
) { delete static_cast<const T*>(this); } } private: //引用計數 mutable volatile int32_t mCount; }

考慮另外一種情況,迴圈引用,父類中儲存了子類的物件,子類中儲存了父類的物件,這樣就是一個“死迴圈”,父類和子類的引用計數永遠不會為0,所以這種情況下單純使用”mCount”就解決不了這個問題,所以有了RefBase。
對RefBase,沒有使用int作為引用計數,而是使用了類weakref_impl* const mRefs;這個類中同時儲存了強引用計數和弱引用計數,也就牽扯出了sp和wp,強智慧指標和弱智慧指標。並且定義了三種普通物件的生命週期,

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
}
    enum {
    //物件的生命週期受強指標控制
        OBJECT_LIFETIME_STRONG  = 0x0000,
        //物件的生命週期受弱指標控制
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_MASK    = 0x0001
    };

如果一個普通物件的生命週期受強指標控制,當強引用計數為0時,普通物件就可以被銷燬,但是RefBase中的mRefs物件的銷燬由弱引用計數決定。在這種情況下,父子物件可以一個使用強指標,一個使用弱指標,解決迴圈引用的問題。
同時規定:弱指標必須先升級為強指標,才能去訪問智慧指標所指向的真實物件。

使用中需要注意的問題

onFirstRef函式

在frameworks中有部分類都實現了onFirstRef函式(需要使用智慧指標的類重寫了父類RefBase的onFirstRef函式),這個非常有用,當強指標在第一次引用的時候,會去呼叫這個函式,

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);

    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }
    //第一次incStrong的時候,mStrong就是INITIAL_STRONG_VALUE,會呼叫refs->mBase->onFirstRef()
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

下面是SurfaceFlinger中建立Layer的例子,

status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
#ifdef NO_RGBX_8888
        format = PIXEL_FORMAT_RGB_565;
#else
        format = PIXEL_FORMAT_RGBX_8888;
#endif
        break;
    }

#ifdef NO_RGBX_8888
    if (format == PIXEL_FORMAT_RGBX_8888)
        format = PIXEL_FORMAT_RGBA_8888;
#endif

    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getBufferQueue();
    }

    ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
    return err;
}

*outLayer = new Layer(this, client, name, w, h, flags);建立一個Layer物件,賦值給outLayer ,這時候會增加強引用計數,同時會去呼叫Layer的onFirstRef()函式。

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    mBufferQueue = new SurfaceTextureLayer(mFlinger);
    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mBufferQueue, mTextureName);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setFrameAvailableListener(this);
    mSurfaceFlingerConsumer->setName(mName);

#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
#warning "disabling triple buffering"
    mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2);
#else
    mSurfaceFlingerConsumer->setDefaultMaxBufferCount(3);
#endif

    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

wp無法操作真實物件

sp中過載了*和->運算子,返回真實物件,所以可以利用sp去操作真實物件。

    //m_ptr是真實物件的指標
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }

對於wp中沒有過載*和->運算子,無法操作真實物件,需要先promote為強指標才能去操作。這種情況就適合於解決如果真實物件已經被delete了,還去操作指標的操作,下面是android中找的例子:
首先在ProxyConsumerListener中定義了一個wp,wp<ConsumerListener> mConsumerListener;

class ProxyConsumerListener : public BnConsumerListener {
    public:
        ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
        virtual ~ProxyConsumerListener();
        virtual void onFrameAvailable();
        virtual void onBuffersReleased();
    private:
        // mConsumerListener is a weak reference to the IConsumerListener.  This is
        // the raison d'etre of ProxyConsumerListener.
        wp<ConsumerListener> mConsumerListener;
    };

在使用時,首先對wp需要promote,返回sp後才能去操作真實物件(呼叫方法嘛),返回為null,則說明沒有真實物件的存在。

void BufferQueue::ProxyConsumerListener::onFrameAvailable() {
    sp<ConsumerListener> listener(mConsumerListener.promote());
    if (listener != NULL) {
        listener->onFrameAvailable();
    }
}

真實物件是在哪存在的?
sp<IConsumerListener>& consumerListener賦值給wp,這時候wp可以成功promote。

status_t BufferQueue::consumerConnect(const sp<IConsumerListener>& consumerListener,
        bool controlledByApp) {
    ST_LOGV("consumerConnect controlledByApp=%s",
            controlledByApp ? "true" : "false");
    Mutex::Autolock lock(mMutex);

    if (mAbandoned) {
        ST_LOGE("consumerConnect: BufferQueue has been abandoned!");
        return NO_INIT;
    }
    if (consumerListener == NULL) {
        ST_LOGE("consumerConnect: consumerListener may not be NULL");
        return BAD_VALUE;
    }

    mConsumerListener = consumerListener;
    mConsumerControlledByApp = controlledByApp;
    mFrameLost = 0;

    return NO_ERROR;
}

相關推薦

Androidsp wp 類模板 以及RefBase類

sp 和 wp 類模板 以及RefBase類的程式碼在frameworks/base/include/utils/RefBase.h 和 frameworks/base/libs/utils/RefBase.cpp 中。 sp是Strong pointer ,wp是We

androidspwp

關於android sp和wp的實現原理,網上很多介紹的。但是為何android為何要設計這兩個智慧指標?使用過程中需要注意什麼? 為何設計智慧指標 首先sp和wp是針對c++而設計的,因為Java根本就沒有指標的概念,替使用者減少了很多不

Android的智慧指標:spwp

原文: 連結:http://blog.csdn.net/DroidPhone/article/details/5799792 經常會在android的framework程式碼中發現sp<xxx>和wp<xxx>這樣的指標,平

AndroidgetDrawablegetColor過時的替代方法

this logs con 知識 log launcher 16px ase spa 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 前言 Android SDK 升級到 23 之後,getDrawable和getColor方法提示過時。 解決方案 getRe

Android BitmapDrawable相互轉換的方法

canvas board null height .com factory oar tool pla 1、Drawable --> Bitmap [java] view plain copy Bitmap drawable2Bitmap(Drawabl

AndroidGalleryImageSwitcher同步自動(滾動)播放圖片庫

目標 art trac repl otto fin instance img com 本文主要內容是如何讓Gallery和ImageSwitcher控件能夠同步自動播放圖片集 ,看起來較難,然而,實現的方法非常簡單, 請跟我慢慢來。總的來說,本文要實現的效果如下圖:(截

AndroidRelativeLayoutLinearLayout性能分析

ant 顯示 二次 iou other comm 排列 vertica 簡單的 先看一些現象吧:用eclipse或者Android studio,新建一個Activity自動生成的布局文件都是RelativeLayout,或許你會認為這是IDE的默認設置問題,其實不然,

AndroidServiceIntentService的差別

前言: ServiceTimeout(20 seconds)小概率型別Service在特定的時間內無法處理完成,會造成ANR — 應用程式無響應(ANR:Application Not Responding)的情況 ▲ 分析 : 避免ANR最核心的一點就是在主執行緒減少耗時操作。這時我們

AndroidSerializableParcelable序列化物件詳解

學習內容: 1.序列化的目的 2.Android中序列化的兩種方式 3.Parcelable與Serializable的效能比較 4.Android中如何使用Parcelable進行序列化操作 5.Parcelable的工作原理 6.相關例項   1.序列化

Android srcbackground的區別

XML屬性中src和background的區別: src會存放原圖的大小,background會根據view的大小拉伸整張圖片。src是前景而background是背景。 可以使用scaleType屬性設定src(只對src起作用)的縮放方式。 詳細的scaleType說明: CEN

Androidactivityxml的第一個專案

我們使用的手機不光是隻用到一個應用程式,比如在淘寶介面要付款的時候可能會啟動微信付款等,這就相當於在淘寶的Activity中啟動了微信的Activity。還比如說當我們註冊一個網站是,可能會給自己傳送一條簡訊作為驗證,這就是在當前的Activity中啟動了簡訊的Activity。之前對比的MVC設

AndroidPaintCanvas的簡單使用

在 Android 中,Canvas 相當於畫布,而 Paint 相當於畫筆;那麼這兩個配合使用就可以畫出來我們想要的形狀了。 首先我們新建一個類,名字叫 MyView,重寫 onDraw() 方法,程式碼如下: @SuppressLint("AppCompatCustomView") pu

android IMEIICCID的校驗位計算

        我們都知道在android 中有IMEI和ICCID 這兩項資料. 至於這兩項資料的獲取方式以及所表示的意思在此我就不寫了.主要講講這兩項資料最後一位的校驗位如何計算的.         IMEI共1

Android-0.AndroidminsdkversiontargetSdkVersion的選擇

問題 在呼叫系統播放mp4時,程式碼如下: private void viewMediaFile(String path) { if (!TextUtils.isEmpty(path)) { String type =

androidstagefrightOMXCodec原理分析

1. 框架結構 1.1StageFright和openCore和NuPlayer的關係 上圖可知,stagefright是在MediaPlayerService這一層加入的,和opencZ喎�"/kf/ware/vc/" target="_blank" class=

AndroidBitmapDrawable相互轉換

一、相關概念 1、Drawable就是一個可畫的物件,其可能是一張點陣圖(BitmapDrawable),也可能是一個圖形(ShapeDrawable),還有可能是一個圖層(LayerDrawable),我們根據畫圖的需求,建立相應的可畫物件 2、Canvas畫布,繪圖的

AndroidDensityPixel的關係對介面顯示的影響

     眾所周知,Android中的Density為分四種,分別是120dpi,160dpi,240dpi,320dpi,對應工程中的資料夾分別為drawable-ldpi,drawable-mdpi,drawable-hdpi,drawable-xhdpi。同一張圖片的尺

AndroidandroidInvalidatepostInvalidate的區別

Android中實現view的更新有兩組方法,一組是invalidate,另一組是postInvalidate,其中前者是在UI執行緒自身中使用,而後者在非UI執行緒中使用。 Android提供了Invalidate方法實現介面重新整理,但是Invalidate不能直接線上程中呼叫,因為他是違背了單執行緒模型

AndroidOkhttpSPDY協議

  HTTP的一個問題在於每個連線一次只允許一個請求和響應,這就讓瀏覽器或者其他客戶端為了並行請求必須生成多個套接字(socket)連線。對客戶端來說,這不算什麼大問題,但是對伺服器來說情況就不一樣了

android CanvasPaint

相關連結: ---------------正文---------------- Canvas類主要實現了螢幕的繪製過程,其中包含了很多實用的方法,比如繪製一條路徑、區域、貼圖、畫點、畫線、渲染文字,下面是Canvas類常用的方法:  void drawRect(RectF rect, Pai