当前位置: 首页 > news >正文

RAII - 安卓中的智能指针

RAII - 安卓中的智能指针

概念 sp wp RefBase

是什么
system/core/libutils/RefBase.cpp
system/core/libutils/include/utils/RefBase.hsystem/core/libutils/StrongPointer.cpp
system/core/libutils/include/utils/StrongPointer.h

Android在标准库之外,自定义了以下两种智能指针类型:

  • 强指针sp(strong pointer),弱指针 wp(weak pointer)都是模板类

  • sp 和share_ptr概念相同。wp 和weak_ptr概念相同。spwp has-a RefBase

    • 弱指针仅仅记录对象的地址,不能通过弱指针来访问该对象。要想访问弱指针所指向的对象,需首先通过wp类所提供的promote()方法将弱指针升级为强指针。

    • 弱指针所指向的对象是有可能在其它地方被销毁的。如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

支持使用 spwp的对象,必须继承RefBase类。观察Android 中使用spwp的地方,传进来的模板参数都是继承自RefBase,例如:

// MediaPlayer是支持使用强指针和弱指针的对象
class MediaPlayer : public BnMediaPlayerClient,public virtual IMediaDeathNotifier
{
public:MediaPlayer();~MediaPlayer();
// 父类是 BnMediaPlayerClient 和 IMediaDeathNotifier,
// 多继承可能出现菱形继承,因此 虚继承自 RefBase
class IMediaDeathNotifier: virtual public RefBase
{
public:IMediaDeathNotifier() { addObitRecipient(this); }virtual ~IMediaDeathNotifier() { removeObitRecipient(this); }//随便找个,可以看到是继承RefBase,注意的是虚拟继承。
怎么用
// 使用智能指针的类必须继承自 RefBase
class Ressource : public RefBase { Ressource(int){}void test() {}
}{ // 作用域开始// 强引用 sp 构造函数有很多,这里简单列举两个Ressource* p = new Ressource(1);sp<Ressource> sp1(p);  // 1sp<Ressource> sp1 = new Ressource(1); // 2// sp::make 提供内存连贯性&缓存命中: 构建一个 sp 对象,其内部new一个资源对象,在将成员变量 m_ptr 指针指向它 static sp<Ressource> sp1 = sp<Ressource>::make(1); // 3// 通过强引用,操作指针指向的对象sp1->test();// 不能直接通过弱引用使用对象,升级为强指针再使用wp<Ressource> wp1(p); // 弱引用sp<Ressource> sp2 = wp1.promote();if(sp2 != NULL) {  // sp重载了 != 原本spRessource2与 NULL 不是同一类型sp2->test();}
}   // 作用域结束,调用强引用对象的析构函数,释放掉 p 指针指向的内存
对比标准库
为什么不直接用标准库?

为什么不用标准库?

1、历史原因!C++11推出 shared_ptr 和 weak_ptr之前,Android就已经需要存在了。

2、特殊的功能:有些功能是针对特定使用场景定制的(显然不符合 C++ 标准)。

例如,特殊的接口:

template<typename T>
class sp {//! Special optimization for use by ProcessState (and nobody else).void force_set(T* other);
}
template<typename T>
void sp<T>::force_set(T* other) {other->forceIncStrong(this);m_ptr = other;
}

例如,智能指针有两种模式

