最近在Android上开发,主要是将以前Win32以及Windows Mobile上的一些程序库移植到Andriod,并在Java层封装二次开发接口。

虽然Google封装的很彻底,但是Android毕竟是一个Linux系统,Linux相关的很多东西在Android的Native开发中是绕不开的。我一直很懒得碰Linux,多么麻烦啊……当时vista出来没多久,听说ubuntu界面是如何如何华丽blabla的,远不是以前的什么红帽红旗Linux可比,于是第一次装ubuntu,是8.04,其不支持我笔记本的HD2400显卡……后来听说它支持了,于是我装了11.04,可惜此时换了一个新本,它又不支持我的nvidia/i5显卡切换……

前几年真正深入研究使用过的主要是ASP.NET平台和VC这两个环境中的技术。现在托管界中说会.NET就和说会算1+1差不多,连欧美写书的大师们都说.NET程序员整体水平是比Java低……他们怎么就断定学.NET的必定都是拖控件流?……于是别人问我,我就说我写C++的……当然最近确实一直写C++

我发现Android上关于Native开发以及封装的讨论很少,因此边做边总结一些东西。Android NDK和SDK的安装使用、Native开发方法、Android上的JNI等基本的东西已经有好多人写过了好多东西,不再提了,在此主要讨论完全会写Hello Word之后,下一步遇到的各种问题和解决方法。


1. 从 VC 到 Android Native C++的一些改变

函数库、语言相关的东西就不说了,看过C/C++各种开源库源代码的人都知道跨平台对代码的要求,各种宏判断,平台无关性的函数封装,字节对齐,大端小端的处理,我觉得SQLite的VFS是一段多么美丽的跨平台代码……就是写的挺麻烦。

主要提一下编译方面的异同

1.Windows下的DLL为了导出函数,或者要写def文件,或者用丑陋的__declspec(dllexport)。Linux下并不需要类似的机制,默认情况下所有符号都是导出的,而且也不需要.lib这类东西,你可以直接链接一个so,通过头文件定义使用里面的类或函数。

2.Android的应用开发(即不理Android源代码)中,Native编译主要有两种方式:1)使用NDK的ndk-build 2) 使用NDK安装Android GCC工具链,自己写makefile。

VC下估计用nmake编译C++的人不多吧,VC总是智能的编译工程里的所有.cpp,傻瓜式的IDE。NDK提供的这两种编译方式相关的问题之后一点点的总结。

3.Android为了保证后续版本的兼容性,在NDK中仅提供极少数的稳定C++ API,真的是够少的,不过好歹Linux标准C函数和标准的C++库已经基本完备了。除了C函数和STL你还有更多要求?自己编译吧...

4.调试真是一大难,Windows下GDB连接调试只成功过一次,剩下的都莫名其妙的卡死了。暂时先用日志吧,反正也还算方便,因为是移植在Win32和WM上已经正常跑的库,所以需要调试的东西不是很多。


2. Android.mk 与 Application.mk

NDK下的那几页帮助文档是必看的,整个NDK就那么几页文档再不看,光想上网搜搜入门教程是不行滴。

写Android.mk的时候还是遇到了几个小陷阱的。

1. 路径问题。各种mk下千万不要写/cygdrive/....这样的路径。Win下开发Android,NDK是要求使用cygwin的。但是最终运行的编译器gcc之类的还是.exe的Windows程序。mk里还是要写Windows路径的,可以接受的形式诸如 /C:/NDK/xxxxx 或直接 C:/NDK/xxx,注意斜杠的方向

2.LOCAL_PATH := $(call my-dir)这句,陷阱。所以说一定要仔细看那几页文档。$(call my-dir)返回得实际不是【当前mk文件的路径】,而是【最后打开的mk文件的路径】,如下就会出错的

LOCAL_PATH := $(call my-dir)

include $(PROJECT_PATH)/prebuilt.mk

.........

这样用的时候,LOCAL_PATH可能在prebuilt.mk中被改写

include $(PROJECT_PATH)/prebuilt.mk

LOCAL_PATH := $(call my-dir)

.........

这样用的时候,LOCAL_PATH实际获得的是prebuilt.mk的路径

mk实际是makefile的一部分,可以参考NDK里面自带的一些库的Android.mk,其实写法很灵活的

