Android中要小心的一种内存泄露
今天把android-developers blog上的一些文章看了下,有篇文章《Avoiding memory leaks》虽然以前也扫了眼,今天再看了一遍,觉得好bt啊。
private static Drawable sBackground; @Override protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); label.setText("Leaks are bad"); if (sBackground == null) { sBackground = getDrawable(R.drawable.large_bitmap); } label.setBackgroundDrawable(sBackground); setContentView(label); }
这段代码看得出会内存泄露么?按文中解释说,因为setBackgroundDrawable时,会在Drawable中把label这个TextView设为callBack,就是说Drawable中对label有引用,而label中对当前Activity的Context也有引用。所以当转屏时,系统自动重新建立UI时,原有的整个activity都不能被回收。
作为一个普通的Java程序员,真的能想到这个么?文档里也没有提示过,没看到这篇文章,又没自己去看Drawable的源代码的开发者,怎么能猜到这个。至少在我第一印象中,我是觉得Drawable对象只是类似于Image的一类对象。系统竟然在我不注意的时候自动设置了callBack。唉,我得检查检查自己的代码中有没有这样的问题了。我不知道除了这个Drawable外,还有没有其它类似的东西,真是防不胜防啊
03-15增: 不过我想了下,好像也有点不对啊
当第二次重设label.setBackgroundDrawable(sBackground);时,应该是把sBackground的
callBack改回成当前的label啦。那这时gc不就应该能工作么
Android platform study tips
Belows are some tips for getting/building/patching android source code.
1. Getting source
I’m not sure if you could download source code fast, if yes, skip this. But it took me more than 10 hours to finish getting the source code. The main git server limits to 3 connections from 1 IP address, so you can repo sync 3 projects together. For me, I start “repo sync”, “repo sync kernel” and “repo sync prebuilt” together, for the kernel and prebuilt projects are larger, and they could finish before repo sync touch them. Well, you can also start “repo sync external/webkit” first, for it’s not from the git server.
2. Building source
Added on 11-03, 2008. Today I installed a clean Ubuntu Linux 8.10, and did these steps to prepare the installation
$ sudo apt-get install git-core 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
(this step is optional, its size is about 22M)
$ sudo apt-get install python2.5
$ sudo apt-get install sun-java5-jdk
Warning: Do not use sun-java6-jdk,or you will meet below error when you do “make sdk”
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
In the document, it says run “make” in the root path of the android code. The result of “make” is generating system.img. For getting the whole runnable sdk, you can run “make sdk”. The sdk will be putted in out/host/YOUR_OS/sdk/. By the way, it’s said “make -j2″ may make it faster.
You can enable CCACHE with
export USE_CCACHE=1
Pls also set a cache dir with “export CCACHE_DIR=YOUR_CACHE_DIR”
After once make the whole projects, if you modify some code, you do not need to re-compile the whole projects with “make”. Run
$. build/envsetup.sh
in the android source code dir. And you can use “mmm” command to compile a project. Like “mmm packages/apps/Contacts”. Well, if you change some code like frameworks/base/core/java/android/widget/ListView.java, you need to run “mmm frameworks/base”, for only frameworks/base is a project, others are only part of this project. There would be a file named “Android.mk” under the root of a project.
Today, I tried to fix some bug in Contacts application. For testing the changes, if you run “make && make sdk” again, it would take a long time. You can easily do as I did:
1. Modify code
2. $mmm packages/apps/Contacts
If there’re compiling errors, go back to 1
3. make snod
With this command, it will build a new system.img very quickly.
4. $cp out/target/product/generic/system.img out/host/YOUR_OS/sdk/YOUR_SDK_PATH/tools/lib/images/
5. $out/host/YOUR_OS/sdk/YOUR_SDK_PATH/tools/emulator -wipe-data
It took about 2 minutes for me on my macbook pro with 2GHz CPU.
Well, you cannot use “make snod” for all the situations. It would not check the dependences. If you change some code in the framework which will effect other applications, please use a clean make.
3. Project layout
Most of the classes you see in the android sdk reference are in frameworks/base/core/java
And the resources like android.R.layout.simple_list_item_1 is defined in frameworks/base/core/res/res/layout
Build in applications are in packages/apps
To be continued..
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目录下旧有的文件,来测试。这个过程熟练了一般两分多钟可以测一把。
Howto set FULL SCREEN on Android
If you only want to remove the title bar, add this line to onCreate() method of your activity:
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
If you want to set your activity to use the whole display screen, which means also remove the status bar, you need to add one more line:
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NO_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NO_STATUS_BAR);
Reference: http://groups.google.com/group/android-developers/msg/1d7497e5626896a7
Note: It’s for SDK-M3, the flag name changed in SDK-M5.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class FullScreenActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NO_STATUS_BAR, WindowManager.LayoutParams.FLAG_NO_STATUS_BAR); setContentView(R.layout.main); } } |
Thanks to Ace’s comment. The code should be changed to
1 2 | this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ); |
移除小方块
再次贡献代码
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里多支持一个函数罢了。
后台运行还是心病,虽然一定要支持的,但怎么做,我还是不太有谱,还没有实验过官方的方式是否可行。
丢了代码怎么办?
重写一遍是王道,当然看起来也没其它办法
Mac下的虚拟机很好用,除了有一个问题很严重。我把两个虚拟系统(XP+2003)共享了一个磁盘,切换系统时经常XP一启动就自动磁盘检查,然后就把内容搞丢了。当然我不可能同时双系统开机,它当然也不支持一块硬盘双系统访问。我奇怪的就是为什么一个关机,换另一个启动时,还会检查。检查后,主要是把文件大小改回在该系统下编辑时的大小了,所以发现所有代码文件都最后少了一段,这个用FinalData这些恢复都有问题。有人提醒我用Source Control的东西了,可是我实在不太乐意在Mac OS上装个SVN Server。
写了一周的中文联系人啊。。。我花了一天时间几乎全部重写了一遍,好在有些原来的代码还留着,搬过来用用了。
同时做了些改进,现在稳定很多。
去掉了Cache机制,因为我们的设计一次只找第一个,不用遍历完,平均来说,用户等待时间很短,而且主要问题其实是那个AddressDB.c写得太有问题了,实在搞不定怎么处理
还有,DmQueryNextInCategory这个东西不要随便用,返回的MemHandle似乎被自动lock过,我是说它有时会被自动lock,注意是有时,所以你unlock一般会死机,不unlock就会经常crash,然后说Chunck-overlocked,真是fz的问题
检索代码几乎重写了。因为码表不止64K,原来REi申请了一个大的memory区,拷贝进去。我想想还是直接lock返回MemPtr作全局用,查两个表罢了。不过他那样做也对,为了考虑以后可以在其它程序中启动,或被其它程序调用,就需要做成可以不用全局变量的形式
后台运行,后台!
敲击键盘,本来想写篇IM后台运行设计构想的。虽然有idea,想想还不成熟,就不乱写了。
不知道有谁了解Chatopus怎么做到的不。难道用了那个创建后台线程的函数?不过那东西似乎也只能在本程序内做多线程啊。麻烦的东西。。。。