  • 强引用控制 OBJECT_LIFETIME_STRONG (默认,且绝大多数情况下都是强引用控制)
  • 弱引用控制 OBJECT_LIFETIME_WEAK

可通过 void RefBase::extendObjectLifetime(int32_t mode) 函数切换。强/弱引用控制的切换,通常在被管理的目标类的构造函数中调用:

// system/core/libutils/include/utils/RefBase.h
enum {OBJECT_LIFETIME_STRONG  = 0x0000, //强引用控制OBJECT_LIFETIME_WEAK    = 0x0001, //弱引用控制// 掩码值,可使用 flag&OBJECT_LIFETIME_MASK 的形式来获取 flag 的最低位数值OBJECT_LIFETIME_MASK    = 0x0001
};// frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(Handle&& handle) {// 调用函数修改智能指针模式extendObjectLifetime(OBJECT_LIFETIME_WEAK);
}

例如,神奇的功能:

static void check_not_on_stack(const void* ptr) {
实现上的区别

从被管理的资源的角度来看:

标准库的 shared_ptr,”管理它所持有的 裸指针“。

  • 被管理的资源——裸指针所指向的对象,可以是任意类型。
  • 处于"被管理状态",不会对资源本身造成什么影响,没有负担。

Android 实现的 sp,wp ”操作它所持有的 RefBase类的计数“。

  • 被管理的资源——必须是 RefBase类,即被管理要求原有类型必须继承 RefBase类。
  • 处于"被管理状态",意味着
    • 资源类RefBase 自身保存计数,保存的强引用计数归零后自己执行delete this
    • sp wp 会去操作,让资源类去管理自身保存的计数
优缺点

Android这样不使用 STD 智能指针的做法,

优点:总结来说就是,客制化的美~

  1. 允许进行根据需求自定义的修改/优化。

    前文介绍过的,特殊接口,强/弱引用控制

  2. 安卓fwk中普遍存在跨进程“引用资源”的的情况,RefBase 自行计数方便IPC情境下的管理。

  3. 方便且臃肿的 debug 函数

缺点:总结来说就是,增大对象本身的开销

  1. 对象占用内存增高—— 作为操作系统必要,但是其它场景不需要的开销

    诸如函数RefBase::getStrongCount()RefBase::weakref_type::trackMe()

  2. 资源类有时候需要虚继承 RefBase —— 为了解决菱形继承而不可避免的选择。因此相较于使用标准库的智能指针,每次更改计数都不得不承担虚函数表带来的开销。

  3. 不同于标准库这件事情本身就会影响可读性,引入新的复杂度。

你自己看看复杂不复杂,臃肿不臃肿

void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id); //删除弱引用//减少对象的弱引用计数,并且返回减少之前的值,存储在c中const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);/*如果c不等于1则不用进行处理,如果c=1,说明弱引用计数为0,则强引用计数必定也为0,则考虑如何释放该对象*/if (c != 1) return; //还有其他引用在用这个指针,直接返回/*释放方式取决于当前对象的生命周期控制标志mFlags的值0,只要强引用计数为0,系统则自动释放这个对象OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放*/   atomic_thread_fence(std::memory_order_acquire);// ...

RefBase类

首先,这屌东西里面有什么。它能干啥?

类方法
内部类
类方法
derived
成员变量
has-a
has-a
RefBase
incStrong/decStrong/createWeak
weakref_type
incWeak/decWeak
weakref_impl
weakref_type* mRefs 描述对象的引用计数
sp
m_ptr, RefBase*
wp
m_refs, weakref_type*
内部类 weakref_type

RefBase类 有两个内部类:定义 & 实现 引用计数维护接口

