原理
RAII(Resource Acquisition Is Initialization),全称资源获取即初始化,1984-1989年期间,比雅尼·斯特劳斯特鲁普和安德鲁·柯尼希在设计C++异常时,为解决资源管理时的异常安全性而使用了该用法,后来比雅尼·斯特劳斯特鲁普将其称为RAII
RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄漏问题
我们来看看cppreference上提供的对比案例
未使用RAII的情况
使用RAII的情况
lock_guard的实现如下图所示,在构造函数完成加锁,析构函数中释放锁,并且禁用了拷贝构造函数和赋值运算
由于RAII可以极大地简化资源管理,并有效地保证程序的正确和代码的简洁,所以通常会强烈建议在C++工程中使用它
RAII在Android底层源码的应用更是随处可见,比如SkImageDecoder_libpng中
应用
这里我们来看看FFmpeg Demo开源工程中RAII的应用
在将java层String对象通过JNI传递时,我们通常都会写如下代码
// jPath type is jstring
const char *path = env->GetStringUTFChars(jPath, nullptr);
// ....
if (path != nullptr) {
env->ReleaseStringUTFChars(jPath, path);
}
样板代码写起来繁琐,而且一旦忘记调用相关release接口就会导致内存泄露
在Android源码的libnativehelper下提供了一个基于RAII思想的工具类
ScopedUtfChars的使用方式如下
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_xyq_libffplayer_FFPlayer_nativePrepare(JNIEnv *env, jobject thiz, jlong handle,
jstring path, jobject surface) {
ATRACE_CALL();
auto *player = reinterpret_cast<FFMpegPlayer *>(handle);
ScopedUtfChars scopedPath(env, path);
std::string s_path = scopedPath.c_str();
bool result = false;
if (player) {
result = player->prepare(env,s_path, surface);
}
return result;
}
ScopedUtfChars的实现如下
除了jstring外,在JNI开发中我们经常使用的jXXArray也有类似问题,同样可以使用RAII进行优化
完整代码可以点击文末的"阅读原文"获取
~~END~