Improve ListView Performance on Android

The performance of ListView on Android is sometimes a disaster when it comes to very complex list. Things become more frustrating when you are working with other things with Android like network images and dynamic loading.  The best example of a complex ListView is the Facebook feed in the Android app. They posted an article to show how complex but smooth ListView can be achieved at the same time.

In short, they split each post in the feed into several parts: the header, the main body and the action panel. Then each part will be able to be reused when rendering the ListView. This is a very clever alternative solution to the problematic ListView performance.

However, for my case things are more complicated since the content of a post does not have a fixed style. It may be pure text, or text with some decorations, or full of images without a single line of text. Moreover, the content of the post should be interactive, i.e., when you click on a link or an image, the app should respond with different actions with that click.

In a previous open source Android app that I contributed to, we tried at least two options.

  1. Use a ListView with WebView, i.e., each item in the ListView is a WebView. In this case, it is easy to interact with other parts of the app while at the same time achieving high dynamic usability. Everything works fine when it is with Android 4.2 or before. Performance becomes a really big issue when it comes to Android 4.4, in which Google made the webkit kernel more functional but also heavier. Creating a WebView becomes a really time intensive task which we cannot afford. Thus we got several other workarounds for this problem.
    1. Keep as many WebViews as possible in the memory as cache. In the case that a user scroll down and up a WebView, the WebViews that are cached in the memory can be used directly. To keep the memory use to an acceptable level, we can use Soft Reference to cache each object.
      This workaround does not work well since when you scroll down the WebView, you are still creating new WebViews and it will only work when you scroll up and down, which is actually not very useful.
    2. Reuse each WebView. This does improved the scrolling experience since we no longer create a lot of WebViews. Instead, we alter the content of each WebView. The experience is still a little bit laggy since rendering HTML also takes a lot of time.
      This workaround worked better than the 1st one but it brings another big issue, i.e., when reusing the WebView, the height of it will not change when the length of its content changes. In other words, we will see a lot of blanks in the ListView when the length of each content of WebView varies significantly.
  2. Use a ScrollView with WebViews and render a page of posts at once. This is very brute but surprisingly work! The disadvantage of this solution is, firstly it is very memory consuming since the whole page of posts live inside the main memory. Secondly the app may froze for a second or two while rendering the page, depending on how complex the page is. However, once the page is rendered, it becomes super smooth no matter how you scroll it!

When I was figuring out the solution for the new app TGFC, I was thinking about what kind of solution I should implement. However, I realized that a WebView may not be the only option for my scenario since actually I don’t really need all the features that a heavy WebView provides. I want to have my app to be able to show some different styles of text, several images and that’s all. I don’t need stuffs like z-index or absolute positioning. In my case, TextView can work perfectly to meet my demands.

I started with a ListView of TextViews. At first, everything works fine when there is only text content in the TextView. However, things become a little bit complicated when I introduced network images into my app. Inside the ImageGetter that I was using, I first download images asynchronously to the local cache. Later on I load those images into memory and show them on the screen. I see notable lag when using ListView as the outside container when it was loading images from the local cache, so I switched to ScrollView later and rendered the whole page of posts at once.

The only thing that we need to be careful about is the usage of images. Large images can consume a lot of memory and make the ScrollView really laggy. Remember to resize those images when loading them into the memory.

Right now I still have some issues with the interaction between my TextView and the other parts of the app. I don’t have time to fix those issues and see whether they come from the HTML that Jsoup generates or the way I use TextView for the time being. But I’m pretty confident that these problems are not unsolvable and actually I have got some ideas on how to handle them. For the time being, TextView + ScrollView may be the solution for extreme complex and dynamic ListView with good user experience with better memory performance than WebView + ScrollView if you do not want to parse HTML and analyze the content to distinguish text parts and image parts.

This article is written as a complement to my Zhihu answer. In my opinion, the reason why we are having so many problems with ListView is the problematic designing of ListView that comes out from Google. Here are my suggestions on how to improve the performance of ListView from the Android designing perspective.

  1. Prepare more Views before scrolling. Currently the ListView will only prepare one more View that are invisible to the user but I believe its not enough. The number of views to be rendered should be extended.
  2. Android should introduce a kind of @PausableTask that is run on the UI thread but pausable to let the UI thread draw things to be shown on the screen. We can only show the basic outlines of the items in the initialization of a View and then gradually fill it with detailed content using the intervals of UI refresh, just like the way that Facebook used in its webpage, filling the page with place holders and filling those holders with content later on.


