Apr 27

html5 webview的本地存储优化

最近接了一个需求,是一个内嵌在webview里的类似于照片墙的页面。可以想见,这样的一个页面在3g甚至是2g的网络环境下,访问速度该面临多大的挑战。利用一些前端优化的技术,最近做了一些尝试,在这里小结一下。

先看一下这样一个页面的组成。一些js,css,若干张图片,还有一个初始化html请求。

首先来说主干流程。为了让用户及早看到内容,我们这样安排页面的加载:

  1. 首先展示上一次看过的图片内容;
  2. 此时,发送ajax请求到服务器,获取最新的图片url们;
  3. 在ajax请求完毕时,加载新的图片。

好,上面是最基本的思路。首先,我们需要让第一个请求,也就是html请求尽快完成,最快的方式也许莫过于保存在本地了。这里,可以使用html5的cache manifest。

<html manifest=’/pic_wall.manifest’>

manifest内部可以这样写:

CACHE MANIFEST
#version 1
/pic_wall.php
/img/web_view/iphone/loading.jpg
/js/jq.mobi.min.js
/js/wv_common.js
/js/backbone_json2_underscore_min.js
/js/pic_wall.js

NETWORK:
*

唯一需要注意的是version这一行,虽然是注释,但是可以用来指定manifest的版本。假设我们修改了其中一个js,需要告诉客户端及时更新,就可以把version改为2。

这样,在一般情况下,使用chrome,safari之类的浏览器就可以测试成功了。但是在iphone 5.1上使用safari测试时,会有一个错误提示,说pic_wall.manifest在下载时的mimetype不正确,是text/plain。这就要修改http服务器的mime type配置了,把manifest后缀的请求设置为text/cache-manifest类型就可以了。

好,现在我们把目光转向js和css。这俩家伙虽然已经列在了manifest文件中,但是当我们点击浏览器的刷新按钮时,他们仍然会向服务器发起一次请求,得到一个304后才安心走到下一步。这个现象根据浏览器的实现各有不同,尤其是在android webview下不太可靠。在移动网络环境下,这些304请求也是相当费时的——每次网络连接都会花掉不少时间。怎么消除这些304呢,这个比较简单,以apache为例,使用一个mod_expires模块,给js和css类型的请求加上etag和max-age就可以了。另外,为了让第一次下载快一点,还可以设置apache使用gzip encoding来下发。

好了,页面上看得见的就只剩下图片没说了。在这种应用中,图片有时候是动态生成的,比如根据需要裁切的,这就要在图片服务器端加上一些缓存header,比如etag和max-age,来达到浏览器端缓存的目的。

现在,所有的内容都可以缓存在浏览器了,但是,图片不应该是写死在页面上的,它们应当是由页面从服务器请求下来src、然后拼装放在dom里的。这个规定图片墙组织结构的请求,我们用一个ajax来完成。ajax请求从服务器端下载到一个描述着图片墙内容的json,然后解析成图片的排版结构和图片src,并拼成dom放到页面上。

好,到此为止,页面已经可以完成流程了。但是,假设我们的图片墙内容是飞速变化的(比如这是一个海量用户正在上传的图片的展示墙),怎么样在用户下一次刷新页面时,让他有个飞速的体验呢?

首先,要让他在刷新或者重新打开页面时,有基本的内容可以先看着,俗话说骑驴找马嘛。我们已经把html请求、js、css缓存在本地了,可是上一次看过的图片墙结构——那个json字符串没有。这里,我们可以使用html5 的localStorage来保存它:在下载之后,保存到localStorage里;js加载完后,从localStorage读取它并展示出来。用户就可以看到上一次看过的图片墙了。此时,再发起ajax请求,更新照片墙。

好,到此位置,在电脑的非ie浏览器上,已经可以完成我们的想法了;iphone上也可以;iphone的webview上也可以;但是,唯独android webview上还不行……

android webview需要做如下设置:

        wv.getSettings().setDomStorageEnabled(true);//设置可以使用localStorage
        wv.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);//默认使用缓存
        wv.getSettings().setAppCacheMaxSize(8*1024*1024);//缓存最多可以有8M
        wv.getSettings().setAllowFileAccess(true);//可以读取文件缓存(manifest生效)
        wv.getSettings().setAppCacheEnabled(true);//应用可以有缓存
