自己动手,丰衣足食

最近虾米Android用得不甚顺畅。主要问题是,拿着虾米Android听电台,听着听着,自动就关了…… 一般都是在启动别的程序的时候挂掉。各种程序都可能引发,用内存多的概率更大。有时候没动手机也会自己挂掉。 之前就怀疑是Android低内存自动杀进程功能干的。近日研究了一下: 首先,用ps | grep xiami列xiami进程。每次挂掉之后,列进程可以发现com.xiami那个进程不见了。过一会儿,这进程又会被重新启动起来。但是音乐播放不会自动恢复。 看系统信息里面,虾米总共开了俩服务,一个是push message receiver,对应ps里面那个com.xiami:push。另一个就是music play service,对应那个被干掉的com.xiami。 我印象里,Android里面开了服务,就不应该会被干掉,至少会比那些后台程序更晚被干掉。利用adb shell dumpsys meminfo,可以发现后台还有好多进程啊…… 仔细看一眼dumpsys meminfo的输出,可以发现当从xiami切到别的程序之后,xiami被列在了Background分类里,而其他的还有A Services和B Services。xiami占的内存也不少,系统想杀进程的时候,挑大的杀,于是就杀了虾米…… 于是首先是这么个问题:既然xiami开了服务,为啥会被扔到Background里去?继续观察,可以发现xiami被杀并重启之后,居然跑到了A Services分类里…… 这样就不会被杀了,问题是音乐也停了…… 为了研究到底杀了重启是啥样的过程,我去网上找了个ServiceDemo。实验后发现,对于一个普通程序,就算开了服务,切到后台也是Background。等到内存不够,系统会先杀了你,之后再启动一个进程来仅仅跑服务。这样或许Activity那边的内存就能释放了?一般杀之前不调onDestroy(),杀完重启的时候调onCreate()和onStart()。所以要适应这种情况,可以在onStart()里处理被杀了重启的情况,并且继续之前的工作。 但是我觉得其他音乐播放器不是这样啊…… 于是随便找了个音乐播放器,开了之后切到别的程序,再看meminfo。这样可以注意到,那个程序并没有被扔到Background里,而是在Perceptible里面…… 于是去网上看啥样的程序会变成Perceptible。虽然找到了ActivityManager里面IMPORTANCE_PERCEPTIBLE这个常数定义,但是没有哪儿告诉我咋样可以变成Perceptible。 既然这样…… 只能看代码了。正好机子上有之前下下来的Android 4.0代码,在里面找PERCEPTIBLE,于是在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java里面找着了。 貌似成为PERCEPTIBLE的条件是,某调整值>=PERCEPTIBLE_APP_ADJ。但是继续看代码,发现这个调整值会因为各种理由而决定…… 通过继续研究dumpsys,发现还可以dump别的,例如dumpsys activity就可以dump出activitymanager这边的信息。在这个信息中,就包括了调整值设置的原因。观察那个靠谱的音乐播放器,发现他Perceptible的理由是foreground-service。 其实如果熟悉Android Service的话,或许已经知道这是啥了。但是我不太熟嘛…… 继续看ActivityManagerService,发现这个原因的触发条件是有foregroundServices,而这个值唯一会变成true的地方只有updateServiceForegroundLocked(true)。而这个东西会在setServiceForeground()里被调用。这个东西貌似只有外部引用。到这里我终于跑到网上去搜foreground service…… 发现Service类有个startForeground()方法,那基本上就是这个了…… 根据Android的描述,普通Service还是会被干掉的,只是日后由内存了可能会重开你。但是Foreground Service是这种只要你一干掉用户立马会有反应的,所以会尽可能不去干他。 我在ServiceDemo里加了startForeground()调用,果然他也成了Perceptible,咋样都没被干掉…… 那,虾米是不是没有调用这个startForeground()呢?我跑去搞了个最近看见的Android反编译器,ded,是PSU的人做的…… 反编译出来,貌似的确没有startForeground()。但是有一个对setForeground()的调用,可惜是false。另外,在新版Android里,setForeground()已经没用了…… 这样就知道是虾米写得烂了,我试了几个音乐播放器,都会变成foreground service…… 不过倒是有个也败了的,那就是豆瓣…… 既然如此嘛,要不改虾米,要不改系统。我尝试修改了/proc//oom_adj,但是貌似系统不care这个。我尝试修改了/sys/modules/lowmemorykiller/parameters/minfree,改小了点,希望系统不要没事情杀进程。但是问题依旧,而且chrome启动还更卡了…… 那只好改虾米。那个ded貌似反编译有些问题,编译回去也不太行。上网上找找apk反编译,找到了apktool。 这个东西能够把apk完整的反编译成dalvik VM的汇编码,还能编译回去,真是不错…… 于是反编译出来,找合适的地方加。因为startForeground()还需要提供一个Notification,而虾米本身就会显示Notification,我希望最好还是用虾米原来的。 找了一下,发现虾米的Notification都是NotificationsUtil管的。里面有个notifyOnGoing(),貌似就是显示平时歌曲信息那个Notification的。于是我篡改了NotificationsUtil.notifyOnGoing()里面显示Notification那句。startForeground()是Service类的函数,需要Service对象,正好MusicPlayService被传了进来,于是我直接把 invoke-virtual {v2, p3, …

Continue reading ‘自己动手,丰衣足食’ »

忘了重定向stderr导致的某问题