Android Support Library v22.1 轻度使用感受

前两天Google出了新的Support Library v22.1,这两天轻度使用了一下,贴点使用感受,慢慢更新

  1. Google又改Activity的基类了,原来是ActionbarActivity,现在是AppCompatActivity。很多逻辑也发生变化,之前是用Toolbar作为Actionbar的,现在也不需要了,直接自带Actionbar了,也是Material Design的设计,这个改动如果是从新写App的话挺好,如果是之前的老项目。。估计要改的吐血吧
  2. 各种Material Design的组件更新极其缓慢,这次确实又加了几个,但是设置不了深度,从用户的角度根本看不出来有多大变化。Annimation什么的通通没有,Google看这样子是不打算支持4.x用户了么?
  3. AlertDialog出了新的了,改下import就行了,好评。


最近在写一个新的应用,后端用的是Nginx +  Python + Django + Gunicorn + Celery + libav,Celery又依赖RabbitMQ,为了让Celery和Gunicorn跑起来又用了Supervisor,东西太多配置又太繁琐,写代码调试和部署都是一个挑战,时间长了怕是配置文件和日志的location都要忘光光。我的工作环境是Mac和Windows,服务器则是Ubuntu或者Centos,像libav这种东西基本上算是Linux独有的,Mac虽然是*nix like但是毕竟不是Linux,想了半天最后决定放弃在Mac做Celery worker的调试,改在Ubuntu的服务器上用最原始的Log来分析调试,简直蛋疼到不行。

这两天在网上看到了Docker这个玩意,看起来确实不错:在Linux上有近乎Native的性能,在其它平台上则通过类似虚拟机的机制来构建运行环境,通过remote debug机制和IDE沟通,而部署则有点像拷贝虚拟机镜像:Docker的每一个优势都直击开发部署的痛点,下次部署的时候一定要用一下。


Overload vs Override, Java vs Groovy

Groovy is one of my favorite languages. It is simple yet powerful. More importantly, it can be integrated into Java stack easily, bringing us thousands of libraries that are ready to use.

One interesting thing about Java and Groovy is the difference in treating overload. A lot of scripting languages do not have function overloading. For example, in Python you need to use named parameters if you want to have some kinds of workaround for that. In JavaScript you need to check if a parameter is defined manually in the function, which brings complexity and lacks intuition. Same things happen to Matlab, where you have to check the number of arguments like the old C code:

Java has overloading, and it is reasonable that Groovy also has overloading. However, they treat overloading differently.

Java dispatch overloading during compile time. In other words, it is fixed and cannot be changed during run-time. However, as a dynamic language, Groovy dispatch overloading at run-time, which causes a different result in the following program.

These two pieces of code are identical to each other, except for the class name. However, the results are quite different. In Java, the output is String and Object, while in Groovy, the output is String and String.

What if we call the Java code in Groovy, and call the Groovy code in Java?

For the Java code, the output is String and Object, and for Groovy, String and String.

This experiment tells us the only deciding factor of which kind of dispatch method is being used is your code type that you are actually writing. If you are writing Java, not matter you are referring a code from Java or Groovy,  overloading functions are dispatched at compile time. If you are writing Groovy, overloading functions are dispatched at run time. This brings us a very essential problem: we need to be really careful with overloaded functions that come from Java library when we are writing Groovy code: it is the devil in the detail.

The good news is that, for function override, both Java and Groovy treat it in run-time and share the same behaviour.



现在已经将CDN切换到了CloudFlare上,并且启用了Strict SSL,服务器端也设置了HTTPS的重定向,不过由于WordPress一直使用的是绝对地址来链接网站内容,所以还会有一些图片和视频的资源文件走HTTP,这个问题可能需要再过段时间想想怎么解决。

The DDOS attack against Github & HTTP Hijacking in China

It is well known that recently a large scale DDOS attack was carried out against Github. The obvious attacker: Great Fire Wall in China, did this by HTTP Hijacking.

A lot of Chinese websites use Baidu’s Analytic service to collect visiting information as an alternative to Google Analytics, which is not available in mainland China. The GFW hijacked the Baidu’s Analytic Javascript file of all in bounding requests, i.e. from outside China to inside China, and changed the content of it so that this piece of Javascript code will visit several certain Github pages every 2 seconds. This caused a heavy burden on Github’s servers and eventually became a DDOS attack in which GFW made use of millions of Chinese people who are outside China but visiting Chinese websites to achieve its goal.

HTTP hijacking is so common in China that it is not a surprise for me to hear that GFW eventually made use of this to attack Github. In China, every ISP is using HTTP hijacking to push ads to end users. In fact, some of the hijacking code is so ugly written that it will even hijack some API HTTP requests like Json response. It has been confirmed by some of the users of an open source android app which I contributed to. They reported this issue several days ago and such circumstances will only happen when using certain ISP service.

The reason why there are so many HTTP hijacking in China may lie in two reasons. First, the ISP market is not a free market and only some of nationally owned companies are allowed to provide Internet service. Second, few websites use HTTPS to deliver their contents, which made them vulnerable to MITM attacks.



一直以为搬瓦工最近速度越来越慢是因为港大的出口带宽吃不消了,今天有空在Digital Ocean上开了一个Singapore的VPS测试了一下,发现是搬瓦工的问题。









俗话说,”所有的男人都是孩子“。 即使已经不再是当初那个无忧无虑每天只知道玩耍的小男孩,我的玩心却依然还在。最近在网上买来了两个玩具,一个是Google Cardboard的华强北仿制版,另一个是PowerUp 2.0的仿制版。

  • Google Cardboard

Google Cardboard 是去年Google I/O大会时推出的一款廉价虚拟现实设备。之所以廉价,是因为它使用手机作为主要的显示和交互设备,而Cardboard本身就是几块硬纸板和两个凸透镜而已。



至于我买的这个华强北山寨版,则是在Google Cardboard的原本设计上进行修改,使用塑料来构建机身,并且设计了三根松紧带来固定,镜片也可以调节焦距和瞳距,可谓是一个超级加强版。



构造和Google Cardboard基本一致,一边是有两个凸透镜,用于观测的孔,另一边则是用于防止手机的卡扣结构。把手机从这个位置放进去,就可以戴上这个玩意,在镜头这边进行观看了。那么看的时候呢,手机上显示的大致是这个玩意:



实际体验上来说,效果还是相当不错的。我的手机分辨率只有720p,如果手机的分辨率能到1080p甚至2k,体验会更好。我觉得最有趣的应用还是Google Cardboard Demo里面的Youtube,那个视频墙出来的时候,只能用震撼两个字来形容。更重要的是,这个Youtube Demo是一个完全能拿来用的Demo,里面可以用语音进行搜索,搜索结果也会显示在视频墙上,视频本身虽然是2D的,但是营造出的氛围,让人仿佛独自置身于一间偌大的放映厅中静静欣赏。

  • PowerUp 2.0

PowerUp 2.0是一款电动纸飞机工具。纸飞机还能电动?当然,它是一款可以加装在纸飞机上电动发动机,让纸飞机可以飞的更高、更远、更久。准确的说来,这个玩意并不能算是山寨,因为它本身就是国内玩具厂商的杰作,只不过墙内开花墙外香,到了国外才被人发现、热捧。







Unblock Everything

平时经常使用网易云音乐听歌,但是经常遇到“版权受限”的提示也够烦人的。要解决这个问题,简单的做法可以直接去装一个Unblock Youku的插件,复杂一点的可以用GoAgent加上放在国内PaaS上的服务器端搭一个本地代理。这两种办法的本质差不多,都是绕经国内的代理服务器进行访问。虽然这些方法都能work,但是都有一个共同的缺点,那就是普适性不高。Unblock Youku和其它代理性质的扩展有冲突,虽然并不是完全不能解决,但是折腾起来也是够呛;而GoAgent的PaaS实现在移动端上几乎不可用,我在Android平台上找了好久都没有找到一个能用的办法,更不要提iOS了。如果要说最好的解决方法,就是能在大陆有一个能放代理的服务器。这样PC端可以用ProxySwitchy来切换,Android有ProxyDroid,iOS则可以直接在系统设置里设置全局代理。

