HTML5 移动应用开发

February 01, 2016

本文分为两部分:第一部分是一次小演讲的PPT,介绍HTML5 APP开发。第二部分是我之前做的基于APICloud平台的两个APP部署及开发案例(思路)。

一、一次演讲

时间:2015年12月28日 地点:西南大学图书馆研修室

这是第11期西大IT技术沙龙的一次演讲,这个技术沙龙刚起步,没多少人来听但会坚持下去成为常规活动进行,采用二进制技术,第11期也就是第三期。

本次技术沙龙由于另两位同学都有一些APP开发经验,我实际上没有开发过APP,只是接触过HTML5的混合开发模式,所以我抛砖引玉,先讲了一下这一块。

现在的HTML5开发主要有两种形式:一种是在APP表层嵌入一个webview,这个webview实际上是访问了一个html页面,这种方式有的需要联网发起http请求,也有在本地可以访问的html。通过这种形式,用户看到的界面不是原生模块,动画效果一般不如原生模块的效果流畅,目前一般只能实现简单的点击、侧滑等等一些基础动画,流畅度可能比较高,但是如果开发的是游戏等复杂度较高的动画的时候,动画效果相对于原生来说会更不流畅。

另一种形式是交叉编译,把javascript代码编译成JAVA/OC语言的代码,这个编译过程一般来说用户看不到。本文第二部分的APICloud平台实际上是在云端编译,编译后的代码实际上是原生应用。这样开发出来的效果是和原生应用一样流畅的。

这张纵向对比图展示了原生应用、混合开发应用与Web APP的区别和联系。后来王宇学长说,现在的开发一般都是原生开发融入进Hybrid的模块,只是融入多少的问题,目前这一块有其显著的优势,就是开发速度快,非常节约开发成本,尤其在做一些展示内容及“一次性”内容(即使用周期短的内容,如很多软件的推送页,这样的页面一般使用周期只有一次,不会涉及复杂的交互)的时候非常合适,而且基本不会影响用户体验。所以原生开发与混合开发的界限不是很清晰,严格意义上来说只有完全不使用Hybrid模块的APP才可以说是原生应用。

我们分为五块来讲:Web APP(Web应用)、Hybrid APP(混合应用)、Native APP(原生应用)、Json数据格式介绍以及一些题外话。

根据上面的展示页面可以看到Web应用的优缺点,其实Web APP就是一个Web页面,通过浏览器就可以访问得到,也可以打包成手机本地的APP,但是访问此APP的时候必须要联网,这两种访问方式都可以叫Web APP。浏览器的Web页面一般我们叫“单页应用”【知乎问题:单页应用有哪些优缺点?】

那么Web APP和正常的Web页面有什么区别?或者说什么样的Web页面可以叫单页应用?这个界限又是很模糊,个人认为一般来说能实现与服务器进行数据交互的Web页面都可以叫Web应用,由于Web页面的特性,一般不会有很多菜单、二级甚至三级页面,所以这样的Web应用一般是广义上的单页即可完成,所有的服务器绑定的数据都展示在这个单页(不要拘泥于它是不是真的只有一页 = =)。

将一个Web APP由浏览器访问的页面打包成本地可以打开的APP,这种操作有很多的实现形式,不难,在这里不做详细介绍,后面的APICloud会提到其中的一种很简单的方式。

这里介绍的三种工具我只使用过后两种,两个都比较容易上手,相对来说APICloud社区更完善一些,所以我使用了这个。

这里是说Web APP与Native APP的一个很重要的区别:

通过浏览器访问的Web APP上下各要有其他信息栏,上面是浏览器的标签栏,可能也要有地址栏,下面是APP(浏览器)的导航菜单,其下面可能还有手机自带的虚拟导航菜单,因此真正的应用被挤得只剩中间的一小块。其UI美化、拖拽动画等都不如原生的漂亮和流畅,体验要差很多。

APICloud提供了很多的页面框架,在新建文件的时候可以选择几种基本的页面结构,建立好项目后随时可以使用页面框架,这里选择的页面框架有二十多种,种类很多,基本我能想到的APP的界面布局均涵盖于其中。

APICloud项目的每个页面文件都是html文件,这里的结构和正常的html网页文件的结构是完全一样的,也就是说使用WEB浏览器也可以打开,但是由于一些javascript代码是仅使用APICloud才可以编译成的APP代码,因此这些javascript代码对于WEB浏览器来说是无意义的,但是对于APP UI界面的调试,这一点是很方便的,有时候不必使用平台自带的APP调试工具,一般这些平台自带的工具或者手机系统虚拟机都很慢。

同样因为都是html文件,对于WEB开发者转APP开发者是非常简便的,如果接触过Bootstrap之类的响应式框架,则可以立即上手APP UI布局。

对于javascript代码,插入方式也很简单。APICloud官方提供了很多很多模块,大部分是免费的,当然也可以自己使用javascript代码或者原生代码编写自己的模块。官方提供的模块都包裹在apiready函数(也可以说是对象【javascript里函数也是一种对象】)里,使用的时候只需在javascript文件里写入apiready = function(){…}即可。

apicloud模块一般都是键值对的形式,使用简便(键值对都有实例代码,只需修改自己所需的值)

下面介绍的是原生开发。

把React Native(RN)归为原生开发,它本来就是原生开发,正如上一张图上的文字所说,它使用了“虚拟DOM”生成原生UI,所以它的UI不是webview,而是原生UI。在React Native的原生应用里是没有html和css代码的。了解React的同学会知道,React作为Web开发框架(View层)是使用javascript代码生成渲染的Web UI,渲染的一个个虚拟DOM节点。

因此RN的开发难度相对于APICloud这样的混合开发模式相对来说难上手一点,但是其使用的是javascript(ECMAScript6)的代码,官方宣传它是“一次学习,到处编写”,而混合开发的理念是“一次编译,处处运行”。他们是有本质的区别的,混合开发可以编写一次javascript代码和html文件,同时生产安卓苹果甚至web端的应用,而RN编写一次javascript代码只能实现一种设备。

其实并不是那么夸张,对于很多组件Android与ios是通用的,有一些组件对于不同手机系统的名称不同,因此对于简单的Native APP做一些修改即可在另一平台运行,但是绝不可能生存WEB页面,因为RN的组件只能编译成移动设备的java或oc代码,并不会编译成html文件。

这里介绍了React组件的生命周期,有兴趣的同学可以学一下React的入门课程,它把HTML页面当做一个个组件来渲染虚拟DOM节点,在浏览器审查页面元素是看不到数据的,这对于大量数据绑定的页面来说相对提高了其安全性,但同时降低了SEO搜索的排名,因此慎用,一般是大框架使用传统html结构,只在特定位置使用React组件,React组件对于大量数据更新的表格应用有很好的表现。

这是我使用RN写的Hello World程序,环境配置了两天,虚拟机运行出Hello World的时候心里高兴坏了!可以看到左下角的javascript代码是使用js对象设置的样式,而非css代码!

右上角的javascript代码就是React渲染的一个组件“MovieTalk”,可以看到这里的DOM节点是View和Text,这两个DOM并非HTML里使用的节点。

二、两个案例

为了完成这学期的人机界面学课程最后的实验,我开发了两个Demo,一个是“天地汇”新闻推送软件,另一个是一个拼图游戏。前者是自己做的练习,准备给好朋友的公司做个推广产品,后者是帮小马哥做的实验课作业。

做完后我对后者更满意,前者的新闻推送并没有做完,只使用了Ajax的Get请求数据,并没有实现数据的上传,由于我不会编写后台脚本代码(不会后台脚本语言如php、jsp、asp那些,想过用nodejs写,不过也是没学深入,短时间内没做出来),如果实现了上传数据功能, 我就可以实现基本的APP管理后台,以一个Web站点的形式供用户使用,提交数据到web服务器上,然后在app内只需下拉刷新或重新进入软件即可访问到新添加的数据。

而后者的拼图游戏则可以实现游戏的功能,至少所有按钮均可点击并有实际意义,它不需要联网。

1.“天地汇”新闻推送软件

扫描可以下载软件的试用版:

界面如上,还算比较美观,借鉴了主流的新闻类应用的布局形式、展示图片的高宽比例、图文展示的字体大小及其比例。

这里使用了js代码调用的ios原生控件,可控制顶栏字体颜色为白色:

api.setStatusBarStyle({
style: ‘light’
});

在config.xml文件里可以设置其为“沉浸式”应用,即在应用里可以看到顶栏,并且顶栏和应用底色相同,这样可以感觉应用是属于ios系统的一部分,有强烈的归属感。

这个APP整体框架使用了APICloud官方的openFrameGroup对象,这个应该是IOS系统的原生框架,我自己的index.html文件里html部分只有一个header,下面的页面跳转、打开及关闭都非常流畅,应该都归功于下面这个openFrameGroup对象,这样APP的页面跳转动画和ios系统的其他本地页面跳转形式是完全一样的,流畅度当然也一样,使用起来也非常简单:

api.openFrameGroup({
name:'home_group',
background:'rgb(255,255,255)',
scrollEnabled:true,
rect:{
x:0,
y:offset.h,
w:'auto',
h:'auto'
},
index:0,
preload:0,
frames:[{
name:'frame_index_home',
url:'./html/frame_index_home.html',
pageParam:{},
bgColor:'rgb(255,255,255)',
bounces:false
},{
name:'frame_index_generalize',
url:'./html/frame_index_generalize.html',
pageParam:{},
bgColor:'rgb(255,255,255)',
bounces:false
},{
name:'frame_index_partTimeJob',
url:'./html/frame_index_partTimeJob.html',
pageParam:{},
bgColor:'rgb(255,255,255)',
bounces:false
},{
name:'frame_index_getJob',
url:'./html/frame_index_getJob.html',
pageParam:{},
bgColor:'rgb(255,255,255)',
bounces:false
},{
name:'frame_index_publicBenifit',
url:'./html/frame_index_publicBenifit.html',
pageParam:{},
bgColor:'rgb(255,255,255)',
bounces:false
}]
}

这样设置后我只需要分别写这几个Frame页面的UI就可以了。

在frameindexhome.html文件里我使用了apicloud内置的api.ajax对象,实现了跨域访问web服务器的json数据:

apiready = function () {
api.ajax({
url: 'http://www.dwbbb.com/test.json',
method: 'get',
timeout: 30,
dataType: 'json',
returnAll: false,

},function( ret, err ){
if( ret ){
var title = JSON.stringify( ret.generalize[0].title);
$('.ioio').html(title);
}else{
alert( JSON.stringify( err) );
}
});

这里是把我自己写的一个简单的test.json文件上传到了阿里云虚拟主机的目录中,通过ajax跨域访问可以读取json文件中的字符串,通过js代码解析后提取指定json对象的属性值,并把该数据输出到class名为’.ioio’的html标签中。

现在只能做到这步。get数据的话需要服务器后台脚本程序,茂茂给我写了一份php代码,但是我还没看懂怎么用= =。

2.拼图游戏

安卓用户扫描下载正式版: iOS用户扫描下载试用版:

由于我没有ios的开发者证书(100刀一年,平时不用没必要买)所以只能编译出安卓的正式版,现在下载的苹果应该过了7天有效期不能用了。在下面贴一些截图吧:

可以看到这个游戏的界面还是比较漂亮的,毕竟我会ps = =。

由于当时赶时间,所以这个游戏是纯纯的HTML5的Hybrid APP,完全是html、css、javascript代码,没有调用原生模块。这样的后果就是页面根本没有跳转,点击后页面是直接过去的,没有渐变或者滑动的动画,但是整个软件在人机交互的完整性来说是相对比较完整的。

整个游戏通过两次点击可以交换两个图片的顺序,在拼图的时候系统自动统计用时和操作步数两个参数,再加上查看提示的次数可以生成最后的得分,得分算法如下:

var score = Math.round(44000/$('.finalTime').html() + 48000/$('.stepTime').html() - 300*tipTime);

整个游戏我改了一个网上的拼图例子,不过是大改,基本原来的方式改的面目全非,网上下载下来的是用鼠标点击拖动交换两个图片的位置,而我改成了鼠标点击交换图片位置,因为鼠标点击拖动的形式在手机上无法拖动,鼠标拖动它原来使用的是jqueryui的拖动事件,但这个函数在手机上无法使用(因为手机不应该有onmouseover事件,而应该是按住后选中之类的事件)。

鼠标点击是我自己构思的思路:

把两次点击的图片的data-value属性的值和style属性的值分别赋值给第三个值然后交换。

代码如下:

myClick: function() {
$('li').click(function() {
$(this).addClass('frontImg')
var front = $(this).attr("data-value");
var frontStyle = $(this).attr("style");

$('li').click(function() {
$(this).removeClass('frontImg');
$(this).addClass('afterImg');
var after = $(this).attr("data-value");
var afterStyle = $(this).attr("style");

var tem = front;
front = after;
after = tem;

tem = frontStyle;
frontStyle = afterStyle;
afterStyle = tem;

$(this).attr("data-value", after)
$(this).attr("style", afterStyle)
$('.frontImg').attr("data-value", front)
$('.frontImg').attr("style", frontStyle)

$('li').removeClass('frontImg')
$('li').removeClass('afterImg')
$('li').unbind("click");

currentList = $('li').map(function(i, el) {
return $(el).attr('data-value');
});

sumStep++;
$('.stepTime').html(sumStep);

if (isSorted(currentList)) {
$('.finalTime').html($('.usedTime').html());
var score = Math.round(44000/$('.finalTime').html() + 48000/$('.stepTime').html() - 300*tipTime);
$('.score').html(score)
$('#actualImageBox').empty().html($('#gameOver').html());
alert('恭喜!完成拼图!');
clearTimeout(t);
}

imagePuzzle.myClick();
})
})
}

整体游戏的思路是:

图片是完整的图片,使用css的position属性可以把图片分区域显示,通过ul_li标签可以构建一个4*4的表格,在这16个表格里分别显示一块图形,并且显示顺序是随机的,这16个li标签及其data-value和style的属性都是通过javascript代码生成。(既然可以生成就可以赋值给我自己定义的变量并进行数值操作),另一个函数可以执行鼠标点击操作,交换两次点击图片的data-value值和style值,交换data-value是为了比较图片顺序是否是正确的顺序,而style值是将两个图片样式交换,在用户看来是交换了图片,在后台来看是交换了li的data-value值。

最后比较16个li的data-value值是否是正常的顺序,正确则结束,得出分数。

这个游戏目前存在一些缺陷:

1.游戏逻辑不够好,一般这样的拼图游戏是相邻的两个图形交换,或者做成华容道那样有一个位置是空缺的,然后只有空缺的相邻位置可以被点击并移动。

2.游戏没有实现排行榜,不过这个应该不难,可以使用一个本地json文件存储数据,接下来我会实现。

3.后台js代码使用的是鼠标的click()事件,这个事件在web端是瞬间完成的,不会有延时感,但是手机在处理这个事件的时候会自动解释为“延时300ms才确认点击事件”,这就造成了移动端进行游戏的时候手要慢点点击,最少300ms间隔。(后来查到了,延时300ms是为了防止移动端的click误操作,比如避免本来是人们想进行滑动事件结果被解释为点击事件的现象。)这样的延时是有必要的,但是可以通过代码避免。

两个APP都较多地使用了jquery的css()方法来进行一些按钮或图标的定位:

$('#btns').css({
    "position": "absolute",
    "top": "25%",
    "width": "100%"
})
$('#start').css({
    "background": "url(images/start.png)",
    "background-size": "100% auto",
    "width": "35%",
    "height": btnHeight-1,
    "position": "absolute",
    "left": "13%"
})

这样的定位更精确,本来已经导入jquery包了,不用白不用,单凭css定位比较麻烦而且不准确,兼容性不好。

最后总结一下(个人观点):

不要忽视弱技术,技术上遵循二八定律,20%的产品使用高技术,80%低技术,低技术同样拥有广阔的市场和同样高的利润空间,在进行软件开发的时候不要好高骛远,一味追求程序的完美而忽略开发周期和开发难度。现在的APP开发也是要将好钢用在刀刃上,把逻辑简单、使用周期短的部分写成webview,不要忽视HTML5的跨平台性。

为什么javascript语言会成为目前最火的程序语言,并且是唯一一个全栈开发语言?(javascript可以做web前端脚本,Flash脚本,javascript web MVC框架,nodejs服务器脚本,开发移动应用和桌面客户端应用)因为它简单易学,最开始javascript是为非程序员服务的编程语言,在互联网WEB开发的十年中,互联网行业产生了数量相当可观的javascript程序员,现在它的标准和发展也是进步很快的,ECMAScript6 开始一年一迭代。

看到Python的火热又可见二斑:以后的程序设计语言将越来越脚本化、简单化,甚至通过交叉编译的形式使用一种脚本语言来实现复杂的另一语言的逻辑。程序不可能过于平民化,毕竟其难度比较高的逻辑能力不适用于使用自然语言实现,因此编程脚本化将是大趋势,符合快速发展的编程需求。