  • class weakref_type:定义接口
  • class weakref_impl:具体实现(weakref_type继承weakref_impl

成员变量RefBase::mRefs,类型就是刚刚提到的内部类 weakref_impl* const——描述对象的引用计数。

内部类weakref_type 提供有类方法:

  • 增加/减少 弱引用计数:weakref_type::incWeakweakref_type::decWeak
  • 维护对象的强/弱引用计数:weakref_type::attemptIncStrongweakref_type::attemptIncWeak
class RefBase {friend class weakref_type;class weakref_type {/*...*/};class weakref_impl;//使用一个weakref_impl对象来描述对象的引用计数weakref_impl* const mRefs;
class RefBase::weakref_impl : public RefBase::weakref_type
成员函数

RefBase类 有一系列 成员函数

  • 关于强引用:incStrongincStrongRequireStrongdecStrongforceIncStrong

  • 关于弱引用:createWeakgetWeakRefs

createWeak类方法,返回weakref_type类型的指针。

sp wp

sp

  • sp::m_ptr 的类型是 T* RefBase * ——sp has-a RefBase

  • 控制计数,最终调用:RefBase::incStrongRefBase::decStrong方法

template <typename T>
class sp {// T 类型就是 RefBase 的子类T* m_ptr;
};
// decStrong 和incStrong的具体实现,在RefBase类中。
void RefBase::incStrong(const void* id) const
void RefBase::decStrong(const void* id) const

wp

  • wp::m_refs的类型是RefBase::weakref_type *
  • 控制计数,最终调用:RefBase::incStrongRefBase::decStrong方法
// RefBase::weakref_type中实现
RefBase::weakref_type* RefBase::createWeak(const void* id) const
void RefBase::weakref_type::incWeak(const void* id)
void RefBase::weakref_type::decWeak(const void* id)
强引用 sp

通过 初始化/传入参数 设置m_ptrm_ptr必须是RefBase类的派生类(虚继承),从而使用 RefBase::incStrong和RefBase::decStrong修改RefBase本身的引用计数。

sp构造时:初始化sp中RefBase类型的指针 m_ptr,并调用指针incStrong()进行计数加1

sp析构时:指针T *m_ptr需要将计数减1

template<typename T>
sp<T>::sp(T* other): m_ptr(other) {if (other)other->incStrong(this);
}template<typename T>
sp<T>::~sp()
{if (m_ptr) m_ptr->decStrong(this);
}
弱引用 wp

成员变量 wp::m_ptrwp::m_refs

  • wp::m_ptr 类型是RefBase类型的指针,指向资源RefBase本身。
  • wp::m_refs 类型是RefBase::weakref_type,即资源RefBase的成员变量RefBase::mRefs——用于指向管理资源的内部类。

构造/移动&拷贝/赋值/清空时,以上两个成员变量被同时修改

通过成员变量wp::m_refs(类型为RefBase::weakref_type)的成员函数去修改资源RefBase计数。

template <typename T>
class wp
{typedef typename RefBase::weakref_type weakref_type;T*              m_ptr;weakref_type*   m_refs;
};template<typename T>
wp<T>::~wp()
{if (m_ptr) m_refs->decWeak(this);
}

源码注释 RefBase

接口
class RefBase
{
public://增加强引用计数void            incStrong(const void* id) const;//减少强引用计数void            decStrong(const void* id) const;// (wp中的m_refs调用)weakref_type*   createWeak(const void* id) const;weakref_type*   getWeakRefs() const;void            forceIncStrong(const void* id) const;int32_t         getStrongCount() const;// 定义了内部类:weakref_type weakref_implclass weakref_type{public:RefBase*            refBase() const;//增加弱引用计数 (wp中的m_refs调用)void                incWeak(const void* id);//减少弱引用计数 (wp中的m_refs调用)void                decWeak(const void* id);//维护对象的强引用计数bool                attemptIncStrong(const void* id);//维护对象的弱引用计数bool                attemptIncWeak(const void* id);// debug用函数int32_t             getWeakCount() const;void                printRefs() const;void                trackMe(bool enable, bool retain);};// Debug 函数...// 允许自定义的Destroyerclass Destroyer {friend class RefBase;public:virtual ~Destroyer();private:virtual void destroy(RefBase const* base) = 0;};void setDestroyer(Destroyer* destroyer);
protected:RefBase();virtual ~RefBase();// 生命周期有关的flag,函数...// 一堆其它功能的虚函数...
private:friend class weakref_type;class weakref_impl;RefBase(const RefBase& o);RefBase& operator=(const RefBase& o);//使用一个weakref_impl对象来描述对象的引用计数weakref_impl* const mRefs;
};
强引用
decStrong

RefBase类的成员函数decStrong减少对象的引用计数

实现如下:

void RefBase::decStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->removeStrongRef(id);//移除强引用链表中的引用idconst int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);//计数减1
#if PRINT_REFSALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endifALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);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);//如果不是最后一个,需要减少弱引用链表
}
incStrong

RefBase类的成员函数incStrong 增加对象的引用计数

实现如下:

frameworks/base/libs/utils/RefBase.cpp
//RefBase类的成员函数incStrong来增加对象的引用计数
void RefBase::incStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->addWeakRef(id);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 PRINT_REFSLOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endifif (c != INITIAL_STRONG_VALUE)  {return;}android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
//如果对象第一次被强指针引用,进行通知const_cast<RefBase*>(this)->onFirstRef()
}
弱引用
incWeak

只有RefBase::weakref_type的对象才能调用 incWeakdecWeak

frameworks/base/libs/utils/RefBase.cpp
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);LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
decWeak

只有RefBase::weakref_type的对象才能调用 incWeakdecWeak

void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id); //删除弱引用//减少对象的弱引用计数,并且返回减少之前的值,存储在c中const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);/*如果c不等于1则不用进行处理,如果c=1,说明弱引用计数为0,则强引用计数必定也为0,则考虑如何释放该对象*/if (c != 1) return; //还有其他引用在用这个指针,直接返回/*释放方式取决于当前对象的生命周期控制标志mFlags的值0,只要强引用计数为0,系统则自动释放这个对象OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放*/   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) {// This is the regular lifetime case. The object is destroyed// when the last strong reference goes away. Since weakref_impl// outlive the object, it is not destroyed in the dtor, and// we'll have to do it here.if (impl->mStrong.load(std::memory_order_relaxed)== INITIAL_STRONG_VALUE) {// Special case: we never had a strong reference, so we need to// destroy the object now.delete impl->mBase; //如果没有其他的引用了,需要释放当前指针} else {// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);delete impl;}} else {// This is the OBJECT_LIFETIME_WEAK case. The last weak-reference// is gone, we can destroy the object.impl->mBase->onLastWeakRef(id);if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {if (impl->mBase) {if (impl->mDestroyer) {impl->mDestroyer->destroy(impl->mBase);} else {delete impl->mBase;//如果没有其他引用了,就释放当前指针}}}}
}
weakref_type 具体实现

