編寫ios和android共用的c/c++庫時 使用iconv的問題(轉)


因為在項目中需要同時維護ios和Android,不同的代碼不利於開發的便捷和以后的維護,所以在最近的一個項目中,兩種手機應用的通信部分打算使用c/c++庫來統一編寫,ios調用.a靜態庫,android調用.so動態庫的方式來實現。

由於通信時,從服務端獲取到的中文數據為GBK編碼,android和ios通過c++庫獲取到的中文亂碼,於是打算在c++庫層統一將GBK轉成UTF-8后再傳遞給上層應用。

由於優先考慮跨平台的方案,最終我采用iconv庫來實現轉碼功能。參考網上搜到的一個代碼如下

 

[cpp]  view plain  copy
 
  1. #ifndef STRINGUTIL_H_  
  2. #define STRINGUTIL_H_  
  3.   
  4. #include <cstring>  
  5. #include <iconv.h>  
  6.   
  7. #ifdef _WIN32  
  8. #pragma comment(lib,"iconv.lib")  
  9. #endif  
  10.   
  11. int code_convert(const char *from_charset,const char *to_charset,const char *inbuf,size_t inlen,char *outbuf,size_t outlen) {  
  12.  iconv_t cd;  
  13.  const char **pin = &inbuf;  
  14.  char **pout = &outbuf;  
  15.   
  16.  cd = iconv_open(to_charset,from_charset);  
  17.  if (cd==0) return -1;  
  18.  memset(outbuf,0,outlen);  
  19.  iconv(cd, const_cast<char**>(pin), &inlen,pout, &outlen);  
  20.  iconv_close(cd);  
  21.  return 0;  
  22. }  
  23.   
  24. /* UTF-8 to GBK  */  
  25. int u2g(const char *inbuf, size_t inlen, char *outbuf, size_t outlen) {  
  26.  return code_convert("UTF-8","GBK",inbuf,inlen,outbuf,outlen);  
  27. }  
  28.   
  29. /* GBK to UTF-8 */  
  30. int g2u(const char *inbuf, size_t inlen, char *outbuf, size_t outlen) {  
  31.  return code_convert("GBK", "UTF-8", inbuf, inlen, outbuf, outlen);  
  32. }  
  33.   
  34. #endif /* STRINGUTIL_H_ */  
  35. </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的代碼如下
[cpp] view plain copy
 
  1. #ifndef NDK_ANDROID_SUPPORT_ICONV_H  
  2. #define NDK_ANDROID_SUPPORT_ICONV_H  
  3.   
  4. #if !defined(__LP64__)  
  5.   
  6. #ifdef __cplusplus  
  7. extern "C" {  
  8. #endif  
  9.   
  10. #include <stddef.h>  
  11.   
  12. typedef void* iconv_t;  
  13.   
  14. iconv_t iconv_open(const char*, const char*);  
  15. size_t  iconv(iconv_t, char**, size_t*, char**, size_t*);  
  16. int     iconv_close(iconv_t);  
  17.   
  18. #ifdef __cplusplus  
  19. }  // extern "C"  
  20. #endif  
  21.   
  22. #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項目中編譯成功后能正常運行並轉碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM