最近做一个项目,是将一个相对复杂(内容后台模块化配置)的mobile web页面嵌入到Android的webview展示,把遇到的问题和一些经验总结下
(1)图片!图片!图片!
我觉得不管是原生App还是Web App,加载优化的第一条就是合理的设置图片,这点往往容易被忽视。一切只在WIFI环境下的测试都是耍流氓!
这个项目的主页面,一开始前端负责切图的同事给出的静态页居然有1M多,其中最大的一张banner图接近300K! 直接从PSD切出来的高保真原汁原味的展示效果确实震撼,百分比布局下,在chrome放到全屏显示还是清晰无比。理想很丰满,现实却骨感,可惜我们不是生活在Provo,没有google fiber的情况下只能忍痛牺牲这种“网络不能承受之美”。wap页面就是手机上看的,一般4~5寸屏幕能清晰显示,6寸‘巨屏’牺牲点效果不影响使用就足够了。
目前总结大致的图片组成
- 横铺图片,大概占全屏的1/5~1/4左右的图片,建议30K左右
- 橱窗图,宽度1/4~1/2方图,8~15K
- 加载占位图、loading动画 单色,质量调低,1.5K
- 多个小图片,最好合成一张用css sprite布局,webview里的http请求很慢,能省则省
- 什么时代了,一般的渐变 圆角样式能用css3就一定不能老土再用图片了!
- 一些小图,可以base64成字符串,用css data:image保存(这个持保留意见,不直观,而且增加了css文件的体积,这种字串一般gzip压缩也不会变小多少)
(2)使用zepto.js代替jquery
或许你是javascript大牛建议一切用原生,但是简单的选择器和DOM操作肯定没有问题,何况手机上不用可以把大量IE兼容的代码直接忽略(暗爽)。但是真正做webapp,稍微复杂点还是需要使用一些插件,每个功能都用野生js重写,难度和稳健性先不说,代码也会越来越臃肿难以维护。(野生Javascript怎么也称不上优雅)
那么为什么强烈建议用zepto.js代替jquery呢,这可绝不仅仅因为gzip后差别20K的文件体积,而是因为Android Webview奇葩的js解析效率和更奇葩的onPageFinished事件,总之一旦用了jquery,页面的白屏loading肯定会多滚很多圈,宝贵的加载时间浪费在一个个用不到的函数对象的建立和兼容判断语句里了。
而用zepto.js可以有明显的改善,而且基本的选择器、DOM操作、ajax,写起来和jquery是完全一样的,无痛迁移,个别插件不兼容,往往也只需要把最后闭包外的(jQuery)改成($,window,document)就可以了。常用的插件一般也可以在github上找到zepto.js compatible version
(3)先载入DOM,延时加载和执行js
奇怪,这不就是$(document).ready和window.onload的却别么?糊弄谁呢
但确实不是这么简单,主要原因就在于Android Webview的onPageFinished事件,Android端一般是用这个事件来标识页面加载完成并显示的,也就是说在此之前,会一直loading,但是
Android的OnPageFinished事件会在Javascript脚本执行完成之后才会触发。如果在页面中使用JQuery,会在处理完DOM对象,执行完$(document).ready(function() {});事件自会后才会渲染并显示页面。(参见 http://hi.baidu.com/goldchocobo/item/9f7b0639f3cd2efe96f88dfb)这篇文章。文中使用的lazyload.js已经有了版本更新,语法也发生了变化,这样用即可
<script src= "js/lazyload.min.js" ></script>
<script> function loadComplete(){
//do something
} //针对Android webview渲染js慢的问题,延时加载 function loadscript(){
LazyLoad.js([
'js/zepto.min.js' ,
'js/jquery.lazyload.min.js' ,
'js/mustache.js' ,
'js/flowtype.js'
], loadComplete);
} setTimeout(loadscript,10); </script> |
这里的关键就是setTimeout(loadscript,10),这个语句就是Webview里页面加载显示和载入和执行其它js和页面渲染事件的分水岭。把原来放在$(document).ready里面的主体程序放在loadComplete里面就行了。
经过测试,这个对包含复杂js的页面在webview中加载的提升最明显,如果你的页面一直在傻乎乎的loading loading loading.. 最好试一下这个办法。
不过我们的主体页面初始什么内容都没有,所有DOM都需要mustache根据api的配置,从模板中render,所以Android交了兵权之后还要在页面上空白或者显示自定义的loading图一小会,不过绝对比之前那种体验要明显快的多(大概15秒=>5秒的样子)。
(4)图片懒加载
原因还是因为不在Provo,注意此lazyload非彼lazyload,这里是jquery.lazyload,小改动就可以支持zepto.js
这个插件很常见,最好还是去github主页https://github.com/tuupola/jquery_lazyload/看用法,手机上调用的时候最好加上 threshold:300,否则滚动,由占位图加载的等待时间还是有点明显。
如果滚动加载失效(找不到原因),可以试试在lazyload之后加一条
$(window).trigger( "scroll" );
|
就可以了。另外lazyload占位图虽然小,但是最好能提前加载到缓存,这样页面显示的时候高度不会突变,把不同宽高比的占位图放在<body>不显示即可
< img src = "upload/images/other/load_full.jpg" style = "display:none;" />
< img src = "upload/images/other/load_half.jpg" style = "display:none;" />
|
(5)使用LocalStorage缓存DOM
如果你的页面主体和我们这次一样,初始的DOM只有一个loading甚至空白,所有的内容都需要api获取接口数据,然后根据模板(比如mustache.js)render之后在append到DOM里的话,那么不管怎么优化,每次还都是需要等待那么一会儿,api请求接收和js模板引擎的处理在webview上都明显的慢。
而有些页面虽然需要后台配置,但并不是那么动态,像一个商城的首页这种,即使前端显示更新不那么即时,也不是很大的问题,刷新或者下次进入再显示最新版本也可以接受甚至是更好的用户体验。
我们这里把第一次mustache render好的html块,存入LocalStorage,然后下次进入页面的时候,先直接从LocalStorage中读取并显示,api读取和模板渲染后的新DOM再更新到LocaStorage中(如果有必要,可以在这个时候,比较下新旧是否相同,不同再更新一次DOM)
function jq_lazyload(){
$( "div#page_all img.lazy" ).lazyload({threshold:300, load : function (e){$( this ).next( 'b' ).hide();$( this ).removeClass( 'lazy' );}});
$(window).trigger( "scroll" );
} function loadComplete(){
//omit ...
//如果用localstorage则先lazyload img
if (window.localStorage){
if (localStorage.getItem( 'dom_all' )){
jq_lazyload();
}
}
$.ajax({
url:server_url,
dataType: "json" ,
type: "GET" ,
success: function (json){
var dom_all= "" ;
for ( var i=0; i<json.floors.length; i++){
var style_this = json.floors[i].style;
dom_all+=Mustache.render($( '#floor_tpl_' +style_this).html(), json.floors[i]);
}
if (!window.localStorage || !localStorage.getItem( 'dom_all' )){
document.getElementById( "page_all" ).innerHTML = dom_all;
jq_lazyload();
}
localStorage.setItem( 'dom_all' ,encodeURIComponent(dom_all));
dom_all= null ; //释放内存
}
});
} function loadscript(){
if (window.localStorage){
if (localStorage.getItem( 'dom_all' )){
document.getElementById( "page_all" ).innerHTML = decodeURIComponent(localStorage.getItem( 'dom_all' ));
}
}
LazyLoad.js([
'js/zepto.min.js' ,
'js/jquery.lazyload.min.js' ,
'js/mustache.js' ,
'js/flowtype.js'
], loadComplete);
} setTimeout(loadscript,10); //处理Webview未lazyload完,进入其它页面,js中止,返回不执行 window.ontouchstart = function (e){
jq_lazyload();
} |
(6)Webview的设置
webview本身的设置也很重要,特别是cache和localStorage是否开始,是否app退出再进入就不存在了,各自空间有多大,这些需要和Android开发的同事沟通好,说不定就是一行参数设置,体验就大不同
- Cache开启和设置
//下面3个是跟浏览器缓存Cache相关的,一个页面的 图片\js\css 载入过之后
//在服务器设置的文件有效期内,每次请求,会去服务器检查文件最后修改时间,如果一致,不会重新下载,而是使用缓存
browser.getSettings().setAppCacheEnabled( true );
browser.getSettings().setAppCachePath( "/data/data/[com.packagename]/cache" );
browser.getSettings().setAppCacheMaxSize( 5 * 1024 * 1024 ); // 5MB
|
- LocalStorage相关设置
//下面是跟浏览的LocalStorage有关的,像首页的DOM,第一次载入,需要从服务器ajax请求接口json配置数据,然后用js从模板中渲染拼接成DOM,显示在页面中
//由于Android webview的JS处理很慢,这里把第一次渲染后的DOM存入LocalStorage中,以后打开页面不用请求API和JS渲染,优先加载页面,和Cache配置,速度会快很多
//但是Android webview的LocalStorage有个问题,关闭APP或者重启后,就清楚了,所以需要下面browser.getSettings().setDatabase相关的操作,把LocalStoarge存到DB中
browser.getSettings().setDatabaseEnabled( true );
browser.getSettings().setDomStorageEnabled( true );
String databasePath = browser.getContext().getDir( "databases" , Context.MODE_PRIVATE).getPath();
browser.getSettings().setDatabasePath(databasePath); myWebView.setWebChromeClient( new WebChromeClient(){
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
{ quotaUpdater.updateQuota(estimatedSize * 2 );
} } |
- 浏览器自带缩放按钮取消显示
//这个是跟浏览器的页面缩放相关,不用显示浏览器的放大缩小按钮,这个一般在最下面出现,体验不好
browser.getSettings().setBuiltInZoomControls( false );
|
(7)服务器端设置 gzip etag Cache-Control
gzip就不说了,总之一定要开启html css js json的gzip压缩!!!
为了弄明白这个,非科班出身的我连着fiddler边调测边翻了小半本<计算机网络>的书,其实也还没完全弄明白。而且测试发现现在的浏览器特别是桌面的360(#Anti-360#)和一些国产手机浏览器,为了制造“极速”的假象,缓存处理很多地方都没有按照规范来,动不动就会过度缓存,导致页面不能及时更新。Android Webview的LOAD_CACHE_ELSE_NETWORK设置更是完全无视etag、expire time这些,强制使用缓存。
总之,这块还没完全弄明白,等后面彻底明白了再结合fiddler和apache总结下吧。给出我这边apache .htaccess相关配置
<IfModule mod_deflate.c> AddOutputFilter DEFLATE html xml php js css json </IfModule> <IfModule mod_headers.c> <FilesMatch "\\.(ico|jpe?g|bmp|png|gif|swf|css|js|json)$">
Header set Cache-Control "max-age=2692000, public"
</FilesMatch>
<FilesMatch "\\.(php|html)$">
Header set Cache-Control "max-age=60, private, must-revalidate"
</FilesMatch>
Header unset ETag
</IfModule> |
(8)以上都不是
其实Hybrid App的最佳实践,还是应该把所有的html css js和主要的图片资源离线存储在Android的asset文件夹下,然后由Android实现从服务器端到手机的这个www主文件夹的更新机制,这样才不用凡事从server端下载(很多人讨论webapp时只大谈特谈性能,其实一切需要加载的实现方式才是最大的“阻塞”)。这样也可以随心所欲的使用一些Sencha Touch或AngularJS+UI这样的中型和重型框架,可惜上面提到的文件更新机制没有建立,暂时还没有机会实践这种模式。这种想法的文章不多,参考http://developer.appcelerator.com/question/146564/update-apps-local-html-webviewed-files的reply部分
就到这里吧…
本文地址:http://awebird.com/blog/art/122/
相关推荐
混合应用(Hybrid App)开发是结合了Web应用和原生应用的优点,通过在应用中嵌入一个WebView(如Android的`WebView`),使得开发者可以使用Web技术进行界面开发,同时通过JavaScript与原生平台API交互,实现更深度...
下面我们将深入探讨基于Apicloud平台的Hybrid App开发Demo,以及其相关的JavaScript开发和混合移动开发知识点。 1. **Apicloud平台介绍**:Apicloud是一个一站式的移动应用开发平台,提供丰富的API接口,允许开发者...
在Hybrid App开发中,本地缓存技术是一个至关重要的环节,能够有效地提高App的性能和用户体验。本文对Hybrid App本地缓存技术进行了预研,并对其进行了一期总结,主要包括预期目标、任务安排与完成情况、性能对比...
在当前的移动开发领域,HybridApp已经成为了一种主流的选择,尤其对于那些希望在iOS和Android平台上快速构建功能丰富的应用的企业或开发者。Python作为一门强大的编程语言,虽然不是直接用于开发HybridApp,但可以...
5. **前端框架**:为了提升用户体验和开发效率,许多Hybrid应用会采用前端框架,如React Native、Ionic或Vue.js。虽然标题没有明确提及,但HybridAPPDemo170111可能使用了某种前端框架来组织和管理UI。 6. **打包与...
1. **Hybrid App概述**:混合式应用是结合了原生应用和Web应用特性的应用类型,它们通常由一个原生的“外壳”(壳层)和一个基于Web的用户界面组成。这种模式允许开发者利用Web技术快速开发,同时又能利用原生平台的...
HybridApp混合移动应用开发是一种结合原生移动应用与Web技术的开发方式,它利用JavaScript、HTML5(H5)和CSS3等Web技术构建应用的界面和逻辑,然后通过像Cordova这样的框架将这些Web内容嵌入到原生的移动应用容器...
HybridApp 一种可以下载的Native App,其用户界面的全部或者部分元素在嵌入式浏览器组件(WebView之类的)里面运行 优雅降级 一开始就构建站点的完整功能,然后针对浏览器测试和修复。认为应该针对那些最高级、最...
"Android+HTML5"的混合开发模式,也被称为Hybrid App开发,通常包括以下步骤: 1. 使用HTML、CSS和JavaScript编写前端界面,这些文件可以放在本地或者远程服务器上。 2. 在Android项目中创建一个Activity,该...
本篇主要讨论了Flyme Hybrid技术的原有架构、通用Hybrid App开发体系的建设和前端工程的优化策略。 1. Flyme Hybrid原有架构简析: 原有的Flyme Hybrid架构包括一个Webview组件,用于加载和渲染H5页面。通过在...
项目实现上,软件采用Android客户端开发,利用Layout布局设计界面,并结合阿里云ECS服务器进行混合开发(Hybrid app),前端使用JS、HTML和CSS构建,通过webView组件展示,图片加载则由imageLoader处理。 总体来看...
4. **Hybrid App开发**:结合Web技术和原生API,开发者可以构建混合应用,它们既有Web应用的开发速度和跨平台特性,又有原生应用的性能和访问硬件能力。 总结来说,使用HTML、CSS和JavaScript开发Android程序是一种...
综合以上,"app_noDilay"是一款采用Hybrid App开发模式、跨平台兼容、基于MUI和AmazeUI构建UI的移动应用。其背后涉及的技术栈广泛,涵盖了前端开发、原生平台接口调用、跨平台框架使用等多个层面,展现了移动应用...
2. **Web App (HTML5)**:Web App是基于Web技术构建的应用程序,它使用HTML5、CSS3和JavaScript等前端技术栈进行开发,并通过用户的移动设备浏览器来运行。这类应用的主要优点在于跨平台性,只需一个版本即可覆盖...
DCloud提供了一系列产品和解决方案来支持HybridApp的开发,包括HBuilder这一轻量级的前端开发IDE,以及HTML5+ Runtime这一核心运行环境。DCloud的解决方案还包括mui框架和HTML5+生态,这些技术一起为开发者提供了...
PhoneGap是一款广受欢迎的Hybrid App开发框架,由Adobe维护。它基于Apache Cordova构建,为开发者提供了一个简洁的方法来创建跨平台的移动应用。开发者可以利用HTML5、CSS3和JavaScript等Web技术编写应用代码,然后...
uniapp是由DCloud(即DCloud(北京)信息技术有限公司)推出的一款基于Vue.js的多端开发框架,它允许开发者编写一次代码,即可发布到iOS、Android、H5以及各种小程序等多个平台。uniapp利用HBuilderX进行开发,提供...
Web App是在浏览器中运行的应用,而Hybrid App则是结合了原生App和Web App特点的混合应用,通常使用WebView(如iOS的UIWebView或Android的WebView)来嵌入HTML5页面,同时可以调用设备的原生API,实现更丰富的功能,...
2. **JavaScript与原生应用的交互**:为了调用Android系统的相机,我们需要借助于Hybrid App开发技术,如Cordova、React Native或Ionic。这些框架允许JavaScript代码与原生平台进行通信。在Android中,我们通常会...