weakref_type类定义了引用计数维护接口,具体的实现由weakref_impl类实现。

frameworks/base/libs/utils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:volatile int32_t    mStrong; 	//描述强引用计数volatile int32_t    mWeak;		//描述弱引用计数RefBase* const      mBase;		//指向它所引用的对象的地址/*mFlags取值范围0,表示对象的生命周期只收强引用计数影响OBJECT_LIFETIME_WEAK,	表示对象的生命周期同时受强引用计数和弱引用计数影响OBJECT_LIFETIME_FOREVER,表示对象的生命周期完全不受强引用指针和弱引用指针影响*/volatile int32_t    mFlags;//描述对象生命周期控制方式Destroyer*          mDestroyer;#if !DEBUG_REFSweakref_impl(RefBase* base): mStrong(INITIAL_STRONG_VALUE), mWeak(0), mBase(base), mFlags(0), mDestroyer(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) { }#else
....//省略部分不重要内容
#endif
};
析构函数

RefBase类成员变量mRefs的解析函数的实现。

frameworks/base/libs/utils/RefBase.cpp
RefBase::~RefBase()
{/*如果发现对象的弱引用计数为0,则在释放RefBase对象的同时也将引用计数对象mRefs一起释放掉。mRefs对象在RefBase的构造函数中被创建,指向weakref_impl,如果RefBase对象都不存在了,并且mWeak也为0,mRefs也没有什么存在的必要了。强引用计数一定是小于弱引用计数的,所以强引用计数为0时弱引用计数仍有可能大于0。所以在弱引用计数大于0的情况下我们只释放RefBase对象,留下mRefs对象继续使用。*/if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) {if (mRefs->mWeak == 0) {delete mRefs;}...}
}

相关文章:

RAII - 安卓中的智能指针

RAII - 安卓中的智能指针 概念 sp wp RefBase 是什么 system/core/libutils/RefBase.cpp system/core/libutils/include/utils/RefBase.hsystem/core/libutils/StrongPointer.cpp system/core/libutils/include/utils/StrongPointer.hAndroid在标准库之外&#xff0c;自定义…...

linux--库指令

ldd ldd 可执行文件路径 显示依赖的库的查找路径以及是否查找到了。...

展讯方案-内置多张开机logo

1. 开机图片的资源存放在logo分区中&#xff0c;这个分区中可以存放一个xx.bmp文件&#xff0c;也可以存放一个bin文件&#xff08;1logo.bin&#xff0c;包含多张压缩的图片集合&#xff09; 2.平台代码中logo.bin是由mk_1ogo_img.py脚本打包&#xff0c;具体如下&#xff08;…...

Stable Diffusion模型资源合集(附整合包)

&#xff08;模型资源在ComfyUI、WebUI以及ForgeUI中都通用&#xff09; 之前的Stable Diffusion笔记受到了不少小伙伴的关注&#xff0c;很感谢大家的建议和支持。有很多小伙伴私信我问我一些AI绘画的模型资源在哪来下载&#xff0c;一般来说有两个网站比较常用&#xff0c;分…...

机器学习|Pytorch实现天气预测

机器学习|Pytorch实现天气预测 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#x…...

【Kuberntes】k8s权限管理

文章目录 权限管理概述核心概念配置RBAC创建Role和ClusterRole创建RoleBinding和ClusterRoleBinding 默认角色和角色绑定权限的实现注意事项 如何在 Kubernetes 中实现 RBAC 的细粒度权限控制&#xff1f;1. Role和ClusterRole2. RoleBinding和ClusterRoleBinding3. 配置RBAC4.…...

C++,STL 033(24.10.15)

内容 queue容器&#xff08;队列&#xff09;的常用接口。 代码 #include <iostream> #include <string> #include <queue> // 注意包含queue容器&#xff08;队列&#xff09;的头文件using namespace std;class Person { public:string m_Name;int m_Age…...

AdmX_new

0x00前言 因为环境问题&#xff0c;此次靶场都放在vm上。都为NAT模式。 靶机地址: https://download.vulnhub.com/admx/AdmX_new.7z 需要找到两个flag文件。 0x01信息搜集 搜集IP 确认目标IP为172.16.8.131&#xff0c;进一步信息搜集 获取端口开放情况&#xff0c;版本信…...

【python3】函数注解