最近某python程序常常抛exception。而且比较神奇的是,貌似这个exception是一层层抛出来的。 先是最内层except块打的log,然后是次外层,然后是再外面…… 我本来写的代码并不会重抛异常,所以这很神奇…… except块大约是这样的: except Exception as e: Log.error("send error: %r" % e) traceback.print_exc()except Exception as e: Log.error("send error: %r" % e) traceback.print_exc() 能看见第一行的log,但是之后print_exc()的结果就没了。下一行log是更外面的except块的log…… 于是这个大概就是print_exc()抛的异常,但是这个东西能抛啥…… 之后想了一下,就算是异常一路抛到最外面,也应该有个backtrace,而log里啥都没有…… 想想backtrace是打到stderr的,难道stderr没有重定向…… 之后再看了一眼,log是打到stdout的…… 再去看启动python程序的脚本,貌似只重定向了stdout,忘了重定向stderr了…… 难道断了的stderr不能写?我又写了个小程序ssh到服务器上跑: #!/usr/bin/env python   import sys import time   time.sleep(10)   try: sys.stderr.write(’test\n’) except Exception as e: print "Exception: %r" % e#!/usr/bin/env python import sys import …

Continue reading ‘忘了重定向stderr导致的某问题’ »

bug备忘

今儿搞定了俩bug,系统能正常通过test suite了,备忘一下。 1. 拷贝构造器漏拷了某field 这个的确很弱智…… 但是拷贝构造器在每次增减field的时候都要手动改,这个真麻烦…… 就不能提供一个只要我没动的都自动拷一遍的拷贝构造器么…… 2. 拷贝构造器中,成员变量中的父指针没有指向新对象 简单来说,就是 class A; class B { A *parent; }; class A { B b; }class A; class B { A *parent; }; class A { B b; } A在拷贝的时候,应该吧b这个成员的parent指针指向新的A…… A::A(const A& other) : b(other.b) … { … b.setParent(this); … }A::A(const A& other) : b(other.b) … { …

Continue reading ‘bug备忘’ »

ibus 1.4.0在gvim里的输入问题

ibus 1.4.0在gvim里有些问题。具体来说,启动gvim之后数秒内无法输入,之后控制台输出 (gvim:77687): IBUS-WARNING **: Process Key Event failed: Timeout was reached 这句话,之后在gvim里可以输入,但是用不了输入法。 根据研究貌似是gvim的问题,FreeBSD的vim还停留在7.3.121,而新的vim,例如7.3.315就没有问题。 这个问题的报告: http://code.google.com/p/ibus/issues/detail?id=1249 https://bugzilla.redhat.com/show_bug.cgi?id=673438 有个workaround是gvim -f,但是毕竟麻烦。 于是手工改了个vim 7.3.315的port,用着挺好…… 除了切输入法的时候会输入一个空格以外…… FreeBSD的vim留在7.3.121是有原因的。首先是7.3.122这个补丁打完之后无法编译。 其实7.3.122是说,不附带src/auto/config.mk,在顶级目录Makefile里加上自动复制src/config.mk.dist为这个文件…… 但是可能是BSD这边编译方法的问题,没有直接跑顶级Makefile,于是缺文件…… 其实自己复制过去就好了。 之后是7.3.225这个补丁打不上。貌似原因是patch数错行数了…… 或许是^M这些东西的关系。 解决方法是在test79.in和test79.ok最后加些只有”+”的行。加到正好数量对为止…… 反正这俩文件也没啥关系 于是现在用着vim 7.3.315,感觉很好…… ibus也没有问题……

更新ibus和ibus-pinyin的ports到1.4.0

其实早就该更新的…… 不过之前尝试了一下,觉得1.4.0不咋的,还有些问题,于是没管…… 结果终于有好心人搞了个1.4.0的patch,还群发邮件说各位基于ibus的ports的maintainer们,快更新吧! 他顺便还加上了GTK3支持的选项,看来大家都要渐渐向GTK3迁移了么…… 在那个patch的基础上,提了些修改,现在在等待commit…… 具体来说: * 貌似其实ibus不依赖Qt?为啥从前加上去了呢…… 于是去掉了 * 没有post-deinstall这个target的,应该在pkg-plist里用@unexec来执行卸载命令的…… 所以从前的ibus port删除之后,是不会更新gtk2的immodules列表的…… 这么多时间了也没人发现…… 为了这个,ibus-pinyin也更新到1.4.0。但是ibus-pinyin 1.4.0居然要编译器支持c++0x…… BSD的古老的gcc 4.2怎么行…… 于是就让port依赖gcc 4.4+,没办法…… 其实ibus-pinyin还有个替代选项是用boost,但是开了之后没法编译。虽说打个补丁应该可以,但是太麻烦…… 另一个好处是,现在就不依赖boost了…… 其他更改: * 不知道为啥,ibus-pinyin有个${DBTAR}在DISTFILES里,而这个东西没定义过…… 删了删了 * 去掉boost支持,反正依赖gcc 4.4+了…… * RUN_DEPENDS单独定义。这个是portlint说的,说BUILD_DEPENDS其实还有别的东西,不能直接赋给RUN_DEPENDS * 目前禁用lua扩展。反正也没人用…… * 加了俩选项,选择是否编译Android DB和OpenPhrase DB。貌似是俩词库,OpenPhrase 那个挺大…… * 加了选项是否依赖opencc进行简繁转换 ibus 1.4.0在gvim里有些问题,换篇文讲吧…… 于是就等待commit了~