这样,一般情况下,android webview也没问题了。但是我们遇到一种情况,就是app本身比较大,占用比较多的内存,此时即便有上述设置,在打开上面的图片墙页面时,也会不正常。我看到的现象是:页面可以加载出来,但是突然会显示手机桌面,1秒钟后又自动切回到app打开图片墙之前的界面。怀疑是内存限制问题,还没有找到确切答案。
额……没错,我们还没说很重要的一点,就是第一次加载。在加载时,我们尽量让请求数少一点,可以把js和css压缩后铺到html里,反正html的那个请求以后会有缓存了。再之后就是图片们;因为我们已经请求下来描述图片信息的json数据了,此时可以先把图片墙的轮廓画出来,然后把真实图片的位置用默认的小图标占上;等到一张图片加载完成时,再替换掉默认小图标,做得炫一点的话,可以模仿flipboard或者zaker那样的翻板效果。
好了,大致就是这些,也许还有遗忘的忘了总结,下次再做的时候再补充吧。欢迎QQ一起讨论、拍砖:1993885292,如蒙不弃,也欢迎转载,别忘了标明出处:http://hulucat.com。
Mar 25

HTML加载提速思路

坊间对于HTML的看法可谓一片大好。每个人都对它的通用性和简便津津乐道。提到它的缺点,都专注在浏览器能力和兼容性上,但很少人会提及它明显而致命的另一个缺陷:加载速度慢,尤其是在3G或者2.5G环境下。

对这个问题,最近听了几位先行者的分享,整理一下思路大致如下:
第一种,在使用APP加webview这种结构的情况下,深度整合二者。联网方面,APP除了自身的功能外,兼具着定制化浏览器的功能,页面联网时,实际上是通过APP的socket与服务器进行沟通,这样,长联接、更精简的请求协议都成为可能。内容方面,APP内置页面需要的js、css和html模板,打开页面时第一步实际上是在客户端本地进行的,之后js再活跃起来操作页面逻辑。页面模板资源变化时,可以根据设计好的逻辑下载更新。
在这种环境下,页面开发人员好比是在温室大棚里成长,冷风和雨夹雪都被保护壳挡在外面了。
可能大多数人都享受不到上面第一种环境,我们面对的是第二种。APP只做好自己的事,需要页面干活时,就调用内嵌的系统浏览器模块,任由页面自生自灭。况且,在纯html5的移动互联网结构下还根本没有本地APP存在呢。这时,优化就只有尝试把HTML5压榨到极致。可以尝试bigpipe的思路,先下载结构性页面内容和基本的js、CSS,然后通过Ajax加载逻辑内容。页面使用本地存储适当保留部分内容,这样下次加载就可以直接用了,甚至直接加载。但是浏览器端本地存储的大小和不可预期性值得考量。如果一个页面够复杂,可以试试websocket,以更简单的形式直接和服务器通信。
先试试第二种,作出效果来,再想办法演进第一种。或者,也许很快就有必要考虑纯粹的脱离APP的页面应用了。
Mar 07

2012年3月7日

用backbone.js写的微笑网新版订单认领小交互终于有了雏形,希望能尽快完善、测试、上线。最近照猫画虎学backbone.js,虽然很累,但是也非常有喜悦感。这个前端技术百家争鸣的时代,让人越来越没有安全感。希望下周把写代码的过程整理一下,写一个笔记发出来。希望到时候没有改变想法:)。

我今天发现,每年的这个时节,我都特别容易悲观甚至颓废。表现在,感觉要做的事情太多,精力太分散,想要抛弃一些感觉不太重要的部分。也许是因为惊蛰?睡虫刚醒来也许就是这样吧。

这个季度,正常工作之外的主要精力要用来学习前端技术。我是个绝对的单进程、无线程的人,开车的时候只要和二丫说话,就立马表现出要追尾的趋势。

周末打算去爬个山,锻炼锻炼;下周和部门去摘草莓,下下周就更暖和了,惊蛰了,美好的春天来到了!

Feb 28

Memcache的单条大小限制

今天发现一处memcache缓存没有存住,测试了一下,认定是这条缓存大小超长了。写代码测试,大小应该是限制在1M,从文档中查了一下,也是这个说法:slab中item的大小默认是1M。

解决这个问题的方法大概有三种:

  1. 修改memcache限制,使用-I(大写的i)启动。但是暂未找到不重启就实时修改的方法。
  2. 修改代码,减少放到缓存中的冗余文本,因为是旧代码,比较麻烦。
  3. 在存入缓存时,使用压缩选项。经过测试,这应该是最取巧的方法。缺点是掩盖了潜在问题(冗余内容多),浪费一些cpu,额外的好处可能是节省网络请求了。