因為在項目中需要同時維護ios和Android,不同的代碼不利於開發的便捷和以后的維護,所以在最近的一個項目中,兩種手機應用的通信部分打算使用c/c++庫來統一編寫,ios調用.a靜態庫,android調用.so動態庫的方式來實現。
由於通信時,從服務端獲取到的中文數據為GBK編碼,android和ios通過c++庫獲取到的中文亂碼,於是打算在c++庫層統一將GBK轉成UTF-8后再傳遞給上層應用。
由於優先考慮跨平台的方案,最終我采用iconv庫來實現轉碼功能。參考網上搜到的一個代碼如下
- #ifndef STRINGUTIL_H_
- #define STRINGUTIL_H_
- #include <cstring>
- #include <iconv.h>
- #ifdef _WIN32
- #pragma comment(lib,"iconv.lib")
- #endif
- int code_convert(const char *from_charset,const char *to_charset,const char *inbuf,size_t inlen,char *outbuf,size_t outlen) {
- iconv_t cd;
- const char **pin = &inbuf;
- char **pout = &outbuf;
- cd = iconv_open(to_charset,from_charset);
- if (cd==0) return -1;
- memset(outbuf,0,outlen);
- iconv(cd, const_cast<char**>(pin), &inlen,pout, &outlen);
- iconv_close(cd);
- return 0;
- }
- /* UTF-8 to GBK */
- int u2g(const char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
- return code_convert("UTF-8","GBK",inbuf,inlen,outbuf,outlen);
- }
- /* GBK to UTF-8 */
- int g2u(const char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
- return code_convert("GBK", "UTF-8", inbuf, inlen, outbuf, outlen);
- }
- #endif /* STRINGUTIL_H_ */
- </span>
代碼用g++編譯,在ubuntu上測試正常,但在移植到ios和android均出現問題。
1.首先講ios上出現的問題,這個比較簡單。
使用xcode能夠成功編譯出.a靜態庫,但是在ios應用編譯時,出現如下問題:
Undefined symbols for architecture x86_64:
"_iconv", referenced from:
code_convert(char const*, char const*, char const*, unsigned long, char*, unsigned long) in libVmNet.a(VmNet-EA133239D29A369D.o)
"_iconv_close", referenced from:
code_convert(char const*, char const*, char const*, unsigned long, char*, unsigned long) in libVmNet.a(VmNet-EA133239D29A369D.o)
"_iconv_open", referenced from:
code_convert(char const*, char const*, char const*, unsigned long, char*, unsigned long) in libVmNet.a(VmNet-EA133239D29A369D.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
后來在網上搜到的解決方法,原來需要在項目中添加libiconv.2.4.0.tbd動態庫。然后重新編譯app成功運行。

2.接下來講在android上出現的問題。
在android studio中編譯.so庫,使用的是最新版的2.2.2,默認使用的是cmake編譯。
編譯中,出現找不到iconv.h頭文件,網上搜索解決方法,大致有以下幾種方法:
1.項目中添加iconv庫的源代碼,跟項目一起編譯。用到了android.mk,這個又跟現在官方推薦使用的cmake相違背了,我下載了iconv的源碼,一大堆,不太懂,暫時放棄這條路子。
2.先編譯一個libiconv.so的動態庫,然后編譯自己的庫。這個是用到了android.mk,不想用這個,嫌麻煩,放棄。
3.據說ndk自帶了iconv的支持,只是需要在android.mk中增加
LOCAL_WHOLE_STATIC_LIBRARIES += android_support
$(call import-module,Android/support)
又是android.mk,但我用的是cmake,放棄。
雖然放棄了方法3,但是從中可以知道ndk有自帶的iconv功能,在一個叫android_support的靜態庫中,於是,我找到了iconv.h所在的路徑
/Users/zhourui/Library/Android/sdk/ndk-bundle/sources/android/support/include,libandroid_support.a所在路徑
/Users/zhourui/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libandroid_support.a;
於是參考了google安卓官方文檔中對cmake參數的解釋,在CMakeLists.txt中添加了以下參數:
# 相當於g++ 中的 -I參數,這個參數讓cmake能找到iconv.h這個頭文件 include_directories(/Users/zhourui/Library/Android/sdk/ndk-bundle/sources/android/support/include)
target_link_libraries( # 這是我需要生成的庫文件VmNet.so
VmNet
# Links the target library to the log library
# included in the NDK.
# 使用android_support.a庫
/Users/zhourui/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libandroid_support.a
${log-lib} )
CMakeLists.txt中只需要這么配置即可。完成了頭文件路徑搜索和靜態庫的鏈接。
但是直接編譯還是會出錯,會提示
error:unknown type name 'iconv_t'
error:use of undeclared identifier 'iconv_open'
到使用到iconv.h的轉碼文件中查看,發現能找到iconv.h文件,但是iconv_t怎么會未定義呢,於是進入到iconv.h文件中查看,發現iconv.h的代碼如下
- #ifndef NDK_ANDROID_SUPPORT_ICONV_H
- #define NDK_ANDROID_SUPPORT_ICONV_H
- #if !defined(__LP64__)
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include <stddef.h>
- typedef void* iconv_t;
- iconv_t iconv_open(const char*, const char*);
- size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
- int iconv_close(iconv_t);
- #ifdef __cplusplus
- } // extern "C"
- #endif
- #endif // !__LP64__</span>
我發現其中有一段是我用紅色標注的,#if !defined(__LP64__) 這句表示在編譯64位程序時,頭文件便是空的了,那么便表示ndk中的iconv不支持64位。
到app下的build.gradle中查看有這么一段:
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -fexceptions"
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
}
}
這個表示編譯出.so動態庫包含x86_64和arm64-v8a兩種64位庫,那么將這兩種abi去除即可。
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -fexceptions"
abiFilters 'x86', 'armeabi', 'armeabi-v7a' // 由於不支持64位,所以只保留32位}
}
再次編譯出.so動態庫,使用在app項目中編譯成功后能正常運行並轉碼。
