Android用NDK和整套源码下编译JNI的不同
前些天要写个jni程序,因为才几行代码,想着用ndk开发可能容易些,就先研究了下。不过最终我的代码虽然只有几行,但需要libdl,因为用到了dlopen一类的函数,只能又换到用android源码体系下编译。
不过两个都用了下,就发现了些不同。我其实并不能确定这些是不是实际存在的差异的,我只是拿ndk的sample和我见到的android源码里的jni代码做的比较
1. ndk支持的库很少,所以用以前最好先看看自己需要的功能是不是已经包括了,省得白忙活。不过人家是有解释的,说ndk里有的库才是稳定的,其它的建议你不要用。嗯,google总是这一套说辞,不公开的api也是。至于要看android源码中多了哪些库,make modules后grep一下lib开头的应该就是了,不过也不是所有都能用啦。
2. 注册函数的方法是不同的。举例说,我在com/evan129/jnitest/jniutils.java有个native int foo()方法,需要在jni中实现
在ndk中,你只要实现这个函数,然后函数名是以jint java_com_evan129_jnitest_jniutils_foo(jnienv* env, jobject thiz) 命名既可。也就是说,如果你的jni只要实现这个函数,并且功能也很简单的话,那么你的jni c/cpp文件里只需要这一个函数就完事了。
但在android源码中编译jni代码是不同的,jni中的函数名无所谓。不过你至少还需要加一个
jniexport jint jnicall jni_onload(javavm* vm, void* reserved)方法,这个方法你可以找个现有的复制一把就行,检查运行环境的。然后主要是这个方法中会调用(*env).registernatives函数,在这里把jni中的方法和java文件中的方法关联起来。
看起来像是ndk自动补上了这部分工作,我并不清楚原理啦。
3. 有个很诡异的区别,自动传入的jnienv* env好像不是一个东西。因为在android源码中使用这个env一般是如env->newstringutf(…),而ndk中sample里的一处是(*env)->newstringutf(…) 这env和*env差很大吧。但两处函数传入的都是jnienv* env,我只能怀疑jnienv的定义是不是都是不同的。
末了,就是说下我这个很简单的jni折腾了我两天的一个问题。如前所说,我是用android源码来编译的,自己在packages/app/下建了pinyinjni工程,下面有个jni目录,每次直接用mmm packages/app/pinyinjni/jni来编译。jni目录下的android.mk中指定的名字是这样的local_module := libpinyin 每次编译都很顺利,生成了libpinyin.so。但是我在java文件里,system.loadlibrary(“libpinyin”)总是失败。感谢paranoia@newsmth帮我解决了这个问题,告诉我在loadlibrary时用”pinyin”。但是,这个事情实在太奇怪了,那文件名是libpinyin.so啊,而且我在这里用/data/data/…/libpinyin.so这种完整路径也会失败,我以前虽然没写过jni,但调用过,用完整路径指定jni so文件应该不会有问题的。或者,从另一个角度想,如果我local_module的名字不是用lib开头的,那它调用时应该用什么名字?这个”lib”前缀又是哪步删除的?我没有多做测试了,但是猜想ndk中应该是没有这种诡异的问题的。
Android源码研究Tips
从去年12月开始写android程序开始,都差不多快一年了。上月google终于公开了android的较完整的源码。最近几天,闲来无事时就给他们提交一些补丁。
这里讲一些开发中的技巧,或许对大家有帮助,转载请注明出处:
下载源码
首先一个问题是http://source.android.com/在国内访问有时,甚至是大多时候,是有问题的。那么怎么翻墙就需要大家自己解决了。其实只是为了看源码的话,问题并不大,源码更新的站点还是可以正常连接的,但是如果有打算要提交patch,那么用tor都有些麻烦,我是找了个vpn出去的。获取源码请参照
http://source.android.com/download
嗯,我这里也可以给一些简单的步骤介绍吧:
1. 你的系统需要是linux或者是mac os x。需要先安装git-core和gnupg,在debian/ubuntu linux上,可以用apt-get获取,mac上使用macports吧。额外的,在mac上你需要先安装xcode 2.4以上版本,另外再用macports安装gmake, libsdl。 事实上编译期还会有问题,这在http://source.android.com/download页文档中没有提及,缺失ncursor和zlib库,也需要单独安装(这个在mail list里有讨论,或许我晚些可以补全些)。
2008-11-03 补 正好干净安装了ubuntu linux 8.10记录一下需要的东西
注意可能blog中会自动折行,请以$来区分新行
$ sudo apt-get install git-core gnupg (gnupg实际已自带)
$ sudo apt-get install flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl
$ sudo apt-get install zlib1g-dev
$ sudo apt-get install valgrind (可选,有21m大,我觉得一般人是用不到的)
$ sudo apt-get install python2.5 (实际上不用装,ubuntu 8.10已经自带)
$ sudo apt-get install sun-java5-jdk
注意,不要用 sun-java6-jdk, 不然在make sdk,具体来说是make doc这一步中,遇到这个错误:
docs droiddoc: out/target/common/docs/dx
javadoc: error – in doclet class droiddoc, method start has thrown an
exception java.lang.reflect.invocationtargetexception
com.sun.tools.javac.code.symbol$completionfailure: class file for
sun.util.resources.openlistresourcebundle not found
2. 下载repo脚本,放到/bin目录下,加上可执行权限
$ curl http://android.git.kernel.org/repo > repo
$ sudo mv repo /bin
$ sudo chmod a+x /bin/repo
其实文档中是把这个放在个人用户的~/bin目录下,但是要改path,我嫌麻烦而已就放/bin下了
3. 创建一个放置源码的文件夹,如叫myandroid。这里要注意下,需要区分大小写的分区,linux下一般是用ext3的,是符合要求的,而mac下默认的分区是不区分大小写的,请自行创建分区。但是事实上,我以前测试过,在非大小写敏感分区上,直接make会报错,但修改makefile后直接跳过检查这一步,最终是可以成功编译的。
4. 在myandroid目录中执行
$ repo init -u git://android.git.kernel.org/platform/manifest.git
中间会提示输入电子邮件什么的,如果你打算要提交patch的话,用google accounts注册过的邮箱
5. 在myandroid中执行 repo sync就可以开始下载源码了
但是这一步非常慢,这里有些文档中没有的技巧了:
测试发现,主站点是限三线程连接的,也就是说你可以同时开三个窗口做不同部分下载,
我建议是第一个窗口直接执行repo sync,第二个窗口执行repo sync kernel,第三个窗口执行repo sync prebuilt
因为kernel和prebuilt这两个工程比较大。
有时因为多次频繁连接,会有服务器端拒绝连接的错误,此时等两三分钟再重试就好了
repo的使用请看repo help。如果只是要学习源码的话,那只用repo sync一个命令就行。repo sync不带参数的话会更新所有子项目,可以repo sync project_path来指定更新项目。 那么project_path在哪可以找到呢?myandroid/.repo/manifests/default.xml (你至少需要先repo init过才有这些东西)
编译源码
当repo sync全部完毕时,进入myandroid目录,首先执行make,大约会耗时一个小时左右(在我的一代macbook pro上大概是这时间)。make最后会生成system.img。看到mail list里有人问这个干嘛用,其实是文档里没有完全写清楚。一般来说,我想普通人还需要的是make sdk这一步,会在myandroid/out/host/your_os/sdk中生成sdk。和从google官方下载下来的sdk差不多,可以直接运行了
在进行过一次完整的make后,以后对一些程序的修改,大可不必重新make sdk,因为make sdk实在太慢了。
先在myandroid目录下执行
$ . build/envsetup.sh
然后你就会多出几个可用的命令。在改了contacts项目后,可以简单的执行mmm packages/apps/contacts/来单独编译这个部分。为了可以直接测试改动,需要生成新的system.img,在myandroid目录下执行
make snod即可 当然,如果你改动的是emulator或者其它外围相关的,而非系统内部的东西,就不只是要重新生成system.img了
修改测试
这个周末,我提交了大约四到五个patch。主要是联系人程序相关的一些bug修正。如果你是想修改build in的程序,像联系人这样的,最好先在编译前把contacts包删去,这样编译出来的system.img不带contacts程序会方便很多,可以直接用eclipse开发contacts程序,在这个模拟器上调试。像一般情况下,contacts是带在system.img内的,对程序修改后想测试,比较麻烦,需要先运行emulator,然后adb uninstall掉原先的程序,再安装自己的,并且如果数据没卸干净,会有permission出错或uid不否而报错的情况。最最麻烦的是,每次重启emulator,build in的程序在被adb uninstall后又会出现,这个问题在真机上不会出现。所以我建议你直接去掉contacts再编译个system.img出来,不过今天的几个修改都比较简单,我全部都是改一点就用mmm重新编译contacts程序,然后用make snod来出一个新的system.img,覆盖掉sdk目录下旧有的文件,来测试。这个过程熟练了一般两分多钟可以测一把。
ZDic on sf.net move to svn source control
i found that i’m not familiar with cvs operation. bobgreen@hi-pda had developed lots to improve the zdic. i guess it would be better to accept the patches to make this project alive.
anyone who’d like to get zdic source code, pls use a svn client to checkout source code from https://zdic.svn.sourceforge.net/svnroot/zdic/trunk/
in command line, you can use:
svn co https://zdic.svn.sourceforge.net/svnroot/zdic/trunk/
you will need both zdic and zdicfont project to compile the whole project.
zdic with skin is putted in https://zdic.svn.sourceforge.net/svnroot/zdic/branches/zdic_skined
hope it works for you.
移除小方块
再次贡献代码
qq昵称里老有人喜欢加ascii 09之类以图排序在前面,可是手机上显示会是方块的,还有一些虽然也是gbk字符,可是在手机上显示不了,亦或者用户用的是gb,显示不了gbk扩展的内容,就移除好了
void RemoveUnprintChars(char * str,Boolean removeNewLine) { UInt16 i,len; UInt8 chW; WChar ch; char * p=str; if (str==NULL) return; len=StrLen(str); i=0; while (i < len) { chW= TxtGetNextChar(str, i, &ch); if (TxtCharIsPrint(ch) || (!removeNewLine && ch!='\n') ) { if (chW==1) *p++=ch; else { *p++=ch/256; *p++=ch%256; } } i+=chW; } *p=0; }
带报错的atoi
好久没写东西了,随便填上一段代码
这个qq烦死了,我自己用了一个钟头,不停在后台运行聊天,都一点问题没有。不过还是有不少人有重启问题,不得不把各种检查都做细致来
上一段代码替换stratoi,不过里面有对%1a这种处理,这是qq协议中带的,一般大家用不到就去了这一段吧,那其实result可以换个变量名:)
Int32 myAToI(const Char *str) { UInt8 result; if (str==NULL || StrLen(str)==0) { ErrDisplay("严重异常:转换空数字串,请退出"); return 0; } if (str[0]=='%') { if (StrLen(str)!=3) { ErrDisplay("严重异常:数字转换出错%,请退出"); return 0; } if (str[1]>='a') result=str[1]-'a'+10; else result=str[1]-'0'; result*=16; if (str[2]>='a') result+=str[2]-'a'+10; else result+=str[2]-'0'; return result; } if (!TxtCharIsDigit(str[0]) && str[0]!='-') { ErrDisplay("严重异常:数字转换出错1,请退出"); return 0; } for (result=1;result<StrLen(str);result++) if (!TxtCharIsDigit(str[result])) { ErrDisplay("严重异常:数字转换出错2,请退出"); return 0; } return StrAToI(str); }
Palm Dev 随笔(2.11)
接下来会常有这种小文章了,可惜国内开发者不多,不然我估计会有不少人有兴趣的,哈哈:p 自我感觉良好一下
嗯,今晚的要收工。写点刚才一个钟头里遇到的一些东西:
1. 打开网络设置面板
launchwithcommand(sysfiletpanel, sysfilecnetworkpanel,sysapplaunchcmdpanelcalledfromapp, null);
2.断开网络连接
hsnetworkdropconnection();
3.退出当前程序
这个我以前试过直接调appstop()似乎也可以,就是frmcloseallforms()啦,不过我觉得逻辑上有点说不通,因为appeventloop()没道理退出啊。
现在一般我就加个全局变量forcestop,默认为false,
改appeventloop那行为
while (event.etype != appstopevent && !forcestop);
有时不能用全局量,或者某些特定情况,直接自己加个appstopevent到事件对列就好了
竟然才三点,其它的没什么好写了。哦,还有把680模拟器中文化。。。嗯,这个懒得说了
PalmQQ动工
我们说人是有惰性的,这是相当正确的。
实在无聊的紧,我今天又是7点多睡,一直睡到十点半,现在又清醒的很,没有办法,想着法子找点好玩的东西,未果,好吧,干活。这个qq是得写过了,唉,我还答应tencent的ppmm二月底完工呢:(
开始一个新的系列,记录一个开发过程。
想来想去,还是挑了cw了,pods实在难用得让我没法忍,想想这次应该是用不到什么arm code的,上次唯一想做成arm code的是那个md5计算函数,用来算密码的东东,速度不是问题,而是上次分段没写好,眼看要过64k了,我打算把它编译到外部,好让程序小一点,可惜最后没有成功。
建新工程,用c application wizard,我看来看去,其实就这个合适点。不过里面的模板还是糟糕了些,对于要开始一个不是demo的程序来说,几乎都是自己写过的好。删掉所有mainform的东西,几乎清空了rcp文件,留了一个menu id editonlymenubar,一个alert id romincompatiblealert,还有把图标先存好了。顺便说一句,iconfamilyex这东西里不能只有一项的,不然图标位置会偏。
这次有个新打算,把每个form做成一个rcp文件,以后好改。而像alert这些都是单独成文件。include这么多*_rsc.h是有些麻烦,所以再写个resource_header.h,光写#include好了。
palmqq.c文件里也就留了apphandleevent,appeventloop,appstart,appstop,pilotmain。在pilotmain这里也小改了一下,我正在琢磨怎么让后台运行重入时定位到正确窗口。
想了下,还是都从appstart()入口好了,如果错误返回>10的值,如果是未登录的正确情况返回0,已登录从后台重进就返回1。于是pilotmain里根据appstart()的返回值再做个switch跳到对应的form就好了。
每次写程序前,目标总是定很大,前阵子想抓mobileqq 2007的包,因为新版的手机qq支持好友分组,支持群消息,支持传图片和文件了。腾讯不提供这协议,我用的还是旧版协议。本来希望自己分析来做的,现在想想吃力不讨好的事情还是不要做得好,何况不小心还是会完不成的。
要开始写loginform先了,这次把各个pref也分开来好了,比如登录窗口要保留原来的登录号码和密码来着,不都放palmqq.h里了,太乱了,反正这个pref应该只有loginform用到,不如直接在这里做好了。在palmqq.h里只存它的prefid
前两天让ayang帮忙设计界面的,一直没什么好看的,想想还是算了,不如走清纯路线。。。和原来的版本差不多就好。我原来还想支持320×480的,现在想想也算了,还是先做出来再说,以后要改应该也不是太困难,每个form里多支持一个函数罢了。
后台运行还是心病,虽然一定要支持的,但怎么做,我还是不太有谱,还没有实验过官方的方式是否可行。
后台运行,后台!
敲击键盘,本来想写篇im后台运行设计构想的。虽然有idea,想想还不成熟,就不乱写了。
不知道有谁了解chatopus怎么做到的不。难道用了那个创建后台线程的函数?不过那东西似乎也只能在本程序内做多线程啊。麻烦的东西。。。。
Fucking PIM SDK
官方的sdk里的代码写得也这么糟糕。。。害偶调程序又麻烦死
pack和unpack联系人记录时,没有检索blob段的大小。按文档要求单个blob不能超过1k,事实上它自己带的铃声和照片也不止了,这也就算了,系统内部的东西我们管不着。但分明发现有的用户的联系人记录里有blob数据有50k,导致我存不进我自己的cache信息了。
相当fz,没有任何检查,返回错误还是因为size超过uint16,溢出了,靠,什么玩意啊,还得自己去改addressdb.c文件,直接把这个超大的blob给扔掉,鬼知道什么程序生成的,估计也用不着,反正createid没找到对应的程序。不知道当初怎么写进去的
中文联系人设计-part3 拼音对照表生成
rei之前已经有一份gbk汉字的所有拼音首字母表了
不过显然这个是非常糟糕的东西,请问看我这blog的人里,有人知道“机”字有个w开头的拼音么?“柴”还有个z开头的音
而且对于多音字,他没有存储哪个音是常用音。
所以不得不赶紧自己改一份出来。
先复制了份”gbk2312汉字拼音对照表“下来,用以下函数拆行出来
read more