以目前国内的网络环境来看,单独拥有一台常规VPS的成本并不低,每个月费用大约在几十到上百不能,如果只是用来做偶尔的Unblock的话,未免成本太高。我知道的一个低价的,接近VPS的解决方案,是使用百度的BAE 3。

BAE 3相当于一个没有root权限的VPS,前提是你能申请到Port权限。所谓Port就是一个能在百度的IP上给你做端口转发的服务,能够在一定程度上将原本只能使用HTTP访问到的虚拟机,暴露给外部的网络。如果能够申请到Port服务,就可以用SSH登录到虚拟机上,虽然没有root权限,但是跑一个Python写的HTTP/SOCKS/SS代理还是没什么问题的,如果偷懒直接跑SSH也不是不行。由于负载很低,我们选择最便宜的128M内存Python Worker配置,加上端口服务每天的费用是0.3元,这样一个月9块钱,基本上啥都能做了。

BAE 3的缺点在于目前Port服务还在测试中,如果要使用的话需要额外申请。我当初申请的时候,还不算很难,现在的话就不太清楚了。不管难度如何,需要额外申请毕竟是一道门槛,不算普适性的解决方案。


  1. 在国内的PHP空间或者PaaS上部署GoAgent的服务器端,然后在搬瓦工的VPS上部署客户端,让客户端监听广域网上的请求,这样就相当于有了一台出口IP在国内的HTTP代理服务器。
  2. 在国内的PaaS上部署一个“反向代理”客户端。这里的“反向代理”,并不是指Nginx这一类的反向代理,而是指让服务器主动连接客户端的代理服务。由于大多数的PaaS服务大多只会暴露一个经过层层转发只能跑HTTP的80端口,根本监听不到任何客户端发来的Socket请求,所以没有办法做代理服务器。但是,通常情况下PaaS对于从代理服务器出来的Socket连接往往没有什么限制。因此我们只要在PaaS上部署一个反向代理,让PaaS主动连接搬瓦工的VPS并建立Socket连接,再在搬瓦工上建立一个转发服务,将某个端口上的流量转发到这个PaaS连过来的Socket连接上,我们就相当于有了一个端口在搬瓦工VPS出口却在国内的代理服务器。





具体到实现上,虽然GoAgent是我一开始想到的做法,但实际上比较之后我还是选择了WallProxy. 原因有二,一是GoAgent本身还是给GAE用的,PaaS的部分只是顺带做了一下,支持的并不完善;相较之下WallProxy则要好得多。二是WallProxy在浏览器代理端支持使用用户名密码进行验证,虽然只是一个防君子不防小人的验证但毕竟总比把代理端口直接暴露在广域网上要好一些。



ip =
port = 8086
username = xxx
password = xxx


enable = 1
password = 123456
;listen = 8088
fetchserver =

[paas]这一节设置成enable, 密码设置成和之前php里一致即可,listen注释掉,我们在之前[listen]一节里已经设置了监听端口,这里的listen端口就不需要了;fetchserver设置好php的地址,再把剩下的所有配置节中,包含enable的全部设置成0,也就是disable掉所有其它的feature,我们的VPS端也就配置好了。只要用



Screen Shot 2015-02-28 at 17.55.39


回到正题。搭建完代理服务器只是第一步,第二步是配置浏览器让它能够Unblock Everything。简单粗暴的做法就是把整个浏览器的代理直接设置成这个,当然我们有更优雅的做法,比方说利用rule来减少代理流量,加快速度(毕竟代理绕了3圈)。我用的插件是ProxySwitchy,当然现在已经改名叫SwitchyOmega,在Auto Switch中添加几条rule即可。我一般常用的就是网易云音乐和优酷,URL分别是**

Unblock Youku的Pac文件里有更多的规则可以参考,我因为一般需要的就是这两个,所以就配置了这两个Rule,其它的Rule用到了再配。不常用的Rule也可以到时候手动切换走全局代理,也不是什么大问题。