Android指针管理:RefBase,SP,WP

2023-10-24 23:34
文章标签 android 指针 管理 wp sp refbase

本文主要是介绍Android指针管理:RefBase,SP,WP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android中通过引用计数来实现智能指针,并且实现有强指针与弱指针。由对象本身来提供引用计数器,但是对象不会去维护引用计数器的值,而是由智能指针来管理。

要达到所有对象都可用引用计数器实现智能指针管理的目标,可以定义一个公共类,提供引用计数的方法,所有对象都去继承这个公共类,这样就可以实现所有对象都可以用引用计数来管理的目标,在Android中,这个公共类就是RefBase,同时还有一个简单版本LightRefBase。

RefBase作为公共基类提供了引用计数的方法,但是并不去维护引用计数的值,而是由两个智能指针来进行管理:sp(Strong Pointer)和wp(Weak Pointer),代表强引用计数和弱引用计数。 

一、轻量级引用计数的实现:LightRefBase

LightRefBase的实现很简单,只是内部保存了一个变量用于保存对象被引用的次数,并提供了两个函数用于增加或减少引用计数。

复制代码
template <class T>
class LightRefBase
{
public:inline LightRefBase() : mCount(0) { }inline void incStrong(const void* id) const {android_atomic_inc(&mCount);}inline void decStrong(const void* id) const {if (android_atomic_dec(&mCount) == 1) {delete static_cast<const T*>(this);}}//! DEBUGGING ONLY: Get current strong ref count.inline int32_t getStrongCount() const {return mCount;}typedef LightRefBase<T> basetype;
protected:inline ~LightRefBase() { }
private:mutable volatile int32_t mCount;
};
复制代码

二、sp(Strong Pointer)

LightRefBase仅仅提供了引用计数的方法,具体引用数应该怎么管理,就要通过智能指针类来管理了,每当有一个智能指针指向对象时,对象的引用计数要加1,当一个智能指针取消指向对象时,对象的引用计数要减1,在C++中,当一个对象生成和销毁时会自动调用(拷贝)构造函数和析构函数,所以,对对象引用数的管理就可以放到智能指针的(拷贝)构造函数和析构函数中。Android提供了一个智能指针可以配合LightRefBase使用:sp,sp的定义如下:

复制代码
template <typename T>
class sp
{
public:inline sp() : m_ptr(0) { }sp(T* other);sp(const sp<T>& other);template<typename U> sp(U* other);template<typename U> sp(const sp<U>& other);~sp();// Assignmentsp& operator = (T* other);sp& operator = (const sp<T>& other);template<typename U> sp& operator = (const sp<U>& other);template<typename U> sp& operator = (U* other);//! Special optimization for use by ProcessState (and nobody else).void force_set(T* other);// Resetvoid clear();// Accessorsinline  T&      operator* () const  { return *m_ptr; }inline  T*      operator-> () const { return m_ptr;  }inline  T*      get() const         { return m_ptr; }// OperatorsCOMPARE(==)COMPARE(!=)COMPARE(>)COMPARE(<)COMPARE(<=)COMPARE(>=)
private:  template<typename Y> friend class sp;template<typename Y> friend class wp;void set_pointer(T* ptr);T* m_ptr;
};
复制代码

代码比较多,其中Accessors部分代码重载了*、->操作符使我们使用sp的时候就像使用真实的对象指针一样,可以直接操作对象的属性或方法,COMPARE是宏定义,用于重载关系操作符,由于对引用计数的控制主要是由(拷贝)构造函数和析构函数控制,所以忽略其他相关代码后,sp可以精简为如下形式(赋值操作符也省略掉了,构造函数省略相似的两个):

复制代码
template <typename T>
class sp
{
public:inline sp() : m_ptr(0) { }sp(T* other);sp(const sp<T>& other);~sp();private:  template<typename Y> friend class sp;template<typename Y> friend class wp;void set_pointer(T* ptr);T* m_ptr;
};
复制代码

默认构造函数使智能指针不指向任何对象,sp(T* other)与sp(const sp<T>& other)的实现如下:

复制代码
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{if (other) other->incStrong(this);
}template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr)
{if (m_ptr) m_ptr->incStrong(this);
}
复制代码

内部变量m_ptr指向实际对象,并调用实际对象的incStrong函数,T继承自LightRefBase,所以此处调用的是LightRefBase的incStrong函数,之后实际对象的引用计数加1。

当智能指针销毁的时候调用智能指针的析构函数:

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

调用实际对象即LightRefBase的decStrong函数,其实现如下:

inline void decStrong(const void* id) const {if (android_atomic_dec(&mCount) == 1) {delete static_cast<const T*>(this);}
}

android_atomic_dec返回mCount减1之前的值,如果返回1表示这次减过之后引用计数就是0了,就把对象delete掉。

三、RefBase