3. 注意使用ndk-build时,ndk-build直接找 jni/Android.mk,所有在此mk中包含的mk也会被加入,但是编译并不是发生在$(BUILD_SHARED_LIBRARY)这句执行时,这句只是记一下一会儿要编译这个库。一直到所有mk执行完,ndk-build才开始编译各个库,所以想在mk中加入拷贝编译好的库到指定位置等指令的想法只是在做梦,好吧我就做过这个梦,不过最终还是另写编译脚本完成


3. 中文编码问题

NDK上的中文编码问题绝对是一个大老虎,至少比纸老虎强一点,皮纸老虎吧……

实际上不用强调”中文“编码,这就是单纯的不同字符集转换问题。

首先,Android2.2以前不支持wchar_t,好吧现在的确支持wchar_t了,不过这又如何,GCC下wchar_t是坑爹的4字节,不是2字节!怪不得你看各种开源库各种不使用wchar_t,人家都直接用void*或者unsigned short。JNI中的jchar也是unsigned short,所以wchar_t纯粹就是坑爹用的。

虽然可以用编译指令-fshort-wchar强制wchar_t使用2字节,不过这样与其他库链接的时候就会收到无数的警告……除非你保证你遇到的所有库(包括C/C++标准库)都是用这个指令编译的。我试过一个犀利无比的WCHAR16定义,用于自动选择最合适的类型,保证WCHAR16总是2字节字符:

template<int>struct tag_auto_WCHAR_CHOOSER;template<>struct tag_auto_WCHAR_CHOOSER<4> {typedef unsigned short WCHAR16;};template<>struct tag_auto_WCHAR_CHOOSER<2> {typedef unsigned short WCHAR16;};typedef tag_auto_WCHAR_CHOOSER<sizeof(wchar_t)>::WCHAR16 WCHAR16;

不过GCC下如此不能保证做到 WCHAR16与unsigned short用于同参数签名的重载函数,而我就是要重载,那么wchar_t对我就无用了,抛弃吧

好吧有点跑题了。。。

首先是源代码文件的编码问题:NDK下C++的char*默认编码是UTF-8,但是你如果这样写: const char* szChina = "中国", 那么就天知道什么编码了。默认情况下,如果你的该代码文件本身使用GBK,则szChina里就是GBK编码,如果代码文件是UTF-8, szChina就也是UTF-8。你非要问如果代码文件是UTF-16?我没试,不过如果你的代码能编译过,想来szChina就会是UTF-8,原因吗,说到这里了你们懂得。如果你不懂,你可以先找 MBCS/UNICODE教程,再找GBK/UTF-8/UTF16的编码规则及其与Unicode的关系教程,再找 UCS-2/UTF-16LE/UTF-16BE区别分析,最后看GCC对于不同编码源文件的处理,然后就一定已经懂了


编码转换:NDK下没提供任何直接的编码转换方法,不过你可以编译一个十分著名的库,iconv,当然要写Android.mk,不过你可以下载大好人hgl868已经改好了的……编译后稍微有点大,900多K……不过去掉除了UTF-8,UTF-16LE,GBK之外的其他编码库后,iconv能精简到200K……至于使用,刚才大好人家的传送门点击打开链接

至于传回Java转换的方法,还是省省吧……使用Android内部的那个转换库?你保证你用这个能继续正常在Android 4.0上运行?




更多相关文章

  1. 箭头函数的基础使用
  2. NPM 和webpack 的基础使用
  3. Python list sort方法的具体使用
  4. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  5. Android系统
  6. Android下基于XML的Graphics shape使用方法
  7. Android4.0 Design之UI设计易犯的错误2
  8. Android(安卓)4.0新增Space及GridLayout初谈
  9. Android(安卓)NDK Hello

随机推荐

  1. 吴恩达课程从未失望,斯坦福CS230深度学习
  2. 自己近几天的所见,所想
  3. 超完整的Vue入门指导
  4. AWS 大数据实战 Lab3 - 数据可视化(四)
  5. 一线架构师的一些项目管理心得
  6. MySQL深入研究:快速安装MySQL
  7. 谈谈我在北理工博士分享会学习到的东西
  8. 结合HTTP 502-504 状态码学习Nginx配置
  9. spring cloud alibaba 组件版本关系
  10. 李飞飞老师离开谷歌?谣言而已