Python 函数注解 (Function Annotations) Python 函数注解 (Function Annotations)函数注解的基本语法基本语法格式示例 特殊类型注解注解信息的存储与访问函数注解的实际用途注意事项小结 函数注解是 Python 的一种特性&#xff0c;用于为函数的参数和返回值添加 元数据。注解…...

leetcode hot100 之【LeetCode 42. 接雨水】 java实现

LeetCode 42. 接雨水 题目描述 给定一个非负整数数组 height 表示柱状图中每个柱子的高度&#xff0c;请你计算按此排列的柱状图能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面的柱状图可以…...

10月18日,每日信息差

第一、现代汽车集团在上海举办了中国前瞻技术研发中心的发布及启新庆典&#xff0c;宣布成立其全资法人公司 —— 现代前瞻汽车技术开发&#xff08;上海&#xff09;有限公司。该中心是集团在海外建立的首个前瞻技术研发中心&#xff0c;专注于自动驾驶、智能座舱、共享出行等…...

Axure科技感元件:打造可视化大屏设计的得力助手

Axure&#xff0c;作为一款专业的原型设计工具&#xff0c;凭借其强大的设计功能、丰富的组件库和灵活的交互能力&#xff0c;成为了许多设计师打造科技感设计的首选工具。其中&#xff0c;Axure科技感元件更是以其独特的魅力和实用性&#xff0c;在数据可视化大屏、登录界面、…...

【模板】最近公共祖先(LCA)倍增

P3379 P3379 【模板】最近公共祖先&#xff08;LCA&#xff09; # 【模板】最近公共祖先&#xff08;LCA&#xff09; ## 题目描述 如题&#xff0c;给定一棵有根多叉树&#xff0c;请求出指定两个点直接最近的公共祖先。 ## 输入格式 第一行包含三个正整数 $N,M,S$&#…...

我的JAVA项目构建

1.Maven maven就是pip 设置maven下载的的jar包位置 换源 下载插件maven-search 配置dependency 2.Tomcat 设置环境变量JAVA_HOME 设置编码方式 方框就是路径的前缀 3.Servlet 新建项目 写一个类继承HttpServlet&#xff0c;复写doGet(应对Get请求)&#xff0c;doPost(应对…...

应用层协议 序列化

自定义应用层协议 例子&#xff1a;网络版本计算器 序列化反序列化 序列化&#xff1a;将消息&#xff0c;昵称&#xff0c;日期整合成消息-昵称-日期 反序列化&#xff1a;消息-昵称-日期->消息&#xff0c;昵称&#xff0c;日期 在序列化中&#xff0c;定义一个结构体…...

【HAD】Half-Truth: A Partially Fake Audio Detection Dataset

文章目录 Half-Truth: A Partially Fake Audio Detection Dataset背景key points研究数据集设计评价指标实验基线:utterance-level分类(话语级)基线:segment-level分类(片段级)Half-Truth: A Partially Fake Audio Detection Dataset 会议/期刊:Interspeech 2021 CCF-C…...

OpenAI Prompt generation - 生成和优化Prompt的Prompt

OpenAI Prompt generation - 生成和优化Prompt的Prompt 从头开始创建 Prompt 可能很耗时&#xff0c;所以快速生成 Prompt 可以帮助我们提高效率。 下面是 OpenAI 提供的协助生成 Prompt 的 Prompt。 from openai import OpenAIclient OpenAI()META_PROMPT ""&qu…...

Android技术探索:深入解析Android组件

Android系统以其开放性和多样性&#xff0c;成为了众多开发者的首选平台。在Android应用的开发中&#xff0c;组件&#xff08;Components&#xff09;是构建应用的基础元素。深入了解Android组件&#xff0c;对于开发者来说至关重要。本文将详细探讨Android的四大核心组件&…...

使用R-GCN处理异质图ACM的demo

加载和处理数据集 import torch from torch_geometric.datasets import HGBDataset from torch_geometric.transforms import RandomLinkSplit# 加载ACM数据集&#xff0c;这是一个包含论文&#xff08;paper&#xff09;、主题&#xff08;subject&#xff09;以及它们之间关…...

征程 6E DISPLAY 功能介绍及上手实践

01 功能概述 本文将带大家一起实现单路、多路 MIPI CSI TX 输出、IDU 回写、IDU oneshot 模式、绑定输出 VPS 数据等功能&#xff0c;此处主要介绍各 sample 的实现与使用方法。 02 软件架构说明 本文中绑定 VPS 输出功能基于 libvio API 实现&#xff0c;调用 libvio 提供的…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...