RefBase提供了更强大的引用计数的管理。

复制代码
class RefBase
{
public:void    incStrong(const void* id) const;void    decStrong(const void* id) const;void    forceIncStrong(const void* id) const;//! DEBUGGING ONLY: Get current strong ref count.int32_t getStrongCount() const;class weakref_type{public:RefBase refBase() const;void    incWeak(const void* id);void    decWeak(const void* id);// acquires a strong reference if there is already one.bool    attemptIncStrong(const void* id);// acquires a weak reference if there is already one.// This is not always safe. see ProcessState.cpp and BpBinder.cpp// for proper use.bool    attemptIncWeak(const void* id);//! DEBUGGING ONLY: Get current weak ref count.int32_t getWeakCount() const;//! DEBUGGING ONLY: Print references held on object.void    printRefs() const;//! DEBUGGING ONLY: Enable tracking for this object.// enable -- enable/disable tracking// retain -- when tracking is enable, if true, then we save a stack trace//           for each reference and dereference; when retain == false, we//           match up references and dereferences and keep only the//           outstanding ones.void    trackMe(bool enable, bool retain);};weakref_type*   createWeak(const void* id) const;weakref_type*   getWeakRefs() const;// DEBUGGING ONLY: Print references held on object.inline  void            printRefs() const { getWeakRefs()->printRefs(); }// DEBUGGING ONLY: Enable tracking of object.inline  void            trackMe(bool enable, bool retain){getWeakRefs()->trackMe(enable, retain);}typedef RefBase basetype;protected:RefBase();virtual     ~RefBase();//! Flags for extendObjectLifetime()enum {OBJECT_LIFETIME_STRONG  = 0x0000,OBJECT_LIFETIME_WEAK    = 0x0001,OBJECT_LIFETIME_MASK    = 0x0003};void    extendObjectLifetime(int32_t mode);//! Flags for onIncStrongAttempted()enum {FIRST_INC_STRONG = 0x0001};virtual void            onFirstRef();virtual void            onLastStrongRef(const void* id);virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);virtual void            onLastWeakRef(const void* id);private:friend class weakref_type;class weakref_impl;RefBase(const RefBase& o);RefBase&        operator=(const RefBase& o);weakref_impl* const mRefs;
};
复制代码

不同于LightRefBase的是,RefBase内部并没有使用一个变量来维护引用计数,而是通过一个weakref_impl *类型的成员来维护引用计数,并且同时提供了强引用计数和弱引用计数。weakref_impl继承于RefBase::weakref_type,代码比较多,不过大都是调试代码,由宏定义分开,Release是不包含调试代码的,去除这些代码后其定义为:

复制代码
#define INITIAL_STRONG_VALUE (1<<28)class RefBase::weakref_impl : public RefBase::weakref_type 
{ 
public: volatile int32_t    mStrong; volatile int32_t    mWeak; RefBase* const      mBase; volatile int32_t    mFlags; weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { } void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } void printRefs() const { } void trackMe(bool, bool) { } 
};
复制代码

weakref_impl中的函数都是作为调试用,Release版的实现都是空的,成员变量分别表示强引用数、弱引用数、指向实际对象的指针与flag,flag可控制实际对象的生命周期,取值为0或RefBase中定义的枚举值。

RefBase提供了incStrong与decStrong函数用于控制强引用计数值,其弱引用计数值是由weakref_impl控制,强引用计数与弱引用数都保存在weakref_impl *类型的成员变量mRefs中。

RefBase同LightRefBase一样为对象提供了引用计数的方法,对引用计数的管理同样要由智能指针控制,由于RefBase同时实现了强引用计数与弱引用计数,所以就有两种类型的智能指针,sp(Strong Pointer)与wp(Weak Pointer)。

sp前面已经说过,其(拷贝)构造函数调用对象即RefBase的incStrong函数。

复制代码
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);LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);if (c != INITIAL_STRONG_VALUE)  {return;}android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);refs->mBase->onFirstRef();
}
复制代码

addStrong的函数体为空,incStrong函数内部首先调用成员变量mRefs的incWeak函数将弱引用数加1,然后再将强引用数加1,由于android_atomic_inc返回变量的旧值,所以如果其不等于INITIAL_STRONG_VALUE就直接返回,则则是第一次由强智能指针(sp)引用,将其减去INITIAL_STRONG_VALUE后变成1,然后调用对象的onFirstRef。

成员变量mRefs是在对象的构造函数中初始化的:

RefBase::RefBase(): mRefs(new weakref_impl(this))
{
}

weakrel_impl的incWeak继承自父类weakrel_type的incWeak:

复制代码
void RefBase::weakref_type::incWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>impl->addWeakRef(id);const int32_t c = android_atomic_inc(&impl->mWeak);LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
复制代码

