Facebook的Dalvik运行期补丁

对于一个功能丰富的Android应用来说,要面对的挑战很多,其中很多问题绝大多数开发者都可能意识不到。Android应用的方法数量限制可能就是一个。

在一些传统的企业应用中,当处理跟业务数据相关的时候,通常不会将数据变量设置为公开访问public,而是会用getter/setter这样的存取方法来封装访问数据模型中的私有变量。在一般的编程实践中,这是一个被推荐和鼓励的做法。但是其副作用是类文件中会产生大量的方法(每一个私有变量都对应着2个方法)。

这个副作用带来的问题是,当应用安装在一些老机型上的时候,可能导致Android的一个Bughttp://code.google.com/p/android/issues/detail?id=22586。原因在于,在应用的安装过程中,会运行一个程序(dexopt)去根据当前机型为应用做一些特定的准备和优化。dexopt中使用了一个固定大小的缓冲区(名为LinearAlloc)来存储应用中方法的信息。最新的几个Android版本中,该缓冲的大小是8MB或16MB。但是Froyo(2.2)和Gingerbread(2.3)只有5MB。因为老版Android的这个限制,当方法数量超过这个缓冲大小的时候,会导致应用崩溃。

解决办法之一是可以利用http://android-developers.blogspot.hk/2011/07/custom-class-loading-in-dalvik.html提到的技术,将dex分成多个dex文件分别加载,首先加载核心dex,其他的模块和扩展功能放在其他的dex文件中。

但是某些时候,如果第二个dex包中的类需要被Android Framework直接访问到的话,上述方案就是不可行的。此时必须要将第二个dex文件注入到Android的系统class loader中。这个一般情况下是无法做到的,但好在Android是开源的系统,可以利用Java的反射去修改Android内部的一些数据结构来解决这个问题。

这个方案在实际运行中,你会发现其实LinearAlloc不仅仅只是在dexopt中存在,而是在每个运行中的Android程序中都有它的身影。dexopt使用LinearAlloc储存dex文件的方法信息的时候,运行期的Android应用使用它访问实际用到的方法。如果在运行期将所有的dex文件都加载到一个进程中的话,实际的方法数量还是会超过限制。应用虽然可以启动但是很快就会崩溃。

看起来似乎要么只能精简应用的功能,要么就只能放弃支持Android部分版本而只支持最新的ICS以上的版本。

再次回头去看Android的源码,找到LinearAlloc缓冲的定义https://github.com/android/platform_dalvik/blob/android-2.3.7_r1/vm/LinearAlloc.h#L33,也许你能意识到:其实只要能将缓冲从5MB增加到8MB就可以安全运转了。

也许,可以使用JNI把当前的缓冲替换成一个更大的缓冲区。这个方案看起来太疯狂了。修改Java的Class Loader的内部是一回事儿,修改运行中的Davlik虚拟机的内部可是另外一回事儿——运行中的代码可能会很危险。但是仔细查看过代码、分析所有使用LinearAlloc的地方后发现,只要在应用启动的时候去做,应该没什么危险。所需要做的就是,找到LinearAlloc对象,锁定,然后替换缓冲。

事实上,只要找到它,后面的事情就很顺利了。在这里https://github.com/android/platform_dalvik/blob/android-2.3.7_r1/vm/Globals.h#L519,DvmGlobals对象中保存了LinearAlloc缓冲区。大概距离对象的开始地址700个字节的位置。从对象的开头开始扫描到这里风险很大,但是幸运的是,可以用距离一步之遥的vmList对象作为起点。这里包含了一个值,可以通过JNI和JavaVM的指针做比较。

最终方案是,找到vmList的值,扫描DvmGlobals对象找到匹配的位置,往后跳几个字节找到LinearAlloc的头,然后替换缓冲区。编写JNI扩展,嵌入到应用中,启动,然后应用应该可以正常运行在Gingerbread上了。

但是又有一个问题是,这个方案在Sumsung Galaxy S II上会失败,这可是最流行的运行Gingerbread的手机。

似乎三星对Android做了一些改动,导致这个方案的失败。其它的厂商可能会做同样的事情。所以这个方案的代码应该得更加健壮才行。

经过观察,在GS II上,LinearAlloc的缓冲区地址距离实际要找的地址只有4个字节。于是调整代码,如果在期望的地址没有找到LinearAlloc,那么就在附近几个字节的范围内查找。做这件事需要获取当前线程的内存映射表,确保没有在附近的查找过程中访问到无效地址(否则会立刻导致应用崩溃)。

(以上就是Facebook的故事,via

3rd-party apt-key list for Ubuntu

Google Chrome

wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
sudo apt-get update
sudo apt-get install google-chrome-stable

JDK 1.7

echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
apt-get update
apt-get install oracle-java7-installer

NodeJS

sudo add-apt-repository ppa:chris-lea/node.js 

MongoDB

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list

Failed to clone a large git repository: The remote end hung up unexpectedly

When git clone a large repository sometimes it give you the error message like this:

$ git clone ssh://xxx@xxx:29418/xxx
Cloning into 'xxx'...
remote: Counting objects: 356213, done
remote: Finding sources: 100% (356213/356213)
Corrupted MAC on input. (277847/356213), 123.59 MiB | 3.86 MiB/s
Finished discarding for xxx
fatal: The remote end hung up unexpectedly
fatal: early EOF
fatal: index-pack failed

If you got this error message, you might want to try run the following command to fix it

git config --global http.postBuffer 524288000
git config --global --add core.compression -1

genymotion Qt error in Ubuntu

When I was trying to install Genymotion on Ubuntu 14.04, I got this error

Installing log handler
Logging activities to file: /home/****
Aborted (core dumped)

the log file /home/xx/.Genymotion/genymotion.log has the following message:

... [Genymotion] [Fatal] Cannot mix incompatible Qt library (version 0x40806) with this library (version 0x40804)

The reason of this error is that Genymotion has its own Qt library which is not compatable with the system Qt library.

To fix this error, we need to let genymotion use system's Qt library. The following commands can fix this issue

sudo apt-get install libxi-dev libxmu-dev
# go to your genymotion directory
mkdir QtLibs && mv *Qt*.so* QtLibs

可自动安装依赖的Ubuntu离线包安装工具 gdebi

可自动安装依赖的Ubuntu离线包安装工具 gdebi

Ubuntu下,通用的在线包管理工具是apt,但是对于下载好的离线deb包,我们通常会用dpkg -i xxx.dev来安装,但是这样常常会遇到依赖包不存在而无法安装的错误。dpkg不会为我们自动解决包依赖的问题,也不会自动下载安装所依赖的包。 gdebi是一个类似的管理工具,但是会自动下载安装依赖。

对于一个离线包,只需要使用下面的命令即可自动下载安装,包括所有依赖包

    gdebi package_name.deb

可以使用下面这条命令安装这个工具

    sudo apt-get install gdebi

better-package-tool-gdebi