linphone-LpConfigImpl文件对应的JNI层文件分析
来源:互联网 发布:php小项目源码 编辑:程序博客网 时间:2024/06/05 01:19
说明
native 函数
private native long newLpConfigImpl(String file); private native void delete(long ptr); private native void sync(long ptr); private native void setInt(long ptr, String section, String key, int value); private native void setFloat(long ptr, String section, String key, float value); private native void setBool(long ptr, String section, String key, boolean value); private native void setString(long ptr, String section, String key, String value); private native void setIntRange(long ptr, String section, String key, int min, int max); private native int getInt(long ptr, String section, String key, int defaultValue); private native float getFloat(long ptr, String section, String key, float defaultValue); private native boolean getBool(long ptr, String section, String key, boolean defaultValue); private native String getString(long ptr, String section, String key, String defaultValue); private native int[] getIntRange(long ptr, String section, String key, int defaultMin, int defaultMax);
具体的函数分析
newLpConfigImpl
// LpConfigextern "C" jlong Java_org_linphone_core_LpConfigImpl_newLpConfigImpl(JNIEnv *env, jobject thiz, jstring file) { const char *cfile = env->GetStringUTFChars(file, NULL); LpConfig *lp = lp_config_new(cfile); env->ReleaseStringUTFChars(file, cfile); return (jlong) lp;}
lp_config_new
submodules/linphone/coreapi/lpconfig.c:LpConfig * lp_config_new(const char *filename){submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC LpConfig * lp_config_new(const char *filename);
根据以往的经验, 本篇写文章就是根据lpconfig.h和lpconfig.c文件展开的。 这充分说明了, linphone是按模块化来区分的 。 这一点在以后的分析中,和自己写程序的时候, 会有很大的帮助。
LpConfig * lp_config_new(const char *filename){ return lp_config_new_with_factory(filename, NULL);}
lp_config_new_with_factory
LpConfig *lp_config_new_with_factory(const char *config_filename, const char *factory_config_filename) { LpConfig *lpconfig=lp_new0(LpConfig,1); lpconfig->refcnt=1; if (config_filename!=NULL){ if(ortp_file_exist(config_filename) == 0) { lpconfig->filename=lp_realpath(config_filename, NULL); if(lpconfig->filename == NULL) { ms_error("Could not find the real path of %s: %s", config_filename, strerror(errno)); goto fail; } } else { lpconfig->filename = ms_strdup(config_filename); } lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",lpconfig->filename); ms_message("Using (r/w) config information from %s", lpconfig->filename);#if !defined(_WIN32) { struct stat fileStat; if ((stat(lpconfig->filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) { /* make existing configuration files non-group/world-accessible */ if (chmod(lpconfig->filename, S_IRUSR | S_IWUSR) == -1) { ms_warning("unable to correct permissions on " "configuration file: %s", strerror(errno)); } } }#endif /*_WIN32*/ /*open with r+ to check if we can write on it later*/ lpconfig->file=fopen(lpconfig->filename,"r+");#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH if (lpconfig->file==NULL){ lpconfig->file=fopen(lpconfig->tmpfilename,"r+"); if (lpconfig->file){ ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename); } }#endif if (lpconfig->file!=NULL){ lp_config_parse(lpconfig,lpconfig->file); fclose(lpconfig->file); lpconfig->file=NULL; lpconfig->modified=0; } } if (factory_config_filename != NULL) { lp_config_read_file(lpconfig, factory_config_filename); } return lpconfig;fail: ms_free(lpconfig); return NULL;}
LpConfig
struct _LpConfig{ int refcnt; FILE *file; char *filename; char *tmpfilename; MSList *sections; int modified; int readonly;};
这个下划线和不错, 将结构做成一个统一的格式, 使用的又是一个没有“_”格式, 这样又方便查找, 又利于其他人的理解。这里用的太帅了。
这里有一个FILE类型的声明, 我突然想到, 是不是那个作为配置文件的文件, 是不是通过这个类存储的呢, 有可能, 也有可能这就是一个结构,存储在另一个地方。我感觉还是前者, 毕竟是FILE类型么。好, 继续看看。
lp_new0
submodules/linphone/coreapi/lpconfig.c:#define lp_new0(type,n) (type*)calloc(sizeof(type),n)
原来是给再分配内存重新分配了一个名字。为啥! 就是为了便于阅读。也许吧。
lp_realpath
submodules/linphone/coreapi/lpconfig.c:char* lp_realpath(const char* file, char* name) {
char* lp_realpath(const char* file, char* name) {#if defined(_WIN32) || defined(__QNX__) || defined(ANDROID) return ms_strdup(file);#else char * output = realpath(file, name); char * msoutput = ms_strdup(output); free(output); return msoutput;#endif}
就是分配内存之类的, 这是个通用的格式。不予理会, 以后可以详细看。
ortp_file_exist
submodules/linphone/oRTP/include/ortp/port.h:ORTP_PUBLIC int ortp_file_exist(const char *pathname);submodules/linphone/oRTP/src/port.c:int ortp_file_exist(const char *pathname) {submodules/linphone/oRTP/src/port.c:int ortp_file_exist(const char *pathname) {submodules/linphone/mediastreamer2/src/ortp-deps/port.c:int ortp_file_exist(const char *pathname) {submodules/linphone/mediastreamer2/src/ortp-deps/port.c:int ortp_file_exist(const char *pathname) {submodules/linphone/mediastreamer2/src/ortp-deps/ortp/port.h:ORTP_PUBLIC int ortp_file_exist(const char *pathname);
这里有两个显现, 分别在oRTP和ortp-deps中。让我先看看, 在下结论这里为什么有两个, 让我带着怀疑‘两个实现不一样吗?’看看。
- 第一个
#if defined (_WIN32_WCE) || defined(_MSC_VER)int ortp_file_exist(const char *pathname) { FILE* fd; if (pathname==NULL) return -1; fd=fopen(pathname,"r"); if (fd==NULL) { return -1; } else { fclose(fd); return 0; }}#elseint ortp_file_exist(const char *pathname) { return access(pathname,F_OK);}#endif /*_WIN32_WCE*/
这里不得不赞叹, 这个预编译也太神奇了, 不同的代码可以存在一个地方, 我曾经在写java的时候被老板说过可不可以像c语言一样预编译,但是并没有什么在意, 没想到现在遇到了,真感觉c语言不错。
- 第二个
#if defined (_WIN32_WCE) || defined(_MSC_VER)int ortp_file_exist(const char *pathname) { FILE* fd; if (pathname==NULL) return -1; fd=fopen(pathname,"r"); if (fd==NULL) { return -1; } else { fclose(fd); return 0; }}#elseint ortp_file_exist(const char *pathname) { return access(pathname,F_OK);}#endif /*_WIN32_WCE*/
第一个和第二个代码一样啊, 为什么会这个样子呢, 估计是第二个是第一个的改进版, 所以才出奇的相似吧。肯定是这个样子的。
ortp_strdup_printf
submodules/linphone/mediastreamer2/src/ortp-deps/logging.c:char *ortp_strdup_printf(const char *fmt,...){submodules/linphone/oRTP/src/logging.c:char *ortp_strdup_printf(const char *fmt,...){
这一一看就是打印log日志的地方。详细看一看,毕竟我们公司的项目里面用到了,这个, 这个号对比的学习一番。
char *ortp_strdup_printf(const char *fmt,...){ char *ret; va_list args; va_start (args, fmt); ret=ortp_strdup_vprintf(fmt, args); va_end (args); return ret;}
这里还是归结到了其他地方, 看下面。
ortp_strdup_vprintf
submodules/linphone/oRTP/include/ortp/port.h:ORTP_PUBLIC char *ortp_strdup_vprintf(const char *fmt, va_list ap);submodules/linphone/oRTP/src/logging.c:char * ortp_strdup_vprintf(const char *fmt, va_list ap)
哇塞!
char * ortp_strdup_vprintf(const char *fmt, va_list ap){/* Guess we need no more than 100 bytes. */ int n, size = 200; char *p,*np;#ifndef _WIN32 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/#endif if ((p = (char *) ortp_malloc (size)) == NULL) return NULL; while (1){/* Try to print in the allocated space. */#ifndef _WIN32 va_copy(cap,ap); n = vsnprintf (p, size, fmt, cap); va_end(cap);#else /*this works on 32 bits, luckily*/ n = vsnprintf (p, size, fmt, ap);#endif /* If that worked, return the string. */ if (n > -1 && n < size) return p;//printf("Reallocing space.\n"); /* Else try again with more space. */ if (n > -1) /* glibc 2.1 */ size = n + 1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ if ((np = (char *) ortp_realloc (p, size)) == NULL) { free(p); return NULL; } else { p = np; } }}
这么多的预编译。真应该好好看看, 但是我估计我也不明白。往下看吧。
delete
extern "C" void Java_org_linphone_core_LpConfigImpl_delete(JNIEnv *env, jobject thiz, jlong lpc) { LpConfig *lp = (LpConfig *)lpc; lp_config_destroy(lp);}
lp_config_destroy
static void _lp_config_destroy(LpConfig *lpconfig){ if (lpconfig->filename!=NULL) ortp_free(lpconfig->filename); if (lpconfig->tmpfilename) ortp_free(lpconfig->tmpfilename); ms_list_for_each(lpconfig->sections,(void (*)(void*))lp_section_destroy); ms_list_free(lpconfig->sections); free(lpconfig);}
sync
extern "C" void Java_org_linphone_core_LpConfigImpl_sync(JNIEnv *env, jobject thiz, jlong lpc) { LpConfig *lp = (LpConfig *)lpc; lp_config_sync(lp);}
lp_config_sync
int lp_config_sync(LpConfig *lpconfig){ FILE *file; if (lpconfig->filename==NULL) return -1; if (lpconfig->readonly) return 0;#ifndef _WIN32 /* don't create group/world-accessible files */ (void) umask(S_IRWXG | S_IRWXO);#endif file=fopen(lpconfig->tmpfilename,"w"); if (file==NULL){ ms_warning("Could not write %s ! Maybe it is read-only. Configuration will not be saved.",lpconfig->filename); lpconfig->readonly=1; return -1; } ms_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)file); fclose(file);#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH /* On windows, rename() does not accept that the newpath is an existing file, while it is accepted on Unix. * As a result, we are forced to first delete the linphonerc file, and then rename.*/ if (remove(lpconfig->filename)!=0){ ms_error("Cannot remove %s: %s",lpconfig->filename, strerror(errno)); }#endif if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){ ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno)); } lpconfig->modified=0; return 0;}
啊哈, 我就说了么, 这个就是写入到文件中的, 不知道是哪个文件。 让我去找找, 应该LinphoneCoreFactory.java中, 我看看是不是。
呜呜呜, 没找到, 这个无耻的搜索linphonerc 这个文件了, 想知道这个是哪里知道的么, 是在linphone工程目录下, 是真的运行目录下。/data/data/org.sainthigh.linphone/files
让我们碰碰运气。
原来是在linphoneManager中。
mLPConfigXsd = basePath + "/lpconfig.xsd"; mLinphoneFactoryConfigFile = basePath + "/linphonerc"; mLinphoneConfigFile = basePath + "/.linphonerc"; mLinphoneRootCaFile = basePath + "/rootca.pem"; mRingSoundFile = basePath + "/oldphone_mono.wav"; mRingbackSoundFile = basePath + "/ringback.wav"; mPauseSoundFile = basePath + "/hold.mkv"; mChatDatabaseFile = basePath + "/linphone-history.db"; mCallLogDatabaseFile = basePath + "/linphone-log-history.db"; mErrorToneFile = basePath + "/error.wav"; mConfigFile = basePath + "/configrc"; mUserCertificatePath = basePath;
setInt
extern "C" void Java_org_linphone_core_LpConfigImpl_setInt(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jint value) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); lp_config_set_int((LpConfig *)lpc, csection, ckey, (int) value); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey);}
这个linphonecore_jni可有意思了, 把所有的有关与java和c对应的东西,都放在这里。
lp_config_set_int
submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value);submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value);submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value);submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){
void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"%i",value); lp_config_set_string(lpconfig,section,key,tmp);}void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"0x%x",value); lp_config_set_string(lpconfig,section,key,tmp);}void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"%lli",(long long)value); lp_config_set_string(lpconfig,section,key,tmp);}
lp_config_set_string
这个应该是写入文件的操作了。为什么会这么想呢, 前面已经遇到了这样的函数了啊。没什么可激动的。
void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){ LpItem *item; LpSection *sec=lp_config_find_section(lpconfig,section); if (sec!=NULL){ item=lp_section_find_item(sec,key); if (item!=NULL){ if (value!=NULL && value[0] != '\0') lp_item_set_value(item,value); else lp_section_remove_item(sec,item); }else{ if (value!=NULL && value[0] != '\0') lp_section_add_item(sec,lp_item_new(key,value)); } }else if (value!=NULL && value[0] != '\0'){ sec=lp_section_new(section); lp_config_add_section(lpconfig,sec); lp_section_add_item(sec,lp_item_new(key,value)); } lpconfig->modified++;}
不错不错, 原来差不多所有的有关操作都在这个类里面的,这是结构化, 不错不错。
LpItem
typedef struct _LpItem{ char *key; char *value; int is_comment; bool_t overwrite; // If set to true, will add overwrite=true when converted to xml} LpItem;
lp_section_find_item
LpItem *lp_section_find_item(const LpSection *sec, const char *name){ MSList *elem; LpItem *item; /*printf("Looking for item %s\n",name);*/ for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){ item=(LpItem*)elem->data; if (!item->is_comment && strcmp(item->key,name)==0) { /*printf("Item %s found\n",name);*/ return item; } } return NULL;}
LpSection
typedef struct _LpSection{ char *name; MSList *items; MSList *params; bool_t overwrite; // If set to true, will add overwrite=true to all items of this section when converted to xml} LpSection;
lp_config_find_section
LpSection *lp_config_find_section(const LpConfig *lpconfig, const char *name){ LpSection *sec; MSList *elem; /*printf("Looking for section %s\n",name);*/ for (elem=lpconfig->sections;elem!=NULL;elem=ms_list_next(elem)){ sec=(LpSection*)elem->data; if (strcmp(sec->name,name)==0){ /*printf("Section %s found\n",name);*/ return sec; } } return NULL;}
lp_item_set_value
void lp_item_set_value(LpItem *item, const char *value){ char *prev_value=item->value; item->value=ortp_strdup(value); ortp_free(prev_value);}
其实我现在还是没有搞明白ortp_strfup 到底是干什么用的, 好尴尬, 其实我已经看到过了。
lp_section_remove_item
void lp_section_remove_item(LpSection *sec, LpItem *item){
sec->items=ms_list_remove(sec->items,(void *)item);
lp_item_destroy(item);
}
lp_item_destroy
void lp_item_destroy(void *pitem){
LpItem item=(LpItem)pitem;
if (item->key) ortp_free(item->key);
ortp_free(item->value);
free(item);
}
就是将整个结构都删除掉, 让其不在保存。
lp_section_add_item
void lp_section_add_item(LpSection *sec,LpItem *item){ sec->items=ms_list_append(sec->items,(void *)item);}
将新的数据添加到列表中。
lp_section_new
LpSection *lp_section_new(const char *name){ LpSection *sec=lp_new0(LpSection,1); sec->name=ortp_strdup(name); return sec;}
lp_config_add_section
void lp_config_add_section(LpConfig *lpconfig, LpSection *section){ lpconfig->sections=ms_list_append(lpconfig->sections,(void *)section);}
setFloat
extern "C" void Java_org_linphone_core_LpConfigImpl_setFloat(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jfloat value) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); lp_config_set_float((LpConfig *)lpc, csection, ckey, (float) value); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey);}
lp_config_set_float
void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"%f",value); lp_config_set_string(lpconfig,section,key,tmp);}
lp_config_set_string
void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){ LpItem *item; LpSection *sec=lp_config_find_section(lpconfig,section); if (sec!=NULL){ item=lp_section_find_item(sec,key); if (item!=NULL){ if (value!=NULL && value[0] != '\0') lp_item_set_value(item,value); else lp_section_remove_item(sec,item); }else{ if (value!=NULL && value[0] != '\0') lp_section_add_item(sec,lp_item_new(key,value)); } }else if (value!=NULL && value[0] != '\0'){ sec=lp_section_new(section); lp_config_add_section(lpconfig,sec); lp_section_add_item(sec,lp_item_new(key,value)); } lpconfig->modified++;}
lp_item_set_value
void lp_item_set_value(LpItem *item, const char *value){ char *prev_value=item->value; item->value=ortp_strdup(value); ortp_free(prev_value);}
setBool
extern "C" void Java_org_linphone_core_LpConfigImpl_setBool(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jboolean value) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); lp_config_set_int((LpConfig *)lpc, csection, ckey, value ? 1 : 0); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey);}
setString
extern "C" void Java_org_linphone_core_LpConfigImpl_setString(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jstring value) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); const char *cvalue = value ? env->GetStringUTFChars(value, NULL) : NULL; lp_config_set_string((LpConfig *)lpc, csection, ckey, cvalue); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); if (value) env->ReleaseStringUTFChars(value, cvalue);}
setIntRange
extern "C" void Java_org_linphone_core_LpConfigImpl_setIntRange(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jint min, jint max) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); lp_config_set_range((LpConfig *)lpc, csection, ckey, min, max); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey);}
lp_config_set_range
void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value) { char tmp[30]; snprintf(tmp, sizeof(tmp), "%i-%i", min_value, max_value); lp_config_set_string(lpconfig, section, key, tmp);}
getInt
extern "C" jint Java_org_linphone_core_LpConfigImpl_getInt(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jint defaultValue) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, (int) defaultValue); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); return (jint) returnValue;}
getFloat
extern "C" jfloat Java_org_linphone_core_LpConfigImpl_getFloat(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jfloat defaultValue) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); float returnValue = lp_config_get_float((LpConfig *)lpc, csection, ckey, (float) defaultValue); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); return (jfloat) returnValue;}
getBool
extern "C" jboolean Java_org_linphone_core_LpConfigImpl_getBool(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jboolean defaultValue) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, defaultValue ? 1 : 0); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); return (jboolean) returnValue == 1;}
getString
extern "C" jstring Java_org_linphone_core_LpConfigImpl_getString(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jstring defaultValue) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); const char *cvalue = defaultValue ? env->GetStringUTFChars(defaultValue, NULL) : NULL; const char *returnValue = lp_config_get_string((LpConfig *)lpc, csection, ckey, cvalue); jstring jreturnValue = NULL; if (returnValue) jreturnValue = env->NewStringUTF(returnValue); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); if (cvalue) env->ReleaseStringUTFChars(defaultValue, cvalue); return jreturnValue;}
getIntRange
extern "C" jintArray Java_org_linphone_core_LpConfigImpl_getIntRange(JNIEnv *env, jobject thiz, jlong lpc, jstring section, jstring key, jint defaultmin, jint defaultmax) { const char *csection = env->GetStringUTFChars(section, NULL); const char *ckey = env->GetStringUTFChars(key, NULL); int *values = (int*)calloc(2, sizeof(int)); lp_config_get_range((LpConfig *)lpc, csection, ckey, &values[0], &values[1], defaultmin, defaultmax); jintArray returnValues = env->NewIntArray(2); env->SetIntArrayRegion(returnValues, 0, 2, values); ms_free(values); env->ReleaseStringUTFChars(section, csection); env->ReleaseStringUTFChars(key, ckey); return returnValues;}
- linphone-LpConfigImpl文件对应的JNI层文件分析
- linphone-LinphoneAddressImpl文件对应的JNI层文件分析
- linphone-LinphoneProxyConfigImpl文件对应的JNI层文件分析
- linphone-LinphoneCallLogImpl文件对应的JNI层文件分析
- linphone-LinphoneCallParamsImpl文件对应的JNI层文件分析
- linphone-LinphoneChatMessageImpl文件对应的JNI层文件分析
- linphone-LinphoneChatRoomImpl文件对应的JNI层文件分析
- linphone-LinphoneCoreFactoryImpl文件对应的JNI层文件分析
- linphone-LinphoneFriendImpl文件对应的JNI层文件分析
- linphone-PayloadTypeImpl文件对应的JNI层文件分析
- linphone-LinphoneInfoMessageImpl文件对应的JNI层文件分析
- linphone-LinphoneEventImpl文件对应的JNI层文件分析
- linphone-PresenceActivityImpl文件对应的JNI层文件分析
- linphone-PresenceModelImpl文件对应的JNI层文件分析
- linphone-PresenceNoteImpl文件对应的JNI层文件分析
- linphone-PresencePersonImpl文件对应的JNI层文件分析
- linphone-PresenceServiceImpl文件对应的JNI层文件分析
- linphone-ErrorInfoImpl文件对应的JNI层文件分析
- 数据库问题原因详解(脏读、不可重复读、幻读)
- 顺序表应用2:多余元素删除之建表算法
- cdoj 1354 柱爷很忙
- 团队管理
- php class回顾篇
- linphone-LpConfigImpl文件对应的JNI层文件分析
- Android事件和事件监听 来个飞机设个背景图让飞机上下左右动
- linux负载排查
- angular中刷新显示与ng-bind
- 博弈论 ( Nim游戏+SG模板)——S-Nim ( HDU 1536 )
- 数据库视频总结(心得)
- android之layout布局和ListView中的一些属性介绍
- |Tyvj|二叉树|P1441 求二叉树的先序序列
- 代理模式