addWeakRef实现同样为空,所以只是将弱引用计数加1。所以当对象被sp引用后,强引用计数与弱引用计数会同时加1。

当sp销毁时其析构函数调用对象即RefBase的decStrong函数:

复制代码
void RefBase::decStrong(const void* id) const
{ weakref_impl* const refs = mRefs; refs->removeStrongRef(id); const int32_t c = android_atomic_dec(&refs->mStrong);if (c == 1) { const_cast<RefBase*>(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { delete this; }} refs->removeWeakRef(id); refs->decWeak(id); 
}
复制代码

decStrong中将强引用数与弱引用数同时减1,如果这是最后一个强引用的话,会调用对象的onLastStrongRef,并且判断成员变量mRefs的成员变量mFlags来决定是否在对象的强引用数为0时释放对象。

mFlags可以为0或以下两个枚举值:

enum {OBJECT_LIFETIME_WEAK    = 0x0001,OBJECT_LIFETIME_FOREVER    = 0x0003
};

mFlags的值可以通过extendObjectLifetime函数改变:

void RefBase::extendObjectLifetime(int32_t mode)
{android_atomic_or(mode, &mRefs->mFlags);
}

OBJECT_LIFETIME_FOREVER包含OBJECT_LIFETIME_WEAK(位运算中其二进制11包含01),所以当

refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK

为true时表示mFlags为0,实际对象的生命周期受强引用数控制,所以在强引用数为0时delete this,否则实际对象的生命周期就由弱引用数控制。

再来看decWeak:

复制代码
void RefBase::weakref_type::decWeak(const void* id) 
{ weakref_impl* const impl = static_cast<weakref_impl*>(this); impl->removeWeakRef(id); const int32_t c = android_atomic_dec(&impl->mWeak); if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) delete impl->mBase; else { delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { delete impl->mBase; } } 
}
复制代码

将弱引用数减1,若减1后不为0直接返回,否则判断

(impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK

若判断结果为true:

    实际对象生命周期被强引用数控制,接下来判断:

mpl->mStrong == INITIAL_STRONG_VALUE
  1. 如果判断为true表示对象只被弱引用引用过,现在弱引用数为0,直接删除实际对象。

  2. 如果判断为false,表示对象曾经被强引用引用过,但现在强引用为变为0了(因为增加或减小强引用数时一定同时增加或减小弱引用数,所以弱引用数为0时,强引用数一定为0),弱引用数为0了,直接释放mRefs,而实际对象由于受强引用数控制,已经在RefBase::decStrong中被delete了。

若判断结果为false:

    判断mFlgs是否是OBJECT_LIFETIME_FOREVER,如果是,什么都不作由用户自己控制对象的生命周期,否则,实际对象的生命周期受弱引用数控制,现在弱引用数为0,delete实际对象。

四、wp(Weak Pointer)

定义如下:

复制代码
template <typename T>
class wp
{
public:typedef typename RefBase::weakref_type weakref_type;inline wp() : m_ptr(0) { }wp(T* other);wp(const wp<T>& other);wp(const sp<T>& other);template<typename U> wp(U* other);template<typename U> wp(const sp<U>& other);template<typename U> wp(const wp<U>& other);~wp();// Assignment
                 wp& operator = (T* other);wp& operator = (const wp<T>& other);wp& operator = (const sp<T>& other);template<typename U> wp& operator = (U* other);template<typename U> wp& operator = (const wp<U>& other);template<typename U> wp& operator = (const sp<U>& other);void set_object_and_refs(T* other, weakref_type* refs);// promotion to sp
                     sp<T> promote() const;// Resetvoid clear();// Accessors
                     inline  weakref_type* get_refs() const { return m_refs; }inline  T* unsafe_get() const { return m_ptr; }// Operators
                         COMPARE(==)COMPARE(!=)COMPARE(>)COMPARE(<)COMPARE(<=)COMPARE(>=)private:template<typename Y> friend class sp;template<typename Y> friend class wp;T*              m_ptr;weakref_type*   m_refs;
};
复制代码

同sp一样,m_ptr指向实际对象,但wp还有一个成员变量m_refs。

复制代码
template<typename T>
wp<T>::wp(T* other): m_ptr(other)
{if (other) m_refs = other->createWeak(this);
}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);
}RefBase::weakref_type* RefBase::createWeak(const void* id) const
{mRefs->incWeak(id);return mRefs;
}
复制代码

可以看到,wp的m_refs就是RefBase即实际对象的mRefs。

wp析构的时候减少弱引用计数:

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

由于弱指针没有重载*与->操作符,所以不能直接操作指向的对象,虽然有unsafe_get函数,但像名字所示的,不建议使用,直接使用实际对象指针的话就没必要用智能指针了。

因为弱指针不能直接操作对象,所以要想操作对象的话就要将其转换为强指针,即wp::promote方法:

复制代码
template<typename T>
sp<T> wp<T>::promote() const
{return sp<T>(m_ptr, m_refs);
}template<typename T>
sp<T>::sp(T* p, weakref_type* refs): m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
{
}
复制代码

是否能从弱指针生成一个强指针关键是看refs->attemptIncStrong,看其定义:

复制代码
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;}if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {bool allow;if (curCount == INITIAL_STRONG_VALUE) {// Attempting to acquire first strong reference...  this is allowed// if the object does NOT have a longer lifetime (meaning the// implementation doesn't need to see this), or if the implementation// allows it to happen.allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);} else {// Attempting to revive the object...  this is allowed// if the object DOES have a longer lifetime (so we can safely// call the object with only a weak ref) and the implementation// allows it to happen.allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);}if (!allow) {decWeak(id);return false;}curCount = android_atomic_inc(&impl->mStrong);// If the strong reference count has already been incremented by// someone else, the implementor of onIncStrongAttempted() is holding// an unneeded reference.  So call onLastStrongRef() here to remove it.// (No, this is not pretty.)  Note that we MUST NOT do this if we// are in fact acquiring the first reference.if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {impl->mBase->onLastStrongRef(id);}}impl->addWeakRef(id);impl->addStrongRef(id);#if PRINT_REFSLOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endifif (curCount == INITIAL_STRONG_VALUE) {android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);impl->mBase->onFirstRef();}return true;
}
复制代码

首先通过incWeak将弱引用数加1(被强指针sp引用会导致强引用数和弱引用数同时加1),然后:

复制代码
int32_t curCount = impl->mStrong;
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {break;}curCount = impl->mStrong;
}
复制代码

如果之前已经有强引用,直接将强引用数加1,android_atomic_cmpxchg表示如果impl->mStrong的值为curCount,则把impl->mString的值改为curCount+1,此处用while循环是防止其他线程已经增加了强引用数。

接下来:

if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE)

表示对象目前没有强引用,这就要判断对象是否存在了。

如果curCount == INITIAL_STRONG_VALUE,表示对象没有被sp引用过。接下来判断:

allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);

表示:如果对象的生命周期只受强引用控制,对象一定存在,要有强引用才可以管理对象的释放,所以一定会允许生成强引用;如果对象的生命周期受弱引用控制,调用对象的onIncStrongAttempted试图增加强引用,由于此时在弱引用中,弱引用一定不为0,对象也一定存在,调用onIncStrongAttempted的意图是因为类的实现者可能不希望用强引用引用对象。在RefBase中onIncStrongAttempted默认返回true:

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id)
{return (flags&FIRST_INC_STRONG) ? true : false;
}

如果curCount <= 0(只会等于0),表示对象强引用数经历了INITIAL_STRONG_VALUE -->大于0 --> 0,接下来就要判断:

allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);

如果对象的生命周期受强引用数控制,那么由于曾被sp引用过,现在强引用数又为0,对象就已经被delete了,所以就不能生成强引用,否则如果对象的生命周期受弱引用数控制,就通过onIncStrongAttempted看类的实现者是否希望当对象的强引用数变为0时可以再次被强引用引用。

if (!allow) {decWeak(id);return false;
}

如果allow为false表示不能从弱引用生成强引用,就要调用decWeak将弱引用减1(因为在promote入口先将弱引用加了1),然后返回false表示生成强引用失败。

if (curCount == INITIAL_STRONG_VALUE) {android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);impl->mBase->onFirstRef();
}

最后,如果curCount == INITIAL_STRONG_VALUE表示第一次被sp引用,调用对象的onFirstRef函数。

五、总结

RefBase内部有一个指针指向实际对象,有一个weakref_impl类型的指针保存对象的强/弱引用计数、对象生命周期控制。

sp只有一个成员变量,用来保存实际对象,但这个实际对象内部已包含了weakref_impl *对象用于保存实际对象的引用计数。sp 管理一个对象指针时,对象的强、弱引用数同时加1,sp销毁时,对象的强、弱引用数同时减1。

wp中有两个成员变量,一个保存实际对象,另一个是weakref_impl *对象。wp管理一个对象指针时,对象的弱引用计数加1,wp销毁时,对象的弱引用计数减1。

weakref_impl中包含一个flag用于决定对象的生命周期是由强引用数控制还是由弱引用数控制:

  • 当flag为0时,实际对象的生命周期由强引用数控制,weakref_impl *对象由弱引用数控制。

  • 当flag为OBJECT_LIFETIME_WEAK时,实际对象的生命周期受弱引用数控制。

  • 当flag为OBJECT_LIFETIME_FOREVER时,实际对象的生命周期由用户控制。

        可以用extendObjectLifetime改变flag的值。

这篇关于Android指针管理:RefBase,SP,WP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/278527

相关文章

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目