有时候,我们写的程序,会涉及到相互引用的问题。比如frameworks\av\media\libstagefright下的这个libstagefright,被frameworks\av\media\libstagefright\codecs\avienc我们这个新增的avienc引用到了,而反过来,libstagefright在录制avi视频时,又用到了我们的avienc.so这个库。看Android.mk:
libstagefright.so的Android.mk里
......
LOCAL_SHARED_LIBRARIES := \
avienc
......
avienc.so的Android.mk里
......
LOCAL_SHARED_LIBRARIES := \
libstagefright
......
像这种情况,在编译时,肯定是通过不了的。因为在编译libstagefright的时候,要用到avienc,而去编译avienc时又需要到libstagefright,这样就造成了一个相互依赖的死结。
处理这种情况,最常用的办法是将avienc里要用到的libstagefright里的文件,全部拷贝到avienc里来,这样就达到了单方向的解耦。但是这样无疑会增加avienc的代码量。有没有一种,在不影响libstagefright和avienc的代码结构、不增加它们代码量的情况下,解决这个问题的方法呢?答应是有的。
动态库dlopen、dlsym、dlclose这三个函数就可以实现这个功能。其实在android里,我们framework层去调用hal层的库时,用的hw_get_module这个函数,只要大家跟时去看,就会发现,实际上它用的也就是动态库的这三板斧。先来介绍下这三个函数:
dlopen
基本定义
功能:打开一个动态链接库
包含头文件:
#include <dlfcn.h>
函数定义:
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误返回NULL
成功,返回库引用
编译时候要加入 -ldl (指定dl库)
dlsym()
功能:
根据动态链接库操作句柄与符号,返回符号对应的地址。
包含头文件:
#include <dlfcn.h>
函数定义:
void*dlsym(void* handle,const char* symbol)
函数描述:
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称。
dlclose()
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
介绍完这三个函数后,再来看我们是如何来解耦的。我们在avienc文件夹里增加一个头文件avidef.h
#ifndef AVI_DEF_H_
#define AVI_DEF_H_
#include <sys/types.h>
#include "AVIWriter.h"
#include <media/stagefright/MediaWriter.h>
namespace android {
#ifdef __cplusplus
extern "C" {
MediaWriter *getaviwriter(int fd);
}
#endif
}
#endif
然后再增加一个.cpp文件avidef.cpp
#include "AVIDef.h"
namespace android {
#ifdef __cplusplus
extern "C" {
MediaWriter *getaviwriter(int fd)
{
return new AVIWriter(fd);
}
}
#endif
}
avienc对应的android.mk
avienc.so的Android.mk里
......
LOCAL_SHARED_LIBRARIES := \
libstagefright
......
同时删除libstagefright里Android.mk里的avienc动态库的引用。然后在在libstagefright里要用到avienc里的地方如下处理:
status_t err = OK;
sp<MediaWriter> writer;
void *handle;
handle = dlopen("/system/lib/libstagefright_soft_avi_enc.so", RTLD_LAZY);
if (!handle)
{
ALOGD("setupAVIRecording dlopen avi enc so is error, %s", dlerror());
}
else
{
ALOGV("setupAVIRecording dlopen avi enc so is succ");
dlerror();
typedef MediaWriter* (*GETAVIWRITER)(int fd);
GETAVIWRITER yunovo_aviWriter = (GETAVIWRITER)dlsym(handle, "getaviwriter");
const char *dlsym_error = dlerror();
if(dlsym_error != NULL)
{
ALOGD("setupAVIRecording dlsym is dlsym_error, %s", dlsym_error);
}
else
{
ALOGV("setupAVIRecording dlsym is succ");
writer = aviWriter(mOutputFd);
}
dlclose(handle);
}
如此一来,libstagefright的Android.mk里没有引用到avienc的库,但是在代码里又通过动态库引用到了它的函数.这样就达到了完美的解耦的目的.