HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

mui产品概述

入门 mui

MUI为何诞生

  1. 性能和体验的差距,一直是mobile app开发者放弃HTML5的首要原因。
    浏览器天生的切页白屏、不忍直视的转页动画、浮动元素的抖动、无法流畅下拉刷新、侧滑抽屉卡顿等问题,这些都让HTML5开发者倍感挫败,尤其拿到Android低端机运行,摔手机的心都有;

  2. 另一方面,浏览器默认控件样式又少又丑,制作一个漂亮的控件非常麻烦,也有一些制作简单的ui框架但性能低下。

mui框架有效的解决了这些问题,这是一个可以方便开发出高性能App的框架,也是目前最接近原生App效果的框架。

其实我们原本是开放心态,大家随意使用自己喜欢的前端框架。但是其他移动App框架实在不给力:

  • 基于jq的jqmobile,性能低的无法忍受
  • 基于angular的ionic,都把pc端很重的东西引入到移动App中。angularjs本身设计是为了pc端网页的双向数据绑定,做个移动App干嘛用这么重的东西。
  • bootstrap这种响应式设计,性能在低端机不足,而且UI风格一看就是网页,不是App的感觉。
    于是为了方便广大开发者,我们制作了mui。

MUI的定位是:最接近原生体验的移动App的UI框架

基于mui的定位,产生了mui的几个特点,轻、小、只涉及UI、只为移动App而生、界面风格原生化。
所以请大家注意,mui有所为有所不为:

  1. mui不是jq,不封装dom操作
    与ui无关的mui不做,你愿意用jq或zepto就自己用,并不冲突。
    但我们并不建议在移动App里引入jq或zepto这些框架,原因如下:

    • 为了性能,层层封装的框架,尤其是遍历循环dom时,影响效率,尤其在低端Android手机上,我们费死劲了才把性能以毫秒为单位一点点提升,搞这个的dom框架进来就让很多努力又付诸东流。
    • 原生JS挺简单,为何需要jq?
      jq的成功当时是因为ie6、7、8、9、10、chrome、ff这些浏览器不兼容,让开发者崩溃,而且pc上浏览器性能好,跨平台兼容也不影响性能。但jq根本就不是为手机设计的。
      手机上只有webkit浏览器(忽略wp,反正mui不支持wp),根本就不需要jq这种封装框架来操作dom。
      而且HBuilder提供了代码块来简化开发,敲dg、dq,直接生成document.getElementById("")、document.querySelectorAll(""),非常快捷方便,而且执行性能非常高,而且没有浏览器兼容问题。
      发现很多开发者只会jq,反正想继续在App里使用jq没有问题。但也建议大家多学学js本身。
      mui与vue、react、angular也不是一个层面的东西,可以在一个工程里混合使用。但在大多数ui控件上,应该直接使用mui的写法,因为mui的绘制是最朴素的HTML绘制,不是经过js操作的绘制,这种方案的效率比经过js绘制的效率要高很多。只有必须经过js操作才能渲染的控件,比如ajax联网后填充的list,此时使用vue或react都可以。
  2. mui、HTML5+、5+Runtime的关系说明
    mui是一个前端框架,HTML5+是一套HTML5能力扩展规范,HTML5+ Runtime是实现HTML5+规范的强化浏览器引擎。
    有点类似于bootstrap、w3c和chrome os的关系。
    HTML5+规范隶属于http://www.html5plus.org,定义了HTML5规范中没有但开发者做App需要的扩展规范。
    DCloud的5+ Runtime完整的实现了HTML5+规范。同时5+ Runtime还实现了Native.js,一种通过js调用几十万原生API的技术。
    为了提升体验,mui势必会调用一些5+Rutime的增强能力,主要是plus.webview、plus.nativeobj和plus.nativeUI。
    但mui不是要替代HTML5Plus,以后也无计划替代把所有5+的api都包一层。
    mui是把一些常用的窗体操作封装了,但这种封装适应面也是有限的,遇到复杂窗体管理,还是要仔细了解plus的api。
    所以,

    • 有人抱怨mui的文档不全,其实是缺本文,本文终于说清楚mui做什么不做什么了。详细的mui文档要去下方提示的mui官网查看。
    • 有人抱怨mui api不全,其实是没去看plus的api。知原理、知如何封装,方能融汇贯通。
    • 有人抱怨Hello mui示例代码里写的mui的方法,为何文档里没有,是因为有些方法是内部工程师简化开发中的封装,未考虑通用设计,还不足以开放为标准api,所以文档里没介绍。
  3. mui有插件体系
    为了简化开发者的多端发布开发,mui在核心库之外,补充了一些插件,这些插件不一定是ui相关,也有业务相关。
    在Hello mui示例里下方的示例模板,基本都属于插件。这些插件的使用需要加载mui标准库之外的js等资源。

mui是一个开源项目,请前往托管在github的mui官网查看详细介绍

这里是mui发布时的演讲视频:http://v.youku.com/v_show/id_XNzYyOTEyMjcy.html

继续阅读 »

MUI为何诞生

  1. 性能和体验的差距,一直是mobile app开发者放弃HTML5的首要原因。
    浏览器天生的切页白屏、不忍直视的转页动画、浮动元素的抖动、无法流畅下拉刷新、侧滑抽屉卡顿等问题,这些都让HTML5开发者倍感挫败,尤其拿到Android低端机运行,摔手机的心都有;

  2. 另一方面,浏览器默认控件样式又少又丑,制作一个漂亮的控件非常麻烦,也有一些制作简单的ui框架但性能低下。

mui框架有效的解决了这些问题,这是一个可以方便开发出高性能App的框架,也是目前最接近原生App效果的框架。

其实我们原本是开放心态,大家随意使用自己喜欢的前端框架。但是其他移动App框架实在不给力:

  • 基于jq的jqmobile,性能低的无法忍受
  • 基于angular的ionic,都把pc端很重的东西引入到移动App中。angularjs本身设计是为了pc端网页的双向数据绑定,做个移动App干嘛用这么重的东西。
  • bootstrap这种响应式设计,性能在低端机不足,而且UI风格一看就是网页,不是App的感觉。
    于是为了方便广大开发者,我们制作了mui。

MUI的定位是:最接近原生体验的移动App的UI框架

基于mui的定位,产生了mui的几个特点,轻、小、只涉及UI、只为移动App而生、界面风格原生化。
所以请大家注意,mui有所为有所不为:

  1. mui不是jq,不封装dom操作
    与ui无关的mui不做,你愿意用jq或zepto就自己用,并不冲突。
    但我们并不建议在移动App里引入jq或zepto这些框架,原因如下:

    • 为了性能,层层封装的框架,尤其是遍历循环dom时,影响效率,尤其在低端Android手机上,我们费死劲了才把性能以毫秒为单位一点点提升,搞这个的dom框架进来就让很多努力又付诸东流。
    • 原生JS挺简单,为何需要jq?
      jq的成功当时是因为ie6、7、8、9、10、chrome、ff这些浏览器不兼容,让开发者崩溃,而且pc上浏览器性能好,跨平台兼容也不影响性能。但jq根本就不是为手机设计的。
      手机上只有webkit浏览器(忽略wp,反正mui不支持wp),根本就不需要jq这种封装框架来操作dom。
      而且HBuilder提供了代码块来简化开发,敲dg、dq,直接生成document.getElementById("")、document.querySelectorAll(""),非常快捷方便,而且执行性能非常高,而且没有浏览器兼容问题。
      发现很多开发者只会jq,反正想继续在App里使用jq没有问题。但也建议大家多学学js本身。
      mui与vue、react、angular也不是一个层面的东西,可以在一个工程里混合使用。但在大多数ui控件上,应该直接使用mui的写法,因为mui的绘制是最朴素的HTML绘制,不是经过js操作的绘制,这种方案的效率比经过js绘制的效率要高很多。只有必须经过js操作才能渲染的控件,比如ajax联网后填充的list,此时使用vue或react都可以。
  2. mui、HTML5+、5+Runtime的关系说明
    mui是一个前端框架,HTML5+是一套HTML5能力扩展规范,HTML5+ Runtime是实现HTML5+规范的强化浏览器引擎。
    有点类似于bootstrap、w3c和chrome os的关系。
    HTML5+规范隶属于http://www.html5plus.org,定义了HTML5规范中没有但开发者做App需要的扩展规范。
    DCloud的5+ Runtime完整的实现了HTML5+规范。同时5+ Runtime还实现了Native.js,一种通过js调用几十万原生API的技术。
    为了提升体验,mui势必会调用一些5+Rutime的增强能力,主要是plus.webview、plus.nativeobj和plus.nativeUI。
    但mui不是要替代HTML5Plus,以后也无计划替代把所有5+的api都包一层。
    mui是把一些常用的窗体操作封装了,但这种封装适应面也是有限的,遇到复杂窗体管理,还是要仔细了解plus的api。
    所以,

    • 有人抱怨mui的文档不全,其实是缺本文,本文终于说清楚mui做什么不做什么了。详细的mui文档要去下方提示的mui官网查看。
    • 有人抱怨mui api不全,其实是没去看plus的api。知原理、知如何封装,方能融汇贯通。
    • 有人抱怨Hello mui示例代码里写的mui的方法,为何文档里没有,是因为有些方法是内部工程师简化开发中的封装,未考虑通用设计,还不足以开放为标准api,所以文档里没介绍。
  3. mui有插件体系
    为了简化开发者的多端发布开发,mui在核心库之外,补充了一些插件,这些插件不一定是ui相关,也有业务相关。
    在Hello mui示例里下方的示例模板,基本都属于插件。这些插件的使用需要加载mui标准库之外的js等资源。

mui是一个开源项目,请前往托管在github的mui官网查看详细介绍

这里是mui发布时的演讲视频:http://v.youku.com/v_show/id_XNzYyOTEyMjcy.html

收起阅读 »

移动App精彩案例分享

案例 App

目前HBuilder已经生成十几万个移动App(不包括离线打包数据)。

我们无权直接采集App信息,以下案例均为开发者在日常交流时提供的截图。
如果有图不适合放出,请告知我们,我们将删除。

但我们认为程序员彼此的交流分享非常重要,并希望开发者能更踊跃的共享自己的案例和开发经验。
欢迎大家在自己的博客撰写案例和开发经验,然后把链接微博 @数字天堂网络
我们将汇总收集在这里,同时给予该开发者1000分的奖励。
我们收集的开源项目,都汇总在该地址:https://github.com/dcloudio/casecode,也欢迎更多人提交开源项目。

最新案例请到DCloud官网案例页面查看

以下是案例集锦:

挑食火锅

挑食火锅是国内领先的火锅外卖上门o2o厂商。
挑食App是典型的一套代码,多端发布。
1套HTML工程,通过grunt编译,发布为iOS版、Android版、手机浏览器版、微信版、360生活助手流应用版。
App下载地址:http://www.dcloud.io/case/tiaoshi/
手机浏览器访问地址:https://www.4000121777.com/m/



36Kr

此版本为36kr流应用专版,开源地址:https://github.com/dcloudio/casecode



明道OA

此App是企业移动应用的典范。明道的该产品目前已经有3千多企业在使用。
官方介绍及下载:http://oa.mingdao.com/home/index/mobile
开发者:zhenjiawang



明道企业理财

明道企业理财,企业账户专属的“余额宝”,详见
https://app.mingdao.com/?appID=d88313c4-e223-4e58-a67c-1a383ebb7a0c



期待乐

月供分期App,体验不错。了解详情及下载App
http://www.qidaile.com/app/download.qi




新华社瞭望智库

各大应用市场搜索“瞭望智库”均有下载,比如应用宝





爱学车

http://www.ai-drive.cn,36kr报道的创业项目,自由选教练而不用去驾校学车。



台湾开发者做的游戏资讯App

这是我们已知的第一个海外案例,也是第一个mui框架的案例,Appstore地址:https://itunes.apple.com/us/app/info-for-puzzle-dragons/id926008312?l=zh&ls=1&mt=8
开发者:WinePaster


赞宝 智能硬件

赞宝是一个智能玩具熊的配套App,使用5+ SDK开发。
没有玩具熊无法登陆,介绍详见http://www.iminido.com/
在各大应用市场搜索赞宝均可下载。



天行者户外 mui的微信App

这是一个脱离5+ runtime环境的、在微信等普通浏览器里运行的web app。http://m.skywalk-er.com/





移动办公

此应用为企业内部应用


U8旅途

旅行类O2O应用,了解详情及下载Apphttp://u8.lt/



一点爱车

IOS Appstore搜索一点爱车
开发者:路先生



呜呜喳喳

大连本地o2o,Appstore搜索呜呜喳喳
开发者:吃饭邋邋汤



人脉返现

导流电商和返现获取优惠,详细介绍及App下载:http://www.rmfanx.com/article/view-366.html





出国汇

已在各大安卓市场上线。是5+ SDK的案例。
开发者:郑州可瑞为信息技术有限公司



呼我

5+SDK案例,使用原生插件与HTML5+结合。
免费电话、多方通话、陌生交友。下载地址: http://android.myapp.com/myapp/detail.htm?apkName=com.taojin.icall




枫桥居花卉

种花养花识花,交花友。下载地址: http://android.myapp.com/myapp/detail.htm?apkName=io.dcloud.H510E2B40




1号房

已经在Appstore及各种安卓市场上架。




爱房帮

地产经纪人专用,非公开发行。
开发者:藏马_paladin



星梦影院

影讯、订票、选座一条龙




领信教育

学校专用,非公开发行。
开发者:燃烧



聚钱袋

互联网金融App,详见http://www.juqiandai.com/app/
开发者:记忆、del






开心洗衣

o2o应用,整个多家线下洗衣门店



健康频道

开发者:ser
豌豆荚地址:http://www.wandoujia.com/apps/com.jsoon.healthChannel



live note

问答社区,下载地址:http://shan-gong.com/iask-download.html




宝宝百科

育儿App,有声有色



某广告公司App

开发者:如果你听见我的歌



海外社交电商




易道时代


管家婆家庭记账


淄博警方

Appstore搜索可下载


华职人才网

mui在纯浏览器里的应用案例。http://www.fznsm.com/hzrcw/index.php/resume/resumelist.htm




指尖东楚


外勤管理


苗管易


Apm个人助理


笔记


手机物流


某订单系统


蜂播


历史上的今天

开发者:紫马科技张工 源码开放地址:http://coding.zimayun.com/?p=19


有些开发者误以为案例App是原生做的,这是由2种情况造成的:

  1. 有些App的Android或iOS中的某个平台使用HBuilder开发,另一个平台是原生的。
  2. 有些App确实是原生的,但是内嵌了5+ SDK,这也属于我们的案例。
    如果你想学习参考这些App的源码,解压安装包,寻找www目录,即可看到HTML等文件。
    反之,如果你想保护源码,请混淆后再发布。

发微博@数字天堂网络,提交你的案例,分享给所有HTML5开发者。

继续阅读 »

目前HBuilder已经生成十几万个移动App(不包括离线打包数据)。

我们无权直接采集App信息,以下案例均为开发者在日常交流时提供的截图。
如果有图不适合放出,请告知我们,我们将删除。

但我们认为程序员彼此的交流分享非常重要,并希望开发者能更踊跃的共享自己的案例和开发经验。
欢迎大家在自己的博客撰写案例和开发经验,然后把链接微博 @数字天堂网络
我们将汇总收集在这里,同时给予该开发者1000分的奖励。
我们收集的开源项目,都汇总在该地址:https://github.com/dcloudio/casecode,也欢迎更多人提交开源项目。

最新案例请到DCloud官网案例页面查看

以下是案例集锦:

挑食火锅

挑食火锅是国内领先的火锅外卖上门o2o厂商。
挑食App是典型的一套代码,多端发布。
1套HTML工程,通过grunt编译,发布为iOS版、Android版、手机浏览器版、微信版、360生活助手流应用版。
App下载地址:http://www.dcloud.io/case/tiaoshi/
手机浏览器访问地址:https://www.4000121777.com/m/



36Kr

此版本为36kr流应用专版,开源地址:https://github.com/dcloudio/casecode



明道OA

此App是企业移动应用的典范。明道的该产品目前已经有3千多企业在使用。
官方介绍及下载:http://oa.mingdao.com/home/index/mobile
开发者:zhenjiawang



明道企业理财

明道企业理财,企业账户专属的“余额宝”,详见
https://app.mingdao.com/?appID=d88313c4-e223-4e58-a67c-1a383ebb7a0c



期待乐

月供分期App,体验不错。了解详情及下载App
http://www.qidaile.com/app/download.qi




新华社瞭望智库

各大应用市场搜索“瞭望智库”均有下载,比如应用宝





爱学车

http://www.ai-drive.cn,36kr报道的创业项目,自由选教练而不用去驾校学车。



台湾开发者做的游戏资讯App

这是我们已知的第一个海外案例,也是第一个mui框架的案例,Appstore地址:https://itunes.apple.com/us/app/info-for-puzzle-dragons/id926008312?l=zh&ls=1&mt=8
开发者:WinePaster


赞宝 智能硬件

赞宝是一个智能玩具熊的配套App,使用5+ SDK开发。
没有玩具熊无法登陆,介绍详见http://www.iminido.com/
在各大应用市场搜索赞宝均可下载。



天行者户外 mui的微信App

这是一个脱离5+ runtime环境的、在微信等普通浏览器里运行的web app。http://m.skywalk-er.com/





移动办公

此应用为企业内部应用


U8旅途

旅行类O2O应用,了解详情及下载Apphttp://u8.lt/



一点爱车

IOS Appstore搜索一点爱车
开发者:路先生



呜呜喳喳

大连本地o2o,Appstore搜索呜呜喳喳
开发者:吃饭邋邋汤



人脉返现

导流电商和返现获取优惠,详细介绍及App下载:http://www.rmfanx.com/article/view-366.html





出国汇

已在各大安卓市场上线。是5+ SDK的案例。
开发者:郑州可瑞为信息技术有限公司



呼我

5+SDK案例,使用原生插件与HTML5+结合。
免费电话、多方通话、陌生交友。下载地址: http://android.myapp.com/myapp/detail.htm?apkName=com.taojin.icall




枫桥居花卉

种花养花识花,交花友。下载地址: http://android.myapp.com/myapp/detail.htm?apkName=io.dcloud.H510E2B40




1号房

已经在Appstore及各种安卓市场上架。




爱房帮

地产经纪人专用,非公开发行。
开发者:藏马_paladin



星梦影院

影讯、订票、选座一条龙




领信教育

学校专用,非公开发行。
开发者:燃烧



聚钱袋

互联网金融App,详见http://www.juqiandai.com/app/
开发者:记忆、del






开心洗衣

o2o应用,整个多家线下洗衣门店



健康频道

开发者:ser
豌豆荚地址:http://www.wandoujia.com/apps/com.jsoon.healthChannel



live note

问答社区,下载地址:http://shan-gong.com/iask-download.html




宝宝百科

育儿App,有声有色



某广告公司App

开发者:如果你听见我的歌



海外社交电商




易道时代


管家婆家庭记账


淄博警方

Appstore搜索可下载


华职人才网

mui在纯浏览器里的应用案例。http://www.fznsm.com/hzrcw/index.php/resume/resumelist.htm




指尖东楚


外勤管理


苗管易


Apm个人助理


笔记


手机物流


某订单系统


蜂播


历史上的今天

开发者:紫马科技张工 源码开放地址:http://coding.zimayun.com/?p=19


有些开发者误以为案例App是原生做的,这是由2种情况造成的:

  1. 有些App的Android或iOS中的某个平台使用HBuilder开发,另一个平台是原生的。
  2. 有些App确实是原生的,但是内嵌了5+ SDK,这也属于我们的案例。
    如果你想学习参考这些App的源码,解压安装包,寻找www目录,即可看到HTML等文件。
    反之,如果你想保护源码,请混淆后再发布。

发微博@数字天堂网络,提交你的案例,分享给所有HTML5开发者。

收起阅读 »

5+ App开发入门指南

App App入门

HTML5 Plus应用概述

HTML5 Plus移动App,简称5+App,是一种基于HTML、JS、CSS编写的运行于手机端的App,这种App可以通过扩展的JS API任意调用手机的原生能力,实现与原生App同样强大的功能和性能。

HTML5 Plus规范

通过HTML5开发移动App时,会发现HTML5很多能力不具备。为弥补HTML5能力的不足,在W3C中国的指导下成立了HTML5中国产业联盟www.html5plus.org组织,推出HTML5+规范。目前该联盟已经挂靠在工信部信通院标准所下,相关标准已经成为行业标准。
HTML5+规范是一个开放规范,隶属于工信部,允许三方浏览器厂商或其他手机runtime制造商实现。
HTML5+扩展了JavaScript对象plus,使得js可以调用各种浏览器无法实现或实现不佳的系统能力,设备能力如摄像头、陀螺仪、文件系统等,业务能力如上传下载、二维码、地图、支付、语音输入、消息推送等。
除了功能外,HTML5+很重要的特点是提供了原生的渲染能力,通过plus.webview、plus.nativeObj、plus.nativeUI,让开发者可以使用js来调用原生渲染能力,实现体验的大幅提升。
原生的api多达40万,HTML5+的封装并非把40万api都封装了一遍,而是分成了2个层面:

  • HTML5Plus规范:常用的扩展能力,比如二维码、语音输入,都封装到了规范中,同时实现了Android和iOS的解析引擎,使得开发者的代码编写一次,可跨平台运行。
  • Native.js是另一项创新技术。手机OS的原生API有四十多万,大量的API无法被HTML5使用。Native.js把几十万原生API映射成了js对象,通过js可以直接调ios和android的原生API。这部分就不再跨平台,写法分别是plus.ios和plus.android,比如调ios game center,或在android手机桌面创建快捷方式,这些都是平台专有的api。

Native.js的用法示例

var obj= plus.android.import("android.content.Intent");

将一个原生对象android.content.Intent映射为js对象obj,然后在js里操作obj对象的方法属性就可以了。
Native.js的详细教程可以参考:5+ App开发Native.js入门指南
在5+App里,同时包含了HTML5Plus规范和Native.js的实现,开发者可以在5+App里自由使用相关技术。

5+ App概念解析

首先开发者需要清楚你要做什么,是要做一个app,安装和运行在手机上?或者要把一个mobile web项目打包成app?

  1. 做一个正经app
    传统意义上的app,是c/s方式的,它的程序要安装和运行在手机上,不通过浏览器在线下载。
    此时开发者在HBuilderX里新建项目时,选择“5+App”。(HBuilder里叫做移动App)
    在App项目下编写的HTML、js等文件,是会被打包到原生的安装包(Android是apk包、iOS是ipa包)里的。
    此时本地的js和服务器通过ajax交互,由服务器按接口方式给出数据(一般是json),然后客户端的js文件解析json,并根据本地的业务逻辑来渲染页面和执行功能。
    所以请不要在App项目中放置运行在服务器端的php等文件。
  2. 使用wap2app打包mobile web项目为app
    如果开发者想把一个做好的mobile web站,方便快速的打包成app,那么要使用DCloud的wap2app框架。
    在HBuilderX中新建项目时,选Wap2App项目,输入项目名称及wap站首页地址,其它参考框架的教程来配置。
    wap2app不同于普通的web打包技术,wap2app可真正做达到原生应用的功能和性能体验。
    具体教程另见:文档中心-wap2app,http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/1244
    wap2app属于5+app,它底层也是强大的HTML5Plus规范和Native.js在支撑。
    wap2app项目下的所有文件,也都是打包在本机运行的。

HTML5+ 应用架构

HTML5+ 应用架构图

HTML5+ 规范 API 及demo示例

最新规范请参考http://www.html5plus.org/#specification
手机端体验各个API的实现效果,ios手机在Appstore搜索Hello H5+,Android手机下载地址
在HBuilderX中新建项目,勾选5+ App,选择模板Hello H5+,即可看到这个demo的源代码。

开发环境HBuilderX

HBuilderX内置HTML5+ APP开发环境,提供一套完整的移动应用开发解决方案。
内置HTML5+ API语法提示,提高开发效率;
集成真机运行环境,方便开发后即时在真机上查看运行效果;
集成应用云端打包系统,不用部署xcode和Android sdk就可以打包应用。使开发者只需要使用HTML5、Javascript、CSS技术就可以快速开发跨平台的移动应用。
下载地址:http://www.dcloud.io/

平台支持

  • iOS 8.0及以上
  • Android 4.4及以上

手把手教你开发HelloWorld

以下教程已更新为HBuilderX版本,使用老版本HBuilder的开发者请更新到最新版本的HBuilderX,老版本HBuilder将不再更新。

创建HelloWorld应用

  • 启动HBuilderX(下载地址:http://www.dcloud.io/
  • 在菜单栏中选择“文件”-> “新建”->“项目”,打开“新建项目”对话框,选择“5+ App”
  • 在项目名称中输入“HelloWorld”,选择模板中勾选“默认模板”

    注意:新建5+ App项目需要联网分配一个appid,在真机联调、打包发行时都需要这个ID,所以不联网将无法获取appid
  • 创建完成后,会在项目管理器中显示新建的“HelloWorld”项目

manifest.json

在项目管理器中双击“manifest.json”文件,打开应用配置页面:

对于要打包的原生应用而言,其各种配置均在此处。具体配置教程见:Manifest.json文件配置,或者点击配置页面上的“配置指南”链接。

调用HTML5+ API

在项目管理器中双击“index.html”文件,对于HTML5+应用的页面有一个很重要的“plusready”事件,此事件会在页面加载后自动触发,表示所有HTML5+ API可以使用,在此事件触发之前不能调用HTML5+ API,所以应该在此事件回调函数中调用页面初始化需要调用的HTML5+ API,而不应该在onload或DOMContentLoaded事件中调用:

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8"/>  
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
    <title>Hello world</title>  
    <script type="text/javascript">   
// 扩展API是否准备好,如果没有则监听“plusready"事件  
if(window.plus){  
    plusReady();  
}else{   
    document.addEventListener("plusready", plusReady, false);  
}  
// 扩展API准备完成后要执行的操作  
function plusReady(){  
    var ws = plus.webview.currentWebview(); //pw回车可输出plus.webview  
    // ... code  
}  
    </script>  
</head>   
<body>  
  Hello World<br/>  
</body>  
</html>

编辑程序启动后默认显示的页面index.html,在页面中添加一个按钮,点击后将打开新页面加载“http://www.dcloud.io/”,为了实现此功能,我们需要定义openNewWebview方法,调用5+ API中plus.webview.createWebview()方法创建窗口,调用窗口对象的show方法显示:

// 打开新Webview窗口  
function openNewWebview(){  
  var wv = plus.webview.create('http://www.dcloud.io/');  
  wv.show();  
}

完整代码截图:

编辑完成后,按Ctrl+S键保存。

真机运行

写完代码后,我们可以通过真机运行来查看效果,真机运行有3个特点:

  1. 真实:
    虽然PC端HBuilderX右侧的内置浏览器也可以看大致的页面,但真实的布局效果以及手机上的特殊能力调用,还是必须在真机测试。
  2. 边改边看:
    在HBuilderX更改页面并保存后,可立即同步在真机上看到保存后的显示效果。比开发原生应用还方便。
  3. 检查错误和log:
    手机运行HTML等文件时如果发生错误以及打印的console.log,都可以在真机运行时从手机端反馈回到HBuilderX的控制台,在控制台直接查看。
    注意只有移动App项目(uni-app、Wap2App、5+ App)才可以真机联调。

在HBuilderX的“项目管理器”中选择创建的“HelloWorld”应用。

启动真机运行

将iOS或Android设备连接到电脑,这时HBuilderX会自动检测连接到电脑上的设备,通过菜单栏中的“运行” -> “运行到手机或模拟器”,选择要运行的设备启动真机运行:

也可通过工具栏启动:

启动真机运行后,在底部控制台显示以下信息:

注:如果提示错误信息,请尝试“终止”后重新启动真机运行!

启动后如果弹出提示框,请选择“确定”,显示以下页面:

点击“打开新页面”按钮,加载显示“http://www.dcloud.io/”网页:

在Android设备会自动安装真机运行基座HBuilder APP,并启动运行,iOS设备需要开发者手动点击手机桌面的HBuilder App图标。

如果你真机失败,注意看控制台的提示,或点HBuilder菜单-运行里的故障排查指南。
注意:真机联调App时,提供的是一个测试环境,并不真实发生打包,调试基座App的名字、图标、启动封面图片、是否可旋转这些只有打包才能更改的属性不会因为开发者修改manifest文件而变化。只有修改manifest且点击菜单发行-打包后,上述4个设置才能更改。

运行后,HBuilder中修改页面代码,保存后会自动同步到手机中,如果手机当前展示着被修改的页面,则会刷新页面。
尝试在js中在plusready之后编写console.log,或者改写错误的js,可以直接在HBuilder的控制台看到结果。
如果真机运行遇到各种故障,请点击运行菜单里的真机运行常见故障指南。

debug调试

除了真机运行,我们还可以利用chrome和safari的开发者控制台来调试5+App。
可以使用真机插上数据线,也可以使用Android或iOS的官方模拟器。所有Api包括plus的各种api,甚至包括plus.ios和plus.android的原生对象,都可以调试。
在HBuilder的菜单运行里选择打开Webview调试模式,如果手机连接正常,5+App启动,在ide上可看到可调试的页面,点击调试后,打开控制台,和普通的浏览器调试是一样的。

发行打包

完成应用页面的编辑后,需要正式打包为原生的apk或ipa安装包。
首先明确一下,有人说HTML5做的应用无法通过苹果Appstore审核,这是错误的说法。苹果只是拒绝开发者把web站点直接打包上Appstore,不优化任何体验,它认为这是给Appstore制造垃圾应用,如果是原生体验的App,虽然使用HTML5技术,苹果也不会拒绝上架。事实上Appstore上使用HTML5技术的App超过40w。
HBuilderX提供的打包有云打包和本地打包两种。
HBuilderX提供的云打包对正常开发者是免费的。但过多浪费服务器资源会额外收费。用本地打包无任何限制。
云打包的特点是DCloud官方配置好了原生的打包环境,可以把HTML等文件编译为原生安装包。

  1. 对于不熟悉原生开发的前端工程师,云打包大幅降低了他们的使用门槛。
  2. 对于没有mac电脑的开发者,他们也可以通过云打包直接打出iOS的ipa包。

无论云打包还是本地打包,在HBuilderX的菜单“发行”菜单中有链接。
本地打包还可参考原生开发者支持网站的App离线打包页面,此处仅对云打包进行说明。

通过菜单栏中的“发行”->“原生App-云打包”,打开“App云端打包”对话框提交。

注意:只有App项目才可以打包

iOS平台打包

对于iOS平台,可以选择越狱包或证书包(Appstore专用或企业证书),前者只能安装在已越狱的设备上,后者则可通过iDP证书打包提交到Appstore发布、或通过iEP证书打包在企业内部发布。

越狱包

  • Bundle ID(AppID):iOS应用标识,推荐使用反向域名风格的字符串,如“com.domainname.appname”。

IDP/IEP证书包

  • Bundle ID(AppID):iOS应用标识,推荐使用反向域名风格的字符串,如“com.domainname.appname”,必须与profile文件绑定的App ID匹配。
  • 证书profile文件:iOS Provisioning Profile文件(.mobileprovision),必须与苹果App ID和私钥证书区配。
  • 证书私钥密码:导入私钥证书的密码。
  • 私钥证书:iOS Certificates文件(.p12)。

私钥证书及profile文件生成请参考http://ask.dcloud.net.cn/article/152

Android平台打包

对于Android平台,可以选择使用DCloud生成的公用证书或自己生成的证书,两者不影响安装包的发布,唯一的差别就是证书中开发者和企业信息不同。
应用在开发测试阶段可以使用公共测试证书打包体验,发布时请使用自有证书打包,更多信息参考Android平台云端打包证书使用说明

使用自有证书

以自有证书打包为例,使用公共测试证书时不用配置证书相关信息,其它配置一致:

  • Android包名:Android应用包名,使用反向域名风格的字符串,如“com.domainname.appname”。
  • 证书别名:生成证书时使用-alias参数设置的证书别名;
  • 私钥密码:生成证书时使用的keystore密码;
  • 证书文件:生成证书时使用-keystore参数设置的证书保存路径;

查看打包状态

通过菜单栏中的“发行”->“原生App-查看打包状态”,可在控制台查看当前打包状态:

如果打包成功,则会在控制台显示下载地址,可点击链接下载安装包。

遇到打包失败,常见原因是:
如果使用自用证书,很可能是证书配置错误。
如果使用DCloud证书仍然出错,很可能是图片错误。所有图片格式必须是标准png,且严格符合分辨率要求。使用其他图片格式重命名为png会导致打包失败!
其他错误请参考: 云打包常见错误排查指南

UI框架

HBuilder并不限制UI框架,开发者使用任何UI框架均可以。
不过市面上确实没有什么好的手机App前端框架,DCloud开发了mui框架,它的性能更高,样式也更接近原生App,并且mui调用了HTML5+扩展能力,可以实现更好的体验。
我们强烈推荐开发移动App的开发者使用mui框架,详情请参考文档中心mui章节
请注意,mui只封装了部分HTML5Plus Api,学会mui框架不代表可以不学习HTML5Plus规范。mui不会做的很重,只是很有限的通过封装简化了常见开发过程。

开发资源

API手册HTML5+规范

HelloH5+示例应用,应用中包括几乎所有plus API的示例:

HelloH5二维码图片
获取Hello H5+的源代码,在HBuilder中新建移动App,选Hello H5+。可以查看所有plus api的调用样例代码。

Hello mui示例应用,漂亮且高性能的前端UI框架:
下载页面

进阶教程

如果想开发出接近原生体验的App,请访问如下教程:

三方培训

HTML5中国产业联盟里有专业的培训机构为HTML5开发者提供DCloud产品的培训。
详见专门文章http://ask.dcloud.net.cn/article/299

发行和变现服务

在你的app开发完毕后,DCloud还提供了发布平台,帮助开发者简单的完成应用推广页面,参考http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/12936
如果开发者还需要流量变现,DCloud提供了广告平台,开发者可以方便的在自己的app中集成广告,参考https://uniad.dcloud.net.cn/

最后,祝你通过DCloud的免费工具,快速完成自己的移动App,并获得更多用户和变现收益!

继续阅读 »

HTML5 Plus应用概述

HTML5 Plus移动App,简称5+App,是一种基于HTML、JS、CSS编写的运行于手机端的App,这种App可以通过扩展的JS API任意调用手机的原生能力,实现与原生App同样强大的功能和性能。

HTML5 Plus规范

通过HTML5开发移动App时,会发现HTML5很多能力不具备。为弥补HTML5能力的不足,在W3C中国的指导下成立了HTML5中国产业联盟www.html5plus.org组织,推出HTML5+规范。目前该联盟已经挂靠在工信部信通院标准所下,相关标准已经成为行业标准。
HTML5+规范是一个开放规范,隶属于工信部,允许三方浏览器厂商或其他手机runtime制造商实现。
HTML5+扩展了JavaScript对象plus,使得js可以调用各种浏览器无法实现或实现不佳的系统能力,设备能力如摄像头、陀螺仪、文件系统等,业务能力如上传下载、二维码、地图、支付、语音输入、消息推送等。
除了功能外,HTML5+很重要的特点是提供了原生的渲染能力,通过plus.webview、plus.nativeObj、plus.nativeUI,让开发者可以使用js来调用原生渲染能力,实现体验的大幅提升。
原生的api多达40万,HTML5+的封装并非把40万api都封装了一遍,而是分成了2个层面:

  • HTML5Plus规范:常用的扩展能力,比如二维码、语音输入,都封装到了规范中,同时实现了Android和iOS的解析引擎,使得开发者的代码编写一次,可跨平台运行。
  • Native.js是另一项创新技术。手机OS的原生API有四十多万,大量的API无法被HTML5使用。Native.js把几十万原生API映射成了js对象,通过js可以直接调ios和android的原生API。这部分就不再跨平台,写法分别是plus.ios和plus.android,比如调ios game center,或在android手机桌面创建快捷方式,这些都是平台专有的api。

Native.js的用法示例

var obj= plus.android.import("android.content.Intent");

将一个原生对象android.content.Intent映射为js对象obj,然后在js里操作obj对象的方法属性就可以了。
Native.js的详细教程可以参考:5+ App开发Native.js入门指南
在5+App里,同时包含了HTML5Plus规范和Native.js的实现,开发者可以在5+App里自由使用相关技术。

5+ App概念解析

首先开发者需要清楚你要做什么,是要做一个app,安装和运行在手机上?或者要把一个mobile web项目打包成app?

  1. 做一个正经app
    传统意义上的app,是c/s方式的,它的程序要安装和运行在手机上,不通过浏览器在线下载。
    此时开发者在HBuilderX里新建项目时,选择“5+App”。(HBuilder里叫做移动App)
    在App项目下编写的HTML、js等文件,是会被打包到原生的安装包(Android是apk包、iOS是ipa包)里的。
    此时本地的js和服务器通过ajax交互,由服务器按接口方式给出数据(一般是json),然后客户端的js文件解析json,并根据本地的业务逻辑来渲染页面和执行功能。
    所以请不要在App项目中放置运行在服务器端的php等文件。
  2. 使用wap2app打包mobile web项目为app
    如果开发者想把一个做好的mobile web站,方便快速的打包成app,那么要使用DCloud的wap2app框架。
    在HBuilderX中新建项目时,选Wap2App项目,输入项目名称及wap站首页地址,其它参考框架的教程来配置。
    wap2app不同于普通的web打包技术,wap2app可真正做达到原生应用的功能和性能体验。
    具体教程另见:文档中心-wap2app,http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/1244
    wap2app属于5+app,它底层也是强大的HTML5Plus规范和Native.js在支撑。
    wap2app项目下的所有文件,也都是打包在本机运行的。

HTML5+ 应用架构

HTML5+ 应用架构图

HTML5+ 规范 API 及demo示例

最新规范请参考http://www.html5plus.org/#specification
手机端体验各个API的实现效果,ios手机在Appstore搜索Hello H5+,Android手机下载地址
在HBuilderX中新建项目,勾选5+ App,选择模板Hello H5+,即可看到这个demo的源代码。

开发环境HBuilderX

HBuilderX内置HTML5+ APP开发环境,提供一套完整的移动应用开发解决方案。
内置HTML5+ API语法提示,提高开发效率;
集成真机运行环境,方便开发后即时在真机上查看运行效果;
集成应用云端打包系统,不用部署xcode和Android sdk就可以打包应用。使开发者只需要使用HTML5、Javascript、CSS技术就可以快速开发跨平台的移动应用。
下载地址:http://www.dcloud.io/

平台支持

  • iOS 8.0及以上
  • Android 4.4及以上

手把手教你开发HelloWorld

以下教程已更新为HBuilderX版本,使用老版本HBuilder的开发者请更新到最新版本的HBuilderX,老版本HBuilder将不再更新。

创建HelloWorld应用

  • 启动HBuilderX(下载地址:http://www.dcloud.io/
  • 在菜单栏中选择“文件”-> “新建”->“项目”,打开“新建项目”对话框,选择“5+ App”
  • 在项目名称中输入“HelloWorld”,选择模板中勾选“默认模板”

    注意:新建5+ App项目需要联网分配一个appid,在真机联调、打包发行时都需要这个ID,所以不联网将无法获取appid
  • 创建完成后,会在项目管理器中显示新建的“HelloWorld”项目

manifest.json

在项目管理器中双击“manifest.json”文件,打开应用配置页面:

对于要打包的原生应用而言,其各种配置均在此处。具体配置教程见:Manifest.json文件配置,或者点击配置页面上的“配置指南”链接。

调用HTML5+ API

在项目管理器中双击“index.html”文件,对于HTML5+应用的页面有一个很重要的“plusready”事件,此事件会在页面加载后自动触发,表示所有HTML5+ API可以使用,在此事件触发之前不能调用HTML5+ API,所以应该在此事件回调函数中调用页面初始化需要调用的HTML5+ API,而不应该在onload或DOMContentLoaded事件中调用:

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8"/>  
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
    <title>Hello world</title>  
    <script type="text/javascript">   
// 扩展API是否准备好,如果没有则监听“plusready"事件  
if(window.plus){  
    plusReady();  
}else{   
    document.addEventListener("plusready", plusReady, false);  
}  
// 扩展API准备完成后要执行的操作  
function plusReady(){  
    var ws = plus.webview.currentWebview(); //pw回车可输出plus.webview  
    // ... code  
}  
    </script>  
</head>   
<body>  
  Hello World<br/>  
</body>  
</html>

编辑程序启动后默认显示的页面index.html,在页面中添加一个按钮,点击后将打开新页面加载“http://www.dcloud.io/”,为了实现此功能,我们需要定义openNewWebview方法,调用5+ API中plus.webview.createWebview()方法创建窗口,调用窗口对象的show方法显示:

// 打开新Webview窗口  
function openNewWebview(){  
  var wv = plus.webview.create('http://www.dcloud.io/');  
  wv.show();  
}

完整代码截图:

编辑完成后,按Ctrl+S键保存。

真机运行

写完代码后,我们可以通过真机运行来查看效果,真机运行有3个特点:

  1. 真实:
    虽然PC端HBuilderX右侧的内置浏览器也可以看大致的页面,但真实的布局效果以及手机上的特殊能力调用,还是必须在真机测试。
  2. 边改边看:
    在HBuilderX更改页面并保存后,可立即同步在真机上看到保存后的显示效果。比开发原生应用还方便。
  3. 检查错误和log:
    手机运行HTML等文件时如果发生错误以及打印的console.log,都可以在真机运行时从手机端反馈回到HBuilderX的控制台,在控制台直接查看。
    注意只有移动App项目(uni-app、Wap2App、5+ App)才可以真机联调。

在HBuilderX的“项目管理器”中选择创建的“HelloWorld”应用。

启动真机运行

将iOS或Android设备连接到电脑,这时HBuilderX会自动检测连接到电脑上的设备,通过菜单栏中的“运行” -> “运行到手机或模拟器”,选择要运行的设备启动真机运行:

也可通过工具栏启动:

启动真机运行后,在底部控制台显示以下信息:

注:如果提示错误信息,请尝试“终止”后重新启动真机运行!

启动后如果弹出提示框,请选择“确定”,显示以下页面:

点击“打开新页面”按钮,加载显示“http://www.dcloud.io/”网页:

在Android设备会自动安装真机运行基座HBuilder APP,并启动运行,iOS设备需要开发者手动点击手机桌面的HBuilder App图标。

如果你真机失败,注意看控制台的提示,或点HBuilder菜单-运行里的故障排查指南。
注意:真机联调App时,提供的是一个测试环境,并不真实发生打包,调试基座App的名字、图标、启动封面图片、是否可旋转这些只有打包才能更改的属性不会因为开发者修改manifest文件而变化。只有修改manifest且点击菜单发行-打包后,上述4个设置才能更改。

运行后,HBuilder中修改页面代码,保存后会自动同步到手机中,如果手机当前展示着被修改的页面,则会刷新页面。
尝试在js中在plusready之后编写console.log,或者改写错误的js,可以直接在HBuilder的控制台看到结果。
如果真机运行遇到各种故障,请点击运行菜单里的真机运行常见故障指南。

debug调试

除了真机运行,我们还可以利用chrome和safari的开发者控制台来调试5+App。
可以使用真机插上数据线,也可以使用Android或iOS的官方模拟器。所有Api包括plus的各种api,甚至包括plus.ios和plus.android的原生对象,都可以调试。
在HBuilder的菜单运行里选择打开Webview调试模式,如果手机连接正常,5+App启动,在ide上可看到可调试的页面,点击调试后,打开控制台,和普通的浏览器调试是一样的。

发行打包

完成应用页面的编辑后,需要正式打包为原生的apk或ipa安装包。
首先明确一下,有人说HTML5做的应用无法通过苹果Appstore审核,这是错误的说法。苹果只是拒绝开发者把web站点直接打包上Appstore,不优化任何体验,它认为这是给Appstore制造垃圾应用,如果是原生体验的App,虽然使用HTML5技术,苹果也不会拒绝上架。事实上Appstore上使用HTML5技术的App超过40w。
HBuilderX提供的打包有云打包和本地打包两种。
HBuilderX提供的云打包对正常开发者是免费的。但过多浪费服务器资源会额外收费。用本地打包无任何限制。
云打包的特点是DCloud官方配置好了原生的打包环境,可以把HTML等文件编译为原生安装包。

  1. 对于不熟悉原生开发的前端工程师,云打包大幅降低了他们的使用门槛。
  2. 对于没有mac电脑的开发者,他们也可以通过云打包直接打出iOS的ipa包。

无论云打包还是本地打包,在HBuilderX的菜单“发行”菜单中有链接。
本地打包还可参考原生开发者支持网站的App离线打包页面,此处仅对云打包进行说明。

通过菜单栏中的“发行”->“原生App-云打包”,打开“App云端打包”对话框提交。

注意:只有App项目才可以打包

iOS平台打包

对于iOS平台,可以选择越狱包或证书包(Appstore专用或企业证书),前者只能安装在已越狱的设备上,后者则可通过iDP证书打包提交到Appstore发布、或通过iEP证书打包在企业内部发布。

越狱包

  • Bundle ID(AppID):iOS应用标识,推荐使用反向域名风格的字符串,如“com.domainname.appname”。

IDP/IEP证书包

  • Bundle ID(AppID):iOS应用标识,推荐使用反向域名风格的字符串,如“com.domainname.appname”,必须与profile文件绑定的App ID匹配。
  • 证书profile文件:iOS Provisioning Profile文件(.mobileprovision),必须与苹果App ID和私钥证书区配。
  • 证书私钥密码:导入私钥证书的密码。
  • 私钥证书:iOS Certificates文件(.p12)。

私钥证书及profile文件生成请参考http://ask.dcloud.net.cn/article/152

Android平台打包

对于Android平台,可以选择使用DCloud生成的公用证书或自己生成的证书,两者不影响安装包的发布,唯一的差别就是证书中开发者和企业信息不同。
应用在开发测试阶段可以使用公共测试证书打包体验,发布时请使用自有证书打包,更多信息参考Android平台云端打包证书使用说明

使用自有证书

以自有证书打包为例,使用公共测试证书时不用配置证书相关信息,其它配置一致:

  • Android包名:Android应用包名,使用反向域名风格的字符串,如“com.domainname.appname”。
  • 证书别名:生成证书时使用-alias参数设置的证书别名;
  • 私钥密码:生成证书时使用的keystore密码;
  • 证书文件:生成证书时使用-keystore参数设置的证书保存路径;

查看打包状态

通过菜单栏中的“发行”->“原生App-查看打包状态”,可在控制台查看当前打包状态:

如果打包成功,则会在控制台显示下载地址,可点击链接下载安装包。

遇到打包失败,常见原因是:
如果使用自用证书,很可能是证书配置错误。
如果使用DCloud证书仍然出错,很可能是图片错误。所有图片格式必须是标准png,且严格符合分辨率要求。使用其他图片格式重命名为png会导致打包失败!
其他错误请参考: 云打包常见错误排查指南

UI框架

HBuilder并不限制UI框架,开发者使用任何UI框架均可以。
不过市面上确实没有什么好的手机App前端框架,DCloud开发了mui框架,它的性能更高,样式也更接近原生App,并且mui调用了HTML5+扩展能力,可以实现更好的体验。
我们强烈推荐开发移动App的开发者使用mui框架,详情请参考文档中心mui章节
请注意,mui只封装了部分HTML5Plus Api,学会mui框架不代表可以不学习HTML5Plus规范。mui不会做的很重,只是很有限的通过封装简化了常见开发过程。

开发资源

API手册HTML5+规范

HelloH5+示例应用,应用中包括几乎所有plus API的示例:

HelloH5二维码图片
获取Hello H5+的源代码,在HBuilder中新建移动App,选Hello H5+。可以查看所有plus api的调用样例代码。

Hello mui示例应用,漂亮且高性能的前端UI框架:
下载页面

进阶教程

如果想开发出接近原生体验的App,请访问如下教程:

三方培训

HTML5中国产业联盟里有专业的培训机构为HTML5开发者提供DCloud产品的培训。
详见专门文章http://ask.dcloud.net.cn/article/299

发行和变现服务

在你的app开发完毕后,DCloud还提供了发布平台,帮助开发者简单的完成应用推广页面,参考http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/12936
如果开发者还需要流量变现,DCloud提供了广告平台,开发者可以方便的在自己的app中集成广告,参考https://uniad.dcloud.net.cn/

最后,祝你通过DCloud的免费工具,快速完成自己的移动App,并获得更多用户和变现收益!

收起阅读 »

5+ App开发Native.js入门指南

Native.JS

概述

Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术。
如果说Node.js把js扩展到服务器世界,那么Native.js则把js扩展到手机App的原生世界。
HTML/JS/Css全部语法只有7万多,而原生语法有几十万,Native.js大幅提升了HTML5的能力。
NJS突破了浏览器的功能限制,也不再需要像Hybrid那样由原生语言开发插件才能补足浏览器欠缺的功能。
NJS编写的代码,最终需要在HBuilder里打包发行为App安装包,或者在支持Native.js技术的浏览器里运行。目前Native.js技术不能在普通手机浏览器里直接运行。

  • NJS大幅扩展了HTML5的能力范围,原本只有原生或Hybrid App的原生插件才能实现的功能如今可以使用JS实现。
  • NJS大幅提升了App开发效率,将iOS、Android、Web的3个工程师组队才能完成的App,变为1个web工程师就搞定。
  • NJS不再需要配置原生开发和编译环境,调试、打包均在HBuilder里进行。没有mac和xcode一样可以开发iOS应用。
  • 如果不熟悉原生API也没关系,我们汇总了很多NJS的代码示例,复制粘贴就可以用。http://ask.dcloud.net.cn/article/114

再次强调,Native.js不是一个js库,不需要下载引入到页面的script中,也不像nodejs那样有单独的运行环境,Native.js的运行环境是集成在5+runtime里的,使用HBuilder打包的app或流应用都可以直接使用Native.js。

注意事项:

Uni-app不支Native.js执行UI相关操作的API调用及webview相关API调用。将失效无法正常使用。Uni-app不推荐使用Native.js

技术要求

由于NJS是直接调用Native API,需要对Native API有一定了解,知道所需要的功能调用了哪些原生API,能看懂原生代码并参考原生代码修改为JS代码。
否则只能直接copy别人写好的NJS代码。

开始使用

判断平台

Native API具有平台依赖性,所以需要通过以下方式判断当前的运行平台:

function judgePlatform(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // Android平台: plus.android.*  
        break;  
        case "iOS":  
        // iOS平台: plus.ios.*  
        break;  
        default:  
        // 其它平台  
        break;  
    }  
}

类型转换

在NJS中调用Native API或从Native API返回数据到NJS时会自动转换数据类型。

类型转换表

类型 Objective-C Java JavaScript
基本数据 byte/short/int/long/float/double/... byte/short/int/long/float/double/... Number
字符 char char String
字符串 NSString/@"" String/"" String
数组 @[1,2,3]/NSArray new XXX[] InstanceObject
@interface class ClassObject
对象(实例) * * InstanceObject
空对象 nil null null
其它 Protocol Interface Object(JSON)

其他转换

  • Android原生应用的主Activity对象 转为plus.android.runtimeMainActivity()
    Android的主Activity对象是启动应用时自动创建的,不是代码创建,此时通过plus.android.runtimeMainActivity()方法获取该Activity对象
  • Objective-C方法冒号剔除
    [pos setPositionX:(int)x Y:(int)y;] 转为 pos.setPositionXY(x,y);
    OC语法中方法的定义格式为:
    “(返回值类型) 函数名: (参数1类型) 形参1 参数2名称: (参数2类型) 形参2”
    方法的完整名称为: “函数名:参数2名称:”。
    如:“(void)setPositionX:(int)x Y:(int)y;”,方法的完整名称为“setPositionX:Y:”,调用时语法为:“[pos setPositionX:x Y:y];”。
    在JS语法中函数名称不能包含“:”字符,所以OC对象的方法名映射成NJS对象方法名时将其中的“:”字符自动删除,上面方法名映射为“setPositionXY”,在NJS调用的语法为:“pos.setPositionXY(x,y);”。
  • 文件路径转换
    Web开发里使用的image/1.png是该web工程的相对路径,而原生API中经常需要使用绝对路径,比如/sdcard/apptest/image/1.png,此时使用这个扩展方法来完成转换:plus.io.convertLocalFileSystemURL("image/1.png")

概念

类对象

由于JavaScript中本身没有类的概念,为了使用Native API层的类,在NJS中引入了类对象(ClassObject)的概念,用于对Native中的类进行操作,如创建类的实例对象、访问类的静态属性、调用类的静态方法等。其原型如下:

Interface ClassObject {  
    function Object plusGetAttribute( String name );  
    function void plusSetAttribute( String name, Object value );  
}

获取类对象
在iOS平台我们可以通过plus.ios.importClass(name)方法导入类对象,参数name为类的名称;在Android平台我们可以通过plus.android.importClass(name)方法导入类对象,其参数name为类的名称,必须包含完整的命名空间。

示例:

// iOS平台导入NSNotificationCenter类  
var NSNotificationCenter = plus.ios.importClass("NSNotificationCenter");  

// Android平台导入Intent类  
var Intent = plus.android.importClass("android.content.Intent");

获取类对象后,可以通过类对象“.”操作符获取类的静态常量属性、调用类的静态方法,类的静态非常量属性需通过plusGetAttribute、plusSetAttribute方法操作。

实例对象

在JavaScript中,所有对象都是Object,为了操作Native层类的实例对象,在NJS中引入了实例对象(InstanceObject)的概念,用于对Native中的对象进行操作,如操作对象的属性、调用对象的方法等。其原型如下:

Interface InstanceObject {  
    function Object plusGetAttribute( String name );  
    function void plusSetAttribute( String name, Object value );  
}

获取实例对象
有两种方式获取类的实例对象,一种是调用Native API返回值获取,另一种是通过new操作符来创建导入的类对象的实例,如下:

// iOS平台导入NSDictionary类  
var NSDictionary = plus.ios.importClass("NSDictionary");  
// 创建NSDictionary的实例对象  
var ns = new NSDictionary();  

// Android平台导入Intent类  
var Intent = plus.android.importClass("android.content.Intent");  
// 创建Intent的实例对象  
var intent = new Intent();

获取实例对象后,可以通过实例对象“.”操作符获取对象的常量属性、调用对象的成员方法,实例对象的非常量属性则需通过plusGetAttribute、plusSetAttribute方法操作。

操作对象的属性方法

  • 常量属性
    获取对象后就可以通过“.”操作符获取对象的常量属性,如果是类对象则获取的是类的静态常量属性,如果是实例对象则获取的是对象的成员常量属性。

  • 非常量属性
    如果Native层对象的属性值在原生环境下被更改,此时使用“.”操作符获取到对应NJS对象的属性值就可能不是实时的属性值,而是该Native层对象被映射为NJS对象那一刻的属性值。
    为获取获取Native层对象的实时属性值,需调用NJS对象的plusGetAttribute(name)方法,参数name为属性的名称,返回值为属性的值。调用NJS对象的plusSetAttribute(name,value)方法设置Native层对象的非常量属性值,参数name为属性的名称,value为要设置新的属性值。
    注意:使用plusGetAttribute(name)方法也可以获取Native层对象的常量属性值,但不如直接使用“.”操作符来获取性能高。

  • 方法
    获取对象后可以通过“.”操作符直接调用Native层方法,如果是类对象调用的是Native层类的静态方法,如果是实例对象调用的是Native层对象的成员方法。
    注意:在iOS平台由于JS语法的原因,Objective-C方法名称中的“:”字符转成NJS对象的方法名称后将会被忽略,因此在NJS中调用的方法名需去掉所有“:”字符。

  • 类的继承
    Objective-C和Java中类如果存在继承自基类,在NJS中对应的对象会根据继承关系递归将所有基类的公有方法一一换成NJS对象的方法,所有基类的公有属性也可以通过其plusGetAttribute、plusSetAttribute方法访问。

开始写NJS

使用NJS调用Native API非常简单,基本步骤如下:

  1. 导入要使用到的类;
  2. 创建类的实例对象(或者调用类的静态方法创建);
  3. 调用实例对象的方法;

以下例子使用NJS调用iOS和Android的原生弹出提示框(类似但不同于js的alert)。

Android

以下代码在Android平台展示调用Native API显示系统提示框。
首先是Android原生 Java代码,用于比对参考:

import android.app.AlertDialog;  
//...  
// 创建提示框构造对象,Builder是AlertDialog的内部类。参数this指代Android的主Activity对象,该对象启动应用时自动生成  
AlertDialog.Builder dlg = new AlertDialog.Builder(this);  
// 设置提示框标题  
dlg.setTitle("自定义标题");  
// 设置提示框内容  
dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
// 设置提示框按钮  
dlg.setPositiveButton("确定(或者其他字符)", null);  
// 显示提示框  
dlg.show();  
//...

Native.js代码:

/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 导入AlertDialog类  
    var AlertDialog = plus.android.importClass("android.app.AlertDialog");  
    // 创建提示框构造对象,构造函数需要提供程序全局环境对象,通过plus.android.runtimeMainActivity()方法获取  
    var dlg = new AlertDialog.Builder(plus.android.runtimeMainActivity());  
    // 设置提示框标题  
    dlg.setTitle("自定义标题");  
    // 设置提示框内容  
    dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    dlg.setPositiveButton("确定(或者其他字符)",null);  
    // 显示提示框  
    dlg.show();  
}  
//...

注意:NJS代码中创建提示框构造对象要求传入程序全局环境对象,可通过plus.android.runtimeMainActivity()方法获取应用的主Activity对象,它是HTML5+应用运行期自动创建的程序全局环境对象。

Android设备上运行效果图:
Android Native.js示例运行效果图
`注意:其实HTML5+规范已经封装过原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此处NJS的示例仅为了开发者方便理解,实际使用时调用plus.ui.alert更简单,性能也更高。**

iOS

以下代码在iOS平台展示调用Native API显示系统提示对话框。
iOS原生Objective-C代码,用于比对参考:

#import <UIKit/UIKit.h>  
//...  
// 创建UIAlertView类的实例对象  
UIAlertView *view = [UIAlertView alloc];  
// 设置提示对话上的内容  
[view initWithTitle:@"自定义标题" // 提示框标题  
    message:@"使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
    delegate:nil // 点击提示框后的通知代理对象,nil类似js的null,意为不设置  
    cancelButtonTitle:@"确定(或者其他字符)" // 提示框上取消按钮的文字  
    otherButtonTitles:nil]; // 提示框上其它按钮的文字,设置为nil表示不显示  
// 调用show方法显示提示对话框,在OC中使用[]语法调用对象的方法  
[view show];  
//...

Native.js代码:

/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 导入UIAlertView类  
    var UIAlertView = plus.ios.importClass("UIAlertView");  
    // 创建UIAlertView类的实例对象  
    var view = new UIAlertView();  
    // 设置提示对话上的内容  
    view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框,在JS中使用()语法调用对象的方法  
    view.show();  
}  
//...

注意:在OC语法中方法的定义格式为: “(返回值类型) 函数名: (参数1类型) 形参1 参数2名称: (参数2类型) 形参2” 方法的完整名称为: “函数名:参数2名称:”。 如:“(void)setPositionX:(int)x Y:(int)y;”,方法的完整名称为“setPositionX:Y:” 调用时语法为:“[pos setPositionX:x Y:y];”。 在JS语法中函数名称不能包含“:”字符,所以OC对象的方法名映射成NJS对象方法名时将其中的“:”字符自动删除,上面方法名映射为“setPositionXY”,在NJS调用的语法为:“pos.setPositionXY(x,y);”。
iOS设备上运行效果图:
iOS Native.js示例运行效果图
`注意:其实HTML5+规范已经封装过原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此处NJS的示例仅为了开发者方便理解,实际使用时调用plus.ui.alert更简单、性能也更高。

在HBuilder自带的Hello H5+模板应用中“Native.JS”(plus/njs.html)页面有完整的源代码,可真机运行查看效果。

常用API

API on Android

为了能更好的理解NJS调用Java Native API,我们在Android平台用Java实现以下测试类,将在会后面API说明中的示例来调用。
文件NjsHello.java代码如下:

package io.dcloud;  

// 定义类NjsHello  
public class NjsHello {  
    // 静态常量  
    public static final int CTYPE = 1;  
    // 静态变量  
    public static int count;  
    // 成员常量  
    public final String BIRTHDAY = "2013-01-13";  
    // 成员变量  
    String name; //定义属性name  
    NjsHelloEvent observer;  
    public void updateName( String newname ) { //定义方法updateName  
        name = newname;  
    }  
    public void setEventObserver( NjsHelloEvent newobserver ) {  
        observer = newobserver;  
    }  
    public void test() { //定义方法test  
        System.out.printf( "My name is: %s", name );  
        observer.onEventInvoked( name );  
    }  
    public static void testCount() {  
        System.out.printf( "Static count is:%d", count );  
    }  
    static{  // 初始化类的静态变量  
        NjsHello.count = 0;  
    }  
}

文件NjsHelloEvent.java代码如下:

package io.dcloud;  

// 定义接口NjsHelloEvent  
public interface NjsHelloEvent {  
    public void onEventInvoked( String name );  
}

注:此NjsHello示例仅为了说明原生代码与NJS代码之间的映射关系,以下示例代码无法直接在HBuilder里真机运行,必须在以后HBuilder开放自定义打包后方可把NjsHello类打入app并被NJS调用。实际使用中,这种需要并非必要,大多数情况可以通过直接写NJS代码调用操作系统API,而无需由原生语言二次封装类供JS调用。

plus.android.importClass

导入Java类对象,方法原型如下:

ClassObject plus.android.importClass( String classname );

导入类对象后,就可以通过“.”操作符直接调用对象(类对象/实例对象)的常量和方法。
classname:要导入的Java类名,必须是完整的命名空间(使用"."分割),如果指定的类名不存在,则导入类失败,返回null。

注意:导入类对象可以方便的使用“.”操作符来调用对象的属性和方法,但也会消耗较多的系统资源。因此导入过多的类对象会影响性能,此时可以使用“高级API”中提供的方法在不导入类对象的情况下调用Native API。

示例:

  1. 导入类对象
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // ...

ClassObject

调用plus.android.importClass()方法导入类并返回ClassObject类对象,通过该类对象,可以创建类的实例对象。在Java中类的静态方法会转换成NJS类对象的方法,可通过类对象的“.”操作符调用;类的静态常量会转换为NJS类对象的属性,可通过类对象的“.”操作符访问;类的静态属性则需通过NJS类对象的plusGetAttribute、plusSetAttribute方法操作。
示例:

  1. 导入类后获取类的静态常量属性
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态常量属性  
    int type = NjsHello.CTYPE;  
    System.out.printf( "NjsHello Final's value: %d", type );  // 输出“NjsHello Final's value: 1”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 获取类的静态常量属性  
    var type = NjsHello.CTYPE;  
    console.log( "NjsHello Final's value: "+type ); // 输出“NjsHello Final's value: 1”  
    // ...
  2. 导入类后调用类的静态方法
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 调用类的静态方法  
    NjsHello.testCount();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 调用类的静态方法  
    NjsHello.testCount();  
    // ...
ClassObject.plusGetAttribute

获取类对象的静态属性值,方法原型如下:

Object classobject.plusGetAttribute( String name );

导入类对象后,就可以调用其plusGetAttribute方法获取类的静态属性值。

  • name:要获取的静态属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果导入的类对象中存在“plusGetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类后获取类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态属性  
    int count = NjsHello.count;  
    System.out.printf( "NjsHello Static's value: %d", count );  // 输出“NjsHello Static's value: 0”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 获取类的静态属性  
    var count = NjsHello.plusGetAttribute( "count" );  
    console.log( "NjsHello Static's value: "+count ); // 输出“NjsHello Static's value: 0”  
    // ...
ClassObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void classobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类后设置类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 设置类的静态属性值  
    NjsHello.count = 2;  
    System.out.printf( "NjsHello Static's value: %d", NjsHello.count );  // 输出“NjsHello Static's value: 2”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 设置类的静态属性值  
    NjsHello.plusSetAttribute( "count", 2 );  
    console.log( "NjsHello Static's value: "+NjsHello.plusGetAttribute( "count" ) ); // 输出“NjsHello Static's value: 2”  
    // ...

InstanceObject

NJS中实例对象与Java中的对象对应,调用plus.android.importClass()方法导入类后,通过new操作符可创建该类的实例对象,或直接调用plus.android.newObject方法创建类的实例对象,也可通过调用Native API返回实例对象。在Java中对象的方法会转换成NJS实例对象的方法,可通过实例对象的“.”操作符调用;对象的常量属性会转换NJS实例对象的属性,可通过实例对象的“.”操作符访问。对象的非常量属性则必须通过NJS实例对象的plusGetAttribute、plusSetAttribute方法操作。
示例:

  1. 导入类创建实例对象,调用对象的方法
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建NjsHello的实例对象  
    NjsHello hello = new NjsHello();  
    // 调用对象的方法  
    hello.updateName( "Tester" );  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // 调用对象的方法  
    hello.updateName( "Tester" );  
    // ...
  2. 导入类创建实例对象,获取对象的常量属性
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建NjsHello的实例对象  
    NjsHello hello = new NjsHello();  
    // 访问对象的常量属性  
    String birthday = hello.BIRTHDAY;  
    System.out.printf( "NjsHello Object Final's value: %s", birthday );  // 输出“NjsHello Object Final's value: 2013-01-13”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // 访问对象的常量属性  
    var birthday = hello.BIRTHDAY;  
    console.log( "NjsHello Object Final's value: "+birthday ); // 输出“NjsHello Object Final's value: 2013-01-13”  
    // ...
InstanceObject.plusGetAttribute

获取实例对象的属性值,方法原型如下:

Object instancebject.plusGetAttribute( String name );

获取实例对象后,就可以调用其plusGetAttribute方法获取对象的属性值。
name:要获取对象的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果实例对象中存在“plusGetAttribute”同名的方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,获取对象的属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    String name = hello.name;  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    var name = hello.plusGetAttribute( "name" );  
    console.log( "NjsHello Object's name: "+name );  // 输出“NjsHello Object's name: Tester”  
    // ...
InstanceObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void instanceobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,设置对象的属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 设置其name属性值  
    hello.name = "Tester";  
    System.out.printf( "NjsHello Object's name: %s", hello.name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var Hello = plus.android.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 设置其name属性值  
    hello.plusSetAttribute( "name", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.implements

在Java中可以通过定义新类并实现Interface的接口,并创建出新类对象作为其它接口的参数,在NJS中则可快速创建对应的Interface对象,方法原型如下:
Object plus.android.implements( String name, Object obj );

此方法创建Native层中Java的接口实现对象,作为调用其它Native API的参数。

  • name:接口的名称,必须是完整的命名空间(使用"."分割),如果不存在此接口,则创建接口实现对象失败,返回null。
  • obj:JSON对象类型,接口实现方法的定义,JSON对象中key值为接口方法的名称;value值为Function,方法参数必须与接口中方法定义的参数区配。

示例:

  1. Test类中实现接口NjsHelloEvent的方法,并调用NjsHello对象的test方法触发接口中函数的运行。
    Java代码:
    import io.dcloud.NjsHello;  
    import io.dcloud.NjsHelloEvent;  
    //...  
    // Test类实现NjsHelloEvent接口  
    public class Test implements NjsHelloEvent {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( this );  
    // 调用test方法,触发接口事件  
    hello.test(); // 触发onEventInvoked函数运行  
    //...  
    }  
    // 实现接口NjsHelloEvent的onEventInvoked方法  
    @Override  
    public void onEventInvoked( String name ) {  
    System.out.printf( "Invoked Object's name is: %s", name );  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 实现接口“NjsHelloEvent”对象  
    var hevent = plus.android.implements( "io.dcloud.NjsHelloEvent", {  
    "onEventInvoked":function( name ){  
        console.log( "Invoked Object’s name: "+name ); // 输出“Invoked Object’s name: Tester”  
    }  
    } );  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( hevent );  
    // 调用test方法,触发代理事件  
    hello.test(); // 触发上面定义的匿名函数运行  
    // ...

plus.android.runtimeMainActivity

获取运行期环境主Activity实例对象,方法原型如下:

InstanceObject plus.android.runtimeMainActivity();

此方法将获取程序的主Activity的实例对象,它是Html5+运行期环境主组件,用于处理与用户交互的各种事件,也是应用程序全局环境android.app.Activity的实现对象。android.app.Activity是一个特殊的类,需要在原生开发环境中注册后才能使用,所以使用new操作符创建对象无实际意义。

示例:

  1. 调用Activity的startActivity方法来拨打电话
    Java代码:
    import android.app.Activity;  
    import android.content.Intent;  
    import android.net.Uri;  
    //...  
    // 获取主Activity对象的实例  
    Activity main = context;  
    // 创建Intent  
    Uri uri = Uri.parse("tel:10086");  
    Intent call = new Intent("android.intent.action.CALL",uri);  
    // 调用startActivity方法拨打电话  
    main.startActivity(call);  
    //...

    NJS代码:

    // 导入Activity、Intent类  
    var Intent = plus.android.importClass("android.content.Intent");  
    var Uri = plus.android.importClass("android.net.Uri");  
    // 获取主Activity对象的实例  
    var main = plus.android.runtimeMainActivity();  
    // 创建Intent  
    var uri = Uri.parse("tel:10086");  
    var call = new Intent("android.intent.action.CALL",uri);  
    // 调用startActivity方法拨打电话  
    main.startActivity( call );  
    // ...

plus.android.currentWebview

获取当前Webview窗口对象的native层实例对象,方法原型如下:

InstanceObject plus.android.currentWebview();

Android平台完整Java类名为android.webkit.Webview,完整API请参考Android开发文档android.webkit.Webview

示例:

  1. 调用Webview的loadUrl方法跳转页面
    Java代码:
    import android.webkit.Webview;  
    //...  
    // 获取Webview对象  
    Webview wv = this;  
    // 跳转页面  
    wv.loadUrl("http://www.dcloud.io/");  
    //...

    NJS代码:

    // 导入Webview类  
    var Webview = plus.android.importClass("android.webkit.Webview");  
    // 当前Webview对象的实例  
    var wv = plus.android.currentWebview();  
    // 跳转页面  
    wv.loadUrl("http://www.dcloud.io/");  
    // ...

    完整API文档参考:HTML5+ API - Native.js for Android

API on iOS

为了能更好的理解NJS调用Objective-C Native API,我们在iOS平台用Objective-C实现以下测试类,将会在后面API说明中的示例来调用。
头文件njshello.h代码如下:

// 定义协议  
@protocol NjsHelloEvent <NSObject>  
@required  
-(void) onEventInvoked:(NSString*)name;  
@end  
// -------------------------------------------------------------  
// 定义类NjsHello  
@interface NjsHello : NSObject {  
    NSString *_name;  
    id<NjsHelloEvent > _delegate;  
}  
@property (nonatomic,retain) NSString *name;  
@property (nonatomic,retain) id delegate;  
-(void)updateName:(NSString*)newname;  
-(void)setEventObserver:(id<NjsHelloEvent >)delegate;  
-(void)test;  
+(void)testCount;  
@end

实现文件njshello.m源代码如下:

#import "njshello.h"  
// 实现类NjsHello  
@implementation NjsHello  
@synthesize name=_name;  
-(void)updateName:(NSString*)newname{  
    _name = [newname copy];  
}  
-(void)setEventObserver:(id<NjsHelloEvent >)delegate{  
    _delegate = delegate;  
}  
-(void)test{  
    NSLog(@"My name is: %@",_name);  
    [[self delegate]onEventInvoked:name];  
}  
-(void)dealloc{  
    [_name release];  
    [supper dealloc];  
}  
+(void)testCount{  
    NSLog( @"Static test count" );  
}  
@end

plus.ios.importClass

导入Objective-C类对象,方法原型如下:

ClassObject plus.ios.importClass( String classname );

导入类对象后,就可以通过“.”操作符直接调用对象(类对象/实例对象)的常量和方法。通过“.”操作符号调用方法时,不需要使用“:”来分割方法名。

  • classname:要导入的Objective-C类名,如果指定的类名不存在,则导入类失败,返回null。

注意:导入类对象可以方便的使用“.”操作符来调用对象的属性和方法,但也会消耗较多的系统资源。因此导入过多的类对象会影响性能,此时可以使用“高级API”中提供的方法在不导入类对象的情况下调用Native API。
示例:

  1. 导入类并创建实例对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // ...

ClassObject

调用plus.ios.importClass()方法导入类并返回ClassObject类对象,通过该类对象,可以创建类的实例对象。在Objective-C中类的静态方法会转换成NJS类对象的方法,可通过类对象的“.”操作符调用;

注意:由于Objective-C中类没有静态变量,而是通过定义全局变量来实现,目前NJS中无法访问全局变量的值。对于全局常量,在NJS中也无法访问,对于原类型常量可在文档中找到其具体的值,在JS代码中直接赋值;对于非原类型常量目前还无法访问。

示例:

  1. 导入类后调用类的静态方法
    Objective-C代码:
    #import "njshello.h"  
    // ...  
    int main( int argc, char *argv[] )  
    {  
    // 调用类的静态方法  
    [NjsHello testCount];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 调用类的静态方法  
    NjsHello.testCount();  
    // ...

InstanceObject

NJS中实例对象与Objective-C中的对象对应,调用plus.ios.importClass()方法导入类后,通过new操作符可创建该类的实例对象,或直接调用plus.ios.newObject方法创建类的实例对象,也可通过调用Native API返回实例对象。在Objective-C中对象的方法会转换成NJS实例对象的方法,可通过实例对象的“.”操作符调用;对象的属性则必须通过NJS实例对象的plusGetAttribute、plusSetAttribute方法操作。

示例:

  1. 导入类创建实例对象,调用对象的方法
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // ...
InstanceObject.plusGetAttribute

获取实例对象的属性值,方法原型如下:

Object instancebject.plusGetAttribute( String name );

获取实例对象后,就可以调用其plusGetAttribute方法获取对象的属性值。

  • name:要获取对象的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果实例对象中存在“plusGetAttribute”同名的方法,则只能通过plus.ios.invoke()方法调用。

示例:

  1. 导入类创建实例对象,获取对象的属性值
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    [hello updateName:@"Tester"];  
    // 获取其name属性值  
    NSString* name = hello.name;  
    NSLog(@"NjsHello Object's name: %@",name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    var name = hello.plusGetAttribute( "name" );  
    console.log( "NjsHello Object’s name: "+name );  // 输出“NjsHello Object’s name: Tester”  
    // ...
InstanceObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void instanceobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则只能通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,设置对象的属性值
    Java代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 设置其name属性值  
    hello.name = @"Tester";  
    NSLog(@"NjsHello Object's name: %@",hello.name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }  
    //...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 设置其name属性值  
    hello.plusSetAttribute( "name", "Tester" );  
    console.log( "NjsHello Object’s name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object’s name: Tester”  
    // ...

plus.ios.implements

在Objective-C中可以通过定义新类并实现Protocol的协议,并创建出新类对象作为代理对象,在NJS中则可实现协议快速创建代理对象,方法原型如下:

Object plus.ios.implements( String name, Object obj );

此方法返回一个NJS实例对象,映射到Native层中的代理对象,其父类为“NSObject”,并且实现obj中指定的协议方法。通常作为调用其它Native API的参数。

  • name:协议的名称,也可以是自定的字符串名称用于定义一个代理。
  • obj:JSON对象类型,代理实现方法的定义,JSON对象中key值为协议中定义的方法名称,必须保留方法名称中的“:”字符;value值为Function,方法参数必须与协议中定义方法的参数区配。

示例:

  1. 实现一个代理,并调用test方法触发调用代理的方法
    Objective-C代码:
    #import "njshello.h"  
    // 定义代理类NjsDelegate  
    @interface NjsDelegate: NSObject<NjsHelloEvent> {  
    -(void) onEventInvoked:(NSString*)name;  
    }  
    @end  
    // -------------------------------------------------------------  
    // 实现代理类NjsDelegate  
    @implementation NjsDelegate  
    -(void) onEventInvoked:(NSString*)name{  
    NSLog(@"Invoked Object's name:%@",name);  // 输出“Invoked Object’s name: Tester”  
    }  
    @end  
    // -------------------------------------------------------------  
    // 主函数  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    // 创建代理对象  
    NjsDelegate* delegate = [[NjsDelegate alloc] init];  
    // 设置监听对象  
    [hello setEventObserver:delegate];  
    // 调用test方法,触发代理事件  
    [hello test];  // 触发上面代理对象定义的onEventInvoked运行  
    // ...  
    }

    在NJS中不需要创建新的类对象,调用plus.ios.implements实现协议接口即可创建出代理对象,代码如下:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 实现协议“NjsHelloEvent”的代理  
    var hevent = plus.ios.implements( "NjsHelloEvent", {  
    "onEventInvoked:":function( name ){  
        console.log( "Invoked Object’s name: "+name ); // 输出“Invoked Object’s name: Tester”  
    }  
    } );  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( hevent );  
    // 调用test方法,触发代理事件  
    hello.test(); // 触发上面代理对象定义的匿名函数运行  
    // ...

plus.ios.deleteObject

释放NJS中实例对象中映射的Native对象,方法原型如下:

void plus.ios.deleteObject( Object obj );

NJS中所有的实例对象(InstanceObject)都可以通过此方法释放,会将Native层的对象使用的资源进行释放。

  • obj:要释放的实例对象,如果obj对象不是有效的实例对象,则不执行对象的是否资源操作。

注意:此方法是可选的,如果不调用此方法释放实例对象,则在页面关闭时会自动释放所有对象;若对象占用较多的系统资源,则在业务逻辑处理完成时应该主动调用此方法释放资源,以提到程序的运行效率。

示例:

  1. 创建实例对象使用完成后,显式操作销毁对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    // ...  
    // 使用完后销毁对象的实例  
    [hello release];  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // ...  
    // 使用完后销毁对象的实例  
    plus.ios.deleteObject( hello );

plus.ios.currentWebview

获取当前Webview窗口对象的native层UIWebview实例对象,方法原型如下:

InstanceObject plus.ios.currentWebview();

UIWebview对象的API请参考Apple开发文档UIWebview

示例:

  1. 创建实例对象使用完成后,显式操作销毁对象
    Objective-C代码:
    // 获取当前Webview对象的实例  
    UIWebview* wv=self;  
    // 创建请求对象  
    NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.dcloud.io/"]];  
    // 跳转页面  
    [web loadRequest:req];  
    // 释放对象  
    // 系统自动回收  
    // ...

    NJS代码:

    // 导入UIWebview、NSURLRequest、NSURL类  
    var Webview = plus.ios.importClass("UIWebview");  
    var NSURLRequest = plus.ios.import('NSURLRequest');  
    var NSURL = plus.ios.import('NSURL');  
    // 获取当前Webview对象的实例  
    var wv = plus.ios.currentWebview();  
    // 创建请求对象  
    var req = NSURLRequest.requestWithURL(NSURL.URLWithString('http://www.dcloud.io/'));  
    // 跳转页面  
    plus.ios.invoke(wv,"loadRequest:",req);  
    // 释放对象(可选)  
    plus.ios.deleteObject(req);  
    plus.ios.deleteObject(wv);  
    // ...

    完整API文档参考:HTML5+ API - Native.js for iOS

完整业务演示

Android

在Android手机桌面上创建快捷方式图标,这是原本只有原生程序才能实现的功能。即使使用Hybrid方案,也需要原生工程师来配合写插件。
下面我们演示如何直接使用js在Android手机桌面创建快捷方式,在HelloH5+应用中Native.JS页面中“Shortcut (Android)”可以查看运行效果。
这段代码是使用原生Java实现的创建快捷方式的代码,用于参考比对:

import android.app.Activity;  
import android.content.Intent;  
import android.graphics.BitmapFactory;  
import android.graphics.Bitmap;  
// 创建桌面快捷方式  
void createShortcut(){  
    // 获取主Activity  
    Activity main = this;  
    // 创建快捷方式意图  
    Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");  
    // 设置快捷方式的名称  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "HelloH5+");  
    // 设置不可重复创建  
    shortcut.putExtra("duplicate",false);  
    // 设置快捷方式图标  
    Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/icon.png");  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);  
    // 设置快捷方式启动执行动作  
    Intent action = new Intent(Intent.ACTION_MAIN);  
    action.setComponent( main.getComponentName() );  
    shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT, action );  
    // 广播创建快捷方式  
    main.sendBroadcast(shortcut);  
}

使用NJS实现时首先导入需要使用到的android.content.Intent、android.graphics.BitmapFactory类,按照Java代码中的方法对应转换成JavaScript代码。
其中快捷方式图标是通过解析本地png文件进行设置,在JavaScript中需要使用plus.io.* API转换成本地路径传递给Native API,完整代码如下:

var Intent=null,BitmapFactory=null;  
var main=null;  
document.addEventListener( "plusready", function() {//"plusready"事件触发时执行plus对象的方法  
    // ...  
    if ( plus.os.name == "Android" ) {  
        // 导入要用到的类对象  
        Intent = plus.android.importClass("android.content.Intent");  
        BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory");  
        // 获取主Activity  
        main = plus.android.runtimeMainActivity();  
    }  
}, false);  
/**  
 * 创建桌面快捷方式  
 */  
function createShortcut(){  
    // 创建快捷方式意图  
    var shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");  
    // 设置快捷方式的名称  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "测试快捷方式");  
    // 设置不可重复创建  
    shortcut.putExtra("duplicate",false);  
    // 设置快捷方式图标  
    var iconPath = plus.io.convertLocalFileSystemURL("/icon.png"); // 将相对路径资源转换成系统绝对路径  
    var bitmap = BitmapFactory.decodeFile(iconPath);  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON,bitmap);  
    // 设置快捷方式启动执行动作  
    var action = new Intent(Intent.ACTION_MAIN);  
    action.setClassName(main.getPackageName(), 'io.dcloud.PandoraEntry');  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT,action);  
    // 广播创建快捷方式  
    main.sendBroadcast(shortcut);  
    console.log( "桌面快捷方式已创建完成!" );  
}

注意:提交到云平台打包时需要添加Android权限才能在桌面创建快捷方式,在HBuilder工程中双击应用的“manifest.json”文件,切换到“代码视图”中在plus->distribute->google->permissions节点下添加权限数据:

"google": {  
    // ...  
    "permissions": [  
"<uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\"/>"  
    ]  
}

如下图所示:
manifest.json中Android权限 permissions

iOS

在iOS手机上登录game center,一个游戏中心服务,这是原本只有原生程序才能实现的功能。即使使用Hybrid方案,也需要原生工程师来配合写插件。
下面我们演示如何直接使用js在iOS手机上登录game center,在HelloH5+应用中Native.JS页面中的“Game Center (iOS)”可以查看运行效果。
注意手机未开通game center则无法登陆,请先点击iOS自带的game center进行配置。
这段代码是使用原生Objective-C实现的登录game center的代码,用于参考比对。原生Objective-C代码的头文件Test.h中代码如下:

@interface Test: NSObject  
// 游戏玩家登录状态监听函数  
- (void)authenticationChanged:(NSNotification*)notification;  
// 获取游戏玩家状态信息  
- (void)playerInformation:(GKPlayer *)player;  
// 登录到游戏中心  
- (void)loginGamecenter;  
// 停止监听登录游戏状态变化  
- (void)logoutGamecenter;  
@end  

实现文件Test.m中代码如下:  
@implementation Test  
// 游戏玩家登录状态监听函数  
- (void)authenticationChanged:(NSNotification*)notification  
{  
    // 获取游戏玩家共享实例对象  
    GKLocalPlayer *player = notification.object;  
    if ( player.isAuthenticated ) {  
        // 玩家已登录认证,获取玩家信息  
        [self playerInformation:player];  
    } else {  
        // 玩家未登录认证,提示用户登录  
        NSLog(@"请登录!");  
    }  
    // 释放使用的对象  
    [player release];  
}  
// 获取游戏玩家状态信息  
- (void)playerInformation:(GKPlayer *)player  
{  
    // 获取游戏玩家的名称  
    NSLog(@"Name: %@",player.displayName);  
}  

// 登录到游戏中心  
- (void)loginGamecenter  
{  
    // 监听用户登录状态变更事件  
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];  
    [nc addObserver:self  
           selector:@selector(authenticationChanged)  
               name:@"GKPlayerAuthenticationDidChangeNotificationName"  
             object:nil];  
    // 获取游戏玩家共享实例对象  
    GKLocalPlayer *localplayer = [GKLocalPlayer localPlayer];  
    // 判断游戏玩家是否已经登录认证  
    if ( localplayer.isAuthenticated ) {  
        // 玩家已登录认证,获取玩家信息  
        [self playerInformation:localplayer];  
    } else {  
        // 玩家未登录认证,发起认证请求  
        [localplayer authenticateWithCompletionHandler:nil];  
        NSLog(@"登录中...");  
    }  
    // 释放使用的对象  
    [localplayer release];  
    [nc release];  
}  

// 停止监听登录游戏状态变化  
- (void)logoutGamecenter  
{  
    // 取消监听用户登录状态变化  
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];  
    [nc removeObserver:self  
                  name:@"GKPlayerAuthenticationDidChangeNotificationName"  
                object:nil];  
    // 释放使用的对象  
    [nc release];  
}  
@end

使用NJS实现时可以按照Objective-C代码中的方法对应转换成JavaScript代码,最关键的代码是loginGamecenter方法中对用户登录状态的监听,需调用NSNotificationCenter对象的“addObserver:selector:name:object”方法,

  1. addObserver:后要求传入一个实例对象用于查找selector参数中指定的方法,在Objective-C中通常将对象自身(self)传入,但在NJS中没有此概念,因此需使用plus.ios.implements方法来创建一个新的对象:
    var delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged});
    第一个参数“NSObject”表示对象的类型,第二个参数中的JSON对象表明对象拥有的方法,“authenticationChanged”方法是delegate对象的方法。
  2. selector:后要传入一个类函数指针,在Objective-C中通过“@selector”指令可选择函数指针,在NJS中则需使用plus.ios.newObject方法来创建一个函数对象:
    plus.ios.newObject("@selector","authenticationChanged:")
    第一个参数需固定值为“@selector”,表示创建的是类函数指针对象,第二个参数。
    在"plusready"事件中导入GKLocalPlayer和NSNotificationCenter类,并调用登录方法longinGamecenter()。

完整JavaScript代码如下:

// 处理"plusready"事件  
var bLogin=false;  
document.addEventListener( "plusready", function() {  
    // ...  
    if ( plus.os.name == "iOS" ) {  
        GKLocalPlayer  = plus.ios.importClass("GKLocalPlayer");  
        NSNotificationCenter = plus.ios.importClass("NSNotificationCenter");  
        longinGamecenter();  
    } else {  
        alert("欢迎您");  
        bLogin = true;  
        setTimeout( function(){  
            plus.ui.toast( "此平台不支持Game Center功能!" );  
        }, 500 );  
    }  
}, false);  

var GKLocalPlayer=null,NSNotificationCenter=null;  
var delegate=null;  

// 游戏玩家登录状态监听函数  
function authenticationChanged( notification ){  
    // 获取游戏玩家共享实例对象  
    var player = notification.plusGetAttribute("object");  
    if ( player.plusGetAttribute("isAuthenticated") ) {  
        // 玩家已登录认证,获取玩家信息  
        playerInformation(player);  
        bLogin = true;  
    } else {  
        // 玩家未登录认证,提示用户登录  
        alert("请登录");  
        bLogin = false;  
    }  
    // 释放使用的对象  
    plus.ios.deleteObject(player);  
}  

// 获取游戏玩家状态信息  
function playerInformation( player ){  
    var name = player.plusGetAttribute("displayName");  
    alert( name+" 已登录!" );  
}  

// 登录到游戏中心  
function longinGamecenter(){  
    if ( bLogin ){  
        return;  
    }  
    // 监听用户登录状态变更事件  
    var nc = NSNotificationCenter.defaultCenter();  
    delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged});  
    nc.addObserverselectornameobject(delegate,  
        plus.ios.newObject("@selector","authenticationChanged:"),  
        "GKPlayerAuthenticationDidChangeNotificationName",  
        null);  
    // 获取游戏玩家共享实例对象  
    var localplayer = GKLocalPlayer.localPlayer();  
    // 判断游戏玩家是否已经登录认证  
    if ( localplayer.isAuthenticated() ) {  // localplayer.plusGetAttribute("isAuthenticated")  
        // 玩家已登录认证,获取玩家信息  
        playerInformation( localplayer );  
        bLogin = true;  
    } else {  
        // 玩家未登录认证,发起认证请求  
        localplayer.authenticateWithCompletionHandler(null);  
        alert( "登录中..." );  
    }  
    // 释放使用的对象  
    plus.ios.deleteObject(localplayer);  
    plus.ios.deleteObject(nc);  
}  

// 停止监听登录游戏状态变化  
function stopGamecenterObserver()  
{  
    // 取消监听用户登录状态变化  
    var nc = NSNotificationCenter.defaultCenter();  
    nc.removeObservernameobject(delegate,"GKPlayerAuthenticationDidChangeNotificationName",null);  
    plus.ios.deleteObject(nc);  
    plus.ios.deleteObject(delegate);  
    delegate = null;  
}

注意

  1. 提交到云平台打包时需要添加Game Center API的系统库(framework)才能正确调用,在HBuilder工程中双击应用的“manifest.json”文件,切换到“代码视图”中在plus->distribute->apple->frameworks节点下添加要引用的系统Framework:
    "apple": {  
    "devices": "universal",  
    "frameworks": [  
        "GameKit.framework"  
    ]  
    }

    ,如下图所示:
    HBuilder frameworks

  2. 正式发布提交到AppStore时,在配置苹果开发者网站上配置App ID需要选中“Game Center”服务:
    Game Center

开发注意和建议用途

Native.js的运行性能仍然不比纯原生应用;JS与Native之间的数据交换效率并不如在js内部的数据交换效率;基于如上原因,有几点开发建议:

  • 以标准web 代码为主,当遇到web能力不足的时候,调用Native.js。
  • 以标准web 代码为主,当遇到web性能不足的时候,需要分析,
    if ((原生进行运算的效率-js与原生通讯的损耗)>纯web的效率){
    使用Native.js
    }else{
    还应该使用纯js
    }
  • 应避免把程序设计为在短时间内并发触发Native.js代码

调试

使用safari和chrome的控制台调试HBuilder的5+App时,一样可以调试NJS对象,即可以在浏览器控制台中查看各种原生对象的属性和方法,如下图所示,57行设了断点,watch了Intent对象,并在右边展开了该对象的所有属性方法:
Chrome Debug
关于如何在浏览器控制台调试HBuilder的5+App,请参考HBuilder的5+App开发入门教程。

开发资源

iOS 官方在线文档:https://developer.apple.com/library/ios/navigation/
Android 官方在线文档:https://developer.android.com/reference/packages.html
演讲视频:http://v.youku.com/v_show/id_XNzYzNTcwNDI4.html

高级API

有前述的常用API,已经可以完成各项业务开发。此处补充的高级API,是在熟悉NJS后,为了提升性能而使用的API。高级API无法直接用“.”操作符使用原生对象的方法,在debug时也无法watch原生对象,但高级API性能高于常规API。
虽然导入类对象(plus.android.importClass和plus.ios.importClass)后,可以方便的通过“.”操作符来访问对象的常量、调用对象的方法,但导入类对象也需要消耗较多的系统资源,所以在实际开发时应该尽可能的减少导入类对象,以提高程序效率。可以参考以下依据进行判断:

  1. 如导入的类特别复杂,继承自很多基类,方法和属性特别多则考虑不导入类;
  2. 对导入类是否需要频繁操作,若导入类仅是为了实例化,并作为调用其它API的参数,则不应该导入类;
  3. 在同一页面中是否导入了很多类?如果导入太多则需要考虑减少导入类的数目。

如果我们不导入类对象则无法通过new操作符实例化类对象,这时可通过plus.ios.newObject()、plus.android.newObject()方法来创建实例对象,如下:

// iOS平台创建NSDictionary的实例对象  
var ns = plus.ios.newObject( "NSDictionary" );  

// Android平台创建Intent的实例对象  
var intent = plus.android.newObject( "android.content.Intent" );

API on Android

plus.android.newObject

不导入类对象直接创建类的实例对象,方法原型如下:

InstanceObject plus.android.newObject( String classname, Object...args );

此方法对Native层中对类进行实例化操作,创建一个类的实体并返回NJS层的实例对象。相比导入类对象后使用new操作符创建对象效率要高。

  • classname:要创建实例对象的类名,类名必须是完整的命名空间,使用“.”分隔符(如“android.app.AlertDialog”),如果需要创建内部类对象需要使用“$”分割符(如“android.app.AlertDialog$Builder”)。如果指定的类名不存在,则创建对象失败,返回null。
  • args:调用类构造函数的参数,其类型和数目必须与Native层Java类构造函数区配,否则无法创建类对象,将返回null。

注意:由于没有导入类对象,所以通过此方法创建的实例对象无法通过“.”操作符直接调用对象的方法,而必须使用plus.android.invoke方法来调用。

示例:

  1. 不导入类创建实例对象
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // ...

plus.android.getAttribute

不导入类对象,则无法通过类对象并访问类的静态属性,需调用以下方法获取类的静态属性值,方法原型如下:

Object plus.android.getAttribute( String|Object obj, String name );

此方法也可以获取类对象或实例对象的属性值,如果是类对象获取的则是类的静态属性,如果是实例对象则获取的是对象的非静态属性。

  • obj:若是String类型,表示要获取静态属性值的类名,类名必须是完整的命名空间(使用"."分割);若是ClassObject类型,表示要获取静态属性的类对象;若是InstanceObject类型,表示要获取属性值的实例对象。
  • name:要获取的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:同样导入类对象后也可以调用此方法,obj参数类型为ClassObject时,其作用与ClassObject.plusSetAttribute方法一致。obj参数类型为InstanceObject时,其作用与InstanceObject.plusSetAttribute方法一致。

示例:

  1. 不导入类对象获取类的静态常量属性
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态常量属性  
    int type = NjsHello.CTYPE;  
    System.out.printf( "NjsHello Final's value: %d", type );  // 输出“NjsHello Final's value: 1”  
    // 获取类的静态属性  
    int count = NjsHello.count;  
    System.out.printf( "NjsHello Static's value: %d", count );  // 输出“NjsHello Static's value: 0”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 访问类的静态常量属性  
    var type = plus.android.getAttribute( "io.dcloud.NjsHello", "CTYPE" );  
    console.log( "NjsHello Final's value: "+type ); // 输出“NjsHello Final's value: 1”  
    // ...
  2. 不导入类对象,创建实例对象,并获取其name属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 获取其name属性值  
    String name = hello.name;  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 获取其name属性值  
    var name = plus.android.getAttribute( hello, "name" );  
    console.log( "NjsHello Object's name: "+name );  // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.setAttribute

若没有导入类对象,则无法通过类对象设置类的静态属性值,需调用以下方法设置类的静态属性值,方法原型如下:

void plus.android.setAttribute( String|Object obj, String name, Object value );

此方法也可以设置类对象或实例对象的属性值,如果是类对象设置的则是类的静态属性,如果是实例对象则设置的是对象的非静态属性。

  • obj:若是String类型,表示要设置静态属性值的类名,类名必须是完整的命名空间(使用"."分割);若是ClassObject类型,表示要设置静态属性的类对象;若是InstanceObject类型,表示要设置属性值的实例对象。
  • name:要设置的属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层obj对象的属性区配,否则设置操作不生效,将保留以前的值。

注意:同样导入类对象后也可以调用此方法,obj参数类型为ClassObject时,其作用与ClassObject.plusSetAttribute方法一致。obj参数类型为InstanceObject时,其作用与InstanceObject.plusSetAttribute方法一致。

示例:

  1. 不导入类对象设置类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 设置类的静态属性值  
    NjsHello.count = 2;  
    System.out.printf( "NjsHello Static's value: %d", NjsHello.count );  // 输出“NjsHello Static's value: 2”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 设置类的静态属性值  
    plus.android.setAttribute( "io.dcloud.NjsHello", "count", 2 );  
    console.log( "NjsHello Static's value: "+plus.android.getAttribute("io.dcloud.NjsHello","count") ); // 输出“NjsHello Static's value: 2”  
    // ...
  2. 导入类对象,创建实例对象,并设置其name属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 设置其name属性值  
    hello.name = "Tester";  
    System.out.printf( "NjsHello Object's name: %s", hello.name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 设置其name属性值  
    plus.android.setAttribute( hello, "name", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.invoke

若没有导入类对象,则无法通过实例对象的“.”操作符调用其成员方法,需通过以下方法调用实例对象的成员方法,方法原型如下:

Object plus.android.invoke( String|Object obj, String name, Object... args );

此方法也可以调用类对象或实例对象的方法,如果是类对象则调用的是类的静态方法,如果是实例对象则调用的是对象的普通成员方法。函数返回值是调用Native层方法运行后的返回值,Native对象的方法无返回值则返回undefined。

  • obj:若是String类型,表示要调用静态方法的类名,类名必须包含完整的包名;若是ClassObject类型,表示要调用静态方法的类对象;若是InstanceObject类型,表示要调用成员方法的实例对象。
  • name:要调用的方法名称,如果指定的方法不存在,则调用方法失败,返回值为null。
  • args:调用方法的参数,其类型和数目必须与Native层对象方法的函数区配,否则无法调用对象的方法,将返回null。

注意:同样导入类对象后也可以调用此方法,其作用与通过类对象或实例对象的“.”操作符调用方法作用一致。

示例:
1.不导入类对象,调用类的静态方法
Java代码:

import io.dcloud.NjsHello;  
//...  
public class Test {  
public static void main( String args[] ) {  
    // 调用类的静态方法  
    NjsHello.testCount();  
    //...  
}  
//...  
}

NJS代码:

// 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
// 调用类的静态方法  
plus.android.invoke( "io.dcloud.NjsHello", "testCount" );  
// ...
  1. 不导入类对象,创建实例对象,并调用其updateNmae方法
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 调用updateName方法  
    plus.android.invoke( hello, "updateName", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

    完整API文档参考:HTML5+ API - Native.js for Android

API on iOS

plus.ios.newObject

不导入类对象直接创建类的实例对象,方法原型如下:

InstanceObject plus.ios.newObject( String classname, Object..args );

此方法会在Native层中对类进行实例化操作,创建一个类的实体并返回NJS层的类实例对象。相比导入类对象后使用new操作符创建对象效率要高。

  • classname:要创建实例对象的类名,如果指定的类名不存在,则创建对象失败,返回null。
  • args:调用类构造函数的参数,其类型和数目必须与Native层对象构造函数区配,否则无法创建类对象,将返回null。

注意:由于没有导入类对象,所以通过此方法创建的实例对象无法通过“.”操作符直接调用对象的方法,而必须使用plus.ios.invoke方法来调用。classname参数值为“@selector”表示需要创建一个函数指针对象,与Objective-C中的@selector指令功能相似,args参数为函数的名称,此时函数的名称需要包含“:”字符。

示例:

  1. 不导入类创建实例对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }

    NJS代码:

    // 未导入“NjsHello”类  
    // 创建对象的实例  
    var hello = plus.ios.newObject( "NjsHello" );  
    // ...

plus.ios.invoke

若没有导入类对象,则无法通过实例对象的“.”操作符调用其成员方法,需通过以下方法调用实例对象的成员方法,方法原型如下:

Object plus.ios.invoke( String|Object obj, String name, Object... args );

此方法也可以调用类对象或实例对象的方法,如果是类对象则调用的是类的静态方法,如果是实例对象则调用的是对象的普通成员方法。函数返回值是调用Native层方法运行后的返回值,Native对象的方法无返回值则返回undefined。

  • obj:若是String类型,表示要调用静态方法的类名,类名必须包含完整的包名;若是ClassObject类型,表示要调用静态方法的类对象;若是InstanceObject类型,表示要调用成员方法的实例对象。
  • name:要调用的方法名称,必须保留方法名称中的“:”字符,如果指定的方法不存在,则调用方法失败,返回值为null。
  • args:调用方法的参数,其类型和数目必须与Native层对象方法的函数区配,否则无法调用对象的方法,将返回null。

注意:同样导入类对象后也可以调用此方法,其作用与通过类对象或实例对象的“.”操作符调用方法作用一致。

示例:

  1. 不导入类创建实例对象,并调用updateName方法
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    NSLog(@"NjsHello Object's name: %@",hello.name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }

    NJS代码:

    // 未导入“NjsHello”类  
    // 创建对象的实例  
    var hello = plus.ios.newObject( "NjsHello" );  
    // 调用updateName方法  
    plus.ios.invoke( hello, "updateName", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

    完整API文档参考:HTML5+ API - Native.js for iOS

性能优化

调整代码结构优化

前面章节中我们介绍如何通过NJS调用Native API来显示系统提示框,在真机运行时会发现第一次调用时会有0.5s左右的延时,再次调用则不会延时。这是因为NJS中导入类对象操作会花费较长的时间,再次调用时由于类对象已经导入过,会能很快执行完毕。因此可以调整代码结构进行优化,在页面打开后触发的“plusready”事件中进行类对象的导入操作,从而避免第一次调用的延时。

Android平台调整NJS代码结构如下:

// 保存Android导入对象和全局环境对象  
var AlertDialog=null,mainActivity=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // 程序全局环境对象,内部自动导入Activity类  
        mainActivity = plus.android.runtimeMainActivity();  
        // 导入AlertDialog类  
        AlertDialog = plus.android.importClass("android.app.AlertDialog");  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 创建提示框构造对象,构造函数需要提供程序全局环境对象,通过plus.android.runtimeMainActivity()方法获取  
    var dlg = new AlertDialog.Builder(mainActivity);  
    // 设置提示框标题  
    dlg.setTitle("自定义标题");  
    // 设置提示框内容  
    dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    dlg.setPositiveButton("确定(或者其他字符)",null);  
    // 显示提示框  
    dlg.show();  
}  
//...

iOS平台调整NJS代码结构如下:

// 保存iOS平台导入的类对象  
var UIAlertView=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "iOS":  
        // 导入UIAlertView类  
        UIAlertView = plus.ios.importClass("UIAlertView");  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 创建UIAlertView类的实例对象  
    var view = new UIAlertView();  
    // 设置提示对话上的内容  
    view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框  
    view.show();  
}  
//...

使用高级API优化

前面章节中我们提到导入类对象会消耗较多的系统资源,导入过多的类对象会影响性能。在高级API中提供一组接口可以在不导入类对象的情况下调用Native API,从而提升代码运行性能。

Android平台使用高级API优化代码如下:

// 保存Android导入对象和全局环境对象  
var mainActivity=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // 程序全局环境对象,内部自动导入Activity类  
        mainActivity = plus.android.runtimeMainActivity();  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 由于Builder类是android.app.AlertDialog类的内部类,这里需要使用$符号分割  
    var dlg = plus.android.newObject("android.app.AlertDialog$Builder",mainActivity);  
    // 设置提示框标题  
    plus.android.invoke(dlg,"setTitle","自定义标题");  
    // 设置提示框内容  
    plus.android.invoke(dlg,"setMessage","使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    plus.android.invoke(dlg,"setPositiveButton","确定(或者其他字符)",null);  
    // 显示提示框  
    plus.android.invoke(dlg,"show");  
}  
//...

iOS平台使用高级API优化代码如下:

/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 创建UIAlertView类的实例对象  
    var view = plus.ios.newObject("UIAlertView");  
    // 设置提示对话上的内容,这里的方法名称中必须包含':'字符  
    plus.ios.invoke(view,"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"  
        ,"自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框,在JS中使用()语法调用对象的方法  
    plus.ios.invoke(view,"show");  
}  
//...
继续阅读 »

概述

Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术。
如果说Node.js把js扩展到服务器世界,那么Native.js则把js扩展到手机App的原生世界。
HTML/JS/Css全部语法只有7万多,而原生语法有几十万,Native.js大幅提升了HTML5的能力。
NJS突破了浏览器的功能限制,也不再需要像Hybrid那样由原生语言开发插件才能补足浏览器欠缺的功能。
NJS编写的代码,最终需要在HBuilder里打包发行为App安装包,或者在支持Native.js技术的浏览器里运行。目前Native.js技术不能在普通手机浏览器里直接运行。

  • NJS大幅扩展了HTML5的能力范围,原本只有原生或Hybrid App的原生插件才能实现的功能如今可以使用JS实现。
  • NJS大幅提升了App开发效率,将iOS、Android、Web的3个工程师组队才能完成的App,变为1个web工程师就搞定。
  • NJS不再需要配置原生开发和编译环境,调试、打包均在HBuilder里进行。没有mac和xcode一样可以开发iOS应用。
  • 如果不熟悉原生API也没关系,我们汇总了很多NJS的代码示例,复制粘贴就可以用。http://ask.dcloud.net.cn/article/114

再次强调,Native.js不是一个js库,不需要下载引入到页面的script中,也不像nodejs那样有单独的运行环境,Native.js的运行环境是集成在5+runtime里的,使用HBuilder打包的app或流应用都可以直接使用Native.js。

注意事项:

Uni-app不支Native.js执行UI相关操作的API调用及webview相关API调用。将失效无法正常使用。Uni-app不推荐使用Native.js

技术要求

由于NJS是直接调用Native API,需要对Native API有一定了解,知道所需要的功能调用了哪些原生API,能看懂原生代码并参考原生代码修改为JS代码。
否则只能直接copy别人写好的NJS代码。

开始使用

判断平台

Native API具有平台依赖性,所以需要通过以下方式判断当前的运行平台:

function judgePlatform(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // Android平台: plus.android.*  
        break;  
        case "iOS":  
        // iOS平台: plus.ios.*  
        break;  
        default:  
        // 其它平台  
        break;  
    }  
}

类型转换

在NJS中调用Native API或从Native API返回数据到NJS时会自动转换数据类型。

类型转换表

类型 Objective-C Java JavaScript
基本数据 byte/short/int/long/float/double/... byte/short/int/long/float/double/... Number
字符 char char String
字符串 NSString/@"" String/"" String
数组 @[1,2,3]/NSArray new XXX[] InstanceObject
@interface class ClassObject
对象(实例) * * InstanceObject
空对象 nil null null
其它 Protocol Interface Object(JSON)

其他转换

  • Android原生应用的主Activity对象 转为plus.android.runtimeMainActivity()
    Android的主Activity对象是启动应用时自动创建的,不是代码创建,此时通过plus.android.runtimeMainActivity()方法获取该Activity对象
  • Objective-C方法冒号剔除
    [pos setPositionX:(int)x Y:(int)y;] 转为 pos.setPositionXY(x,y);
    OC语法中方法的定义格式为:
    “(返回值类型) 函数名: (参数1类型) 形参1 参数2名称: (参数2类型) 形参2”
    方法的完整名称为: “函数名:参数2名称:”。
    如:“(void)setPositionX:(int)x Y:(int)y;”,方法的完整名称为“setPositionX:Y:”,调用时语法为:“[pos setPositionX:x Y:y];”。
    在JS语法中函数名称不能包含“:”字符,所以OC对象的方法名映射成NJS对象方法名时将其中的“:”字符自动删除,上面方法名映射为“setPositionXY”,在NJS调用的语法为:“pos.setPositionXY(x,y);”。
  • 文件路径转换
    Web开发里使用的image/1.png是该web工程的相对路径,而原生API中经常需要使用绝对路径,比如/sdcard/apptest/image/1.png,此时使用这个扩展方法来完成转换:plus.io.convertLocalFileSystemURL("image/1.png")

概念

类对象

由于JavaScript中本身没有类的概念,为了使用Native API层的类,在NJS中引入了类对象(ClassObject)的概念,用于对Native中的类进行操作,如创建类的实例对象、访问类的静态属性、调用类的静态方法等。其原型如下:

Interface ClassObject {  
    function Object plusGetAttribute( String name );  
    function void plusSetAttribute( String name, Object value );  
}

获取类对象
在iOS平台我们可以通过plus.ios.importClass(name)方法导入类对象,参数name为类的名称;在Android平台我们可以通过plus.android.importClass(name)方法导入类对象,其参数name为类的名称,必须包含完整的命名空间。

示例:

// iOS平台导入NSNotificationCenter类  
var NSNotificationCenter = plus.ios.importClass("NSNotificationCenter");  

// Android平台导入Intent类  
var Intent = plus.android.importClass("android.content.Intent");

获取类对象后,可以通过类对象“.”操作符获取类的静态常量属性、调用类的静态方法,类的静态非常量属性需通过plusGetAttribute、plusSetAttribute方法操作。

实例对象

在JavaScript中,所有对象都是Object,为了操作Native层类的实例对象,在NJS中引入了实例对象(InstanceObject)的概念,用于对Native中的对象进行操作,如操作对象的属性、调用对象的方法等。其原型如下:

Interface InstanceObject {  
    function Object plusGetAttribute( String name );  
    function void plusSetAttribute( String name, Object value );  
}

获取实例对象
有两种方式获取类的实例对象,一种是调用Native API返回值获取,另一种是通过new操作符来创建导入的类对象的实例,如下:

// iOS平台导入NSDictionary类  
var NSDictionary = plus.ios.importClass("NSDictionary");  
// 创建NSDictionary的实例对象  
var ns = new NSDictionary();  

// Android平台导入Intent类  
var Intent = plus.android.importClass("android.content.Intent");  
// 创建Intent的实例对象  
var intent = new Intent();

获取实例对象后,可以通过实例对象“.”操作符获取对象的常量属性、调用对象的成员方法,实例对象的非常量属性则需通过plusGetAttribute、plusSetAttribute方法操作。

操作对象的属性方法

  • 常量属性
    获取对象后就可以通过“.”操作符获取对象的常量属性,如果是类对象则获取的是类的静态常量属性,如果是实例对象则获取的是对象的成员常量属性。

  • 非常量属性
    如果Native层对象的属性值在原生环境下被更改,此时使用“.”操作符获取到对应NJS对象的属性值就可能不是实时的属性值,而是该Native层对象被映射为NJS对象那一刻的属性值。
    为获取获取Native层对象的实时属性值,需调用NJS对象的plusGetAttribute(name)方法,参数name为属性的名称,返回值为属性的值。调用NJS对象的plusSetAttribute(name,value)方法设置Native层对象的非常量属性值,参数name为属性的名称,value为要设置新的属性值。
    注意:使用plusGetAttribute(name)方法也可以获取Native层对象的常量属性值,但不如直接使用“.”操作符来获取性能高。

  • 方法
    获取对象后可以通过“.”操作符直接调用Native层方法,如果是类对象调用的是Native层类的静态方法,如果是实例对象调用的是Native层对象的成员方法。
    注意:在iOS平台由于JS语法的原因,Objective-C方法名称中的“:”字符转成NJS对象的方法名称后将会被忽略,因此在NJS中调用的方法名需去掉所有“:”字符。

  • 类的继承
    Objective-C和Java中类如果存在继承自基类,在NJS中对应的对象会根据继承关系递归将所有基类的公有方法一一换成NJS对象的方法,所有基类的公有属性也可以通过其plusGetAttribute、plusSetAttribute方法访问。

开始写NJS

使用NJS调用Native API非常简单,基本步骤如下:

  1. 导入要使用到的类;
  2. 创建类的实例对象(或者调用类的静态方法创建);
  3. 调用实例对象的方法;

以下例子使用NJS调用iOS和Android的原生弹出提示框(类似但不同于js的alert)。

Android

以下代码在Android平台展示调用Native API显示系统提示框。
首先是Android原生 Java代码,用于比对参考:

import android.app.AlertDialog;  
//...  
// 创建提示框构造对象,Builder是AlertDialog的内部类。参数this指代Android的主Activity对象,该对象启动应用时自动生成  
AlertDialog.Builder dlg = new AlertDialog.Builder(this);  
// 设置提示框标题  
dlg.setTitle("自定义标题");  
// 设置提示框内容  
dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
// 设置提示框按钮  
dlg.setPositiveButton("确定(或者其他字符)", null);  
// 显示提示框  
dlg.show();  
//...

Native.js代码:

/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 导入AlertDialog类  
    var AlertDialog = plus.android.importClass("android.app.AlertDialog");  
    // 创建提示框构造对象,构造函数需要提供程序全局环境对象,通过plus.android.runtimeMainActivity()方法获取  
    var dlg = new AlertDialog.Builder(plus.android.runtimeMainActivity());  
    // 设置提示框标题  
    dlg.setTitle("自定义标题");  
    // 设置提示框内容  
    dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    dlg.setPositiveButton("确定(或者其他字符)",null);  
    // 显示提示框  
    dlg.show();  
}  
//...

注意:NJS代码中创建提示框构造对象要求传入程序全局环境对象,可通过plus.android.runtimeMainActivity()方法获取应用的主Activity对象,它是HTML5+应用运行期自动创建的程序全局环境对象。

Android设备上运行效果图:
Android Native.js示例运行效果图
`注意:其实HTML5+规范已经封装过原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此处NJS的示例仅为了开发者方便理解,实际使用时调用plus.ui.alert更简单,性能也更高。**

iOS

以下代码在iOS平台展示调用Native API显示系统提示对话框。
iOS原生Objective-C代码,用于比对参考:

#import <UIKit/UIKit.h>  
//...  
// 创建UIAlertView类的实例对象  
UIAlertView *view = [UIAlertView alloc];  
// 设置提示对话上的内容  
[view initWithTitle:@"自定义标题" // 提示框标题  
    message:@"使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
    delegate:nil // 点击提示框后的通知代理对象,nil类似js的null,意为不设置  
    cancelButtonTitle:@"确定(或者其他字符)" // 提示框上取消按钮的文字  
    otherButtonTitles:nil]; // 提示框上其它按钮的文字,设置为nil表示不显示  
// 调用show方法显示提示对话框,在OC中使用[]语法调用对象的方法  
[view show];  
//...

Native.js代码:

/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 导入UIAlertView类  
    var UIAlertView = plus.ios.importClass("UIAlertView");  
    // 创建UIAlertView类的实例对象  
    var view = new UIAlertView();  
    // 设置提示对话上的内容  
    view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框,在JS中使用()语法调用对象的方法  
    view.show();  
}  
//...

注意:在OC语法中方法的定义格式为: “(返回值类型) 函数名: (参数1类型) 形参1 参数2名称: (参数2类型) 形参2” 方法的完整名称为: “函数名:参数2名称:”。 如:“(void)setPositionX:(int)x Y:(int)y;”,方法的完整名称为“setPositionX:Y:” 调用时语法为:“[pos setPositionX:x Y:y];”。 在JS语法中函数名称不能包含“:”字符,所以OC对象的方法名映射成NJS对象方法名时将其中的“:”字符自动删除,上面方法名映射为“setPositionXY”,在NJS调用的语法为:“pos.setPositionXY(x,y);”。
iOS设备上运行效果图:
iOS Native.js示例运行效果图
`注意:其实HTML5+规范已经封装过原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此处NJS的示例仅为了开发者方便理解,实际使用时调用plus.ui.alert更简单、性能也更高。

在HBuilder自带的Hello H5+模板应用中“Native.JS”(plus/njs.html)页面有完整的源代码,可真机运行查看效果。

常用API

API on Android

为了能更好的理解NJS调用Java Native API,我们在Android平台用Java实现以下测试类,将在会后面API说明中的示例来调用。
文件NjsHello.java代码如下:

package io.dcloud;  

// 定义类NjsHello  
public class NjsHello {  
    // 静态常量  
    public static final int CTYPE = 1;  
    // 静态变量  
    public static int count;  
    // 成员常量  
    public final String BIRTHDAY = "2013-01-13";  
    // 成员变量  
    String name; //定义属性name  
    NjsHelloEvent observer;  
    public void updateName( String newname ) { //定义方法updateName  
        name = newname;  
    }  
    public void setEventObserver( NjsHelloEvent newobserver ) {  
        observer = newobserver;  
    }  
    public void test() { //定义方法test  
        System.out.printf( "My name is: %s", name );  
        observer.onEventInvoked( name );  
    }  
    public static void testCount() {  
        System.out.printf( "Static count is:%d", count );  
    }  
    static{  // 初始化类的静态变量  
        NjsHello.count = 0;  
    }  
}

文件NjsHelloEvent.java代码如下:

package io.dcloud;  

// 定义接口NjsHelloEvent  
public interface NjsHelloEvent {  
    public void onEventInvoked( String name );  
}

注:此NjsHello示例仅为了说明原生代码与NJS代码之间的映射关系,以下示例代码无法直接在HBuilder里真机运行,必须在以后HBuilder开放自定义打包后方可把NjsHello类打入app并被NJS调用。实际使用中,这种需要并非必要,大多数情况可以通过直接写NJS代码调用操作系统API,而无需由原生语言二次封装类供JS调用。

plus.android.importClass

导入Java类对象,方法原型如下:

ClassObject plus.android.importClass( String classname );

导入类对象后,就可以通过“.”操作符直接调用对象(类对象/实例对象)的常量和方法。
classname:要导入的Java类名,必须是完整的命名空间(使用"."分割),如果指定的类名不存在,则导入类失败,返回null。

注意:导入类对象可以方便的使用“.”操作符来调用对象的属性和方法,但也会消耗较多的系统资源。因此导入过多的类对象会影响性能,此时可以使用“高级API”中提供的方法在不导入类对象的情况下调用Native API。

示例:

  1. 导入类对象
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // ...

ClassObject

调用plus.android.importClass()方法导入类并返回ClassObject类对象,通过该类对象,可以创建类的实例对象。在Java中类的静态方法会转换成NJS类对象的方法,可通过类对象的“.”操作符调用;类的静态常量会转换为NJS类对象的属性,可通过类对象的“.”操作符访问;类的静态属性则需通过NJS类对象的plusGetAttribute、plusSetAttribute方法操作。
示例:

  1. 导入类后获取类的静态常量属性
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态常量属性  
    int type = NjsHello.CTYPE;  
    System.out.printf( "NjsHello Final's value: %d", type );  // 输出“NjsHello Final's value: 1”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 获取类的静态常量属性  
    var type = NjsHello.CTYPE;  
    console.log( "NjsHello Final's value: "+type ); // 输出“NjsHello Final's value: 1”  
    // ...
  2. 导入类后调用类的静态方法
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 调用类的静态方法  
    NjsHello.testCount();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 调用类的静态方法  
    NjsHello.testCount();  
    // ...
ClassObject.plusGetAttribute

获取类对象的静态属性值,方法原型如下:

Object classobject.plusGetAttribute( String name );

导入类对象后,就可以调用其plusGetAttribute方法获取类的静态属性值。

  • name:要获取的静态属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果导入的类对象中存在“plusGetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类后获取类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态属性  
    int count = NjsHello.count;  
    System.out.printf( "NjsHello Static's value: %d", count );  // 输出“NjsHello Static's value: 0”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 获取类的静态属性  
    var count = NjsHello.plusGetAttribute( "count" );  
    console.log( "NjsHello Static's value: "+count ); // 输出“NjsHello Static's value: 0”  
    // ...
ClassObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void classobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类后设置类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 设置类的静态属性值  
    NjsHello.count = 2;  
    System.out.printf( "NjsHello Static's value: %d", NjsHello.count );  // 输出“NjsHello Static's value: 2”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 设置类的静态属性值  
    NjsHello.plusSetAttribute( "count", 2 );  
    console.log( "NjsHello Static's value: "+NjsHello.plusGetAttribute( "count" ) ); // 输出“NjsHello Static's value: 2”  
    // ...

InstanceObject

NJS中实例对象与Java中的对象对应,调用plus.android.importClass()方法导入类后,通过new操作符可创建该类的实例对象,或直接调用plus.android.newObject方法创建类的实例对象,也可通过调用Native API返回实例对象。在Java中对象的方法会转换成NJS实例对象的方法,可通过实例对象的“.”操作符调用;对象的常量属性会转换NJS实例对象的属性,可通过实例对象的“.”操作符访问。对象的非常量属性则必须通过NJS实例对象的plusGetAttribute、plusSetAttribute方法操作。
示例:

  1. 导入类创建实例对象,调用对象的方法
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建NjsHello的实例对象  
    NjsHello hello = new NjsHello();  
    // 调用对象的方法  
    hello.updateName( "Tester" );  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // 调用对象的方法  
    hello.updateName( "Tester" );  
    // ...
  2. 导入类创建实例对象,获取对象的常量属性
    Java代码:

    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建NjsHello的实例对象  
    NjsHello hello = new NjsHello();  
    // 访问对象的常量属性  
    String birthday = hello.BIRTHDAY;  
    System.out.printf( "NjsHello Object Final's value: %s", birthday );  // 输出“NjsHello Object Final's value: 2013-01-13”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建NjsHello的实例对象  
    var hello = new NjsHello();  
    // 访问对象的常量属性  
    var birthday = hello.BIRTHDAY;  
    console.log( "NjsHello Object Final's value: "+birthday ); // 输出“NjsHello Object Final's value: 2013-01-13”  
    // ...
InstanceObject.plusGetAttribute

获取实例对象的属性值,方法原型如下:

Object instancebject.plusGetAttribute( String name );

获取实例对象后,就可以调用其plusGetAttribute方法获取对象的属性值。
name:要获取对象的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果实例对象中存在“plusGetAttribute”同名的方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,获取对象的属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    String name = hello.name;  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    var name = hello.plusGetAttribute( "name" );  
    console.log( "NjsHello Object's name: "+name );  // 输出“NjsHello Object's name: Tester”  
    // ...
InstanceObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void instanceobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则必须通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,设置对象的属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 设置其name属性值  
    hello.name = "Tester";  
    System.out.printf( "NjsHello Object's name: %s", hello.name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var Hello = plus.android.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 设置其name属性值  
    hello.plusSetAttribute( "name", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.implements

在Java中可以通过定义新类并实现Interface的接口,并创建出新类对象作为其它接口的参数,在NJS中则可快速创建对应的Interface对象,方法原型如下:
Object plus.android.implements( String name, Object obj );

此方法创建Native层中Java的接口实现对象,作为调用其它Native API的参数。

  • name:接口的名称,必须是完整的命名空间(使用"."分割),如果不存在此接口,则创建接口实现对象失败,返回null。
  • obj:JSON对象类型,接口实现方法的定义,JSON对象中key值为接口方法的名称;value值为Function,方法参数必须与接口中方法定义的参数区配。

示例:

  1. Test类中实现接口NjsHelloEvent的方法,并调用NjsHello对象的test方法触发接口中函数的运行。
    Java代码:
    import io.dcloud.NjsHello;  
    import io.dcloud.NjsHelloEvent;  
    //...  
    // Test类实现NjsHelloEvent接口  
    public class Test implements NjsHelloEvent {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( this );  
    // 调用test方法,触发接口事件  
    hello.test(); // 触发onEventInvoked函数运行  
    //...  
    }  
    // 实现接口NjsHelloEvent的onEventInvoked方法  
    @Override  
    public void onEventInvoked( String name ) {  
    System.out.printf( "Invoked Object's name is: %s", name );  
    }  
    //...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.android.importClass("io.dcloud.NjsHello");  
    // 实现接口“NjsHelloEvent”对象  
    var hevent = plus.android.implements( "io.dcloud.NjsHelloEvent", {  
    "onEventInvoked":function( name ){  
        console.log( "Invoked Object’s name: "+name ); // 输出“Invoked Object’s name: Tester”  
    }  
    } );  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( hevent );  
    // 调用test方法,触发代理事件  
    hello.test(); // 触发上面定义的匿名函数运行  
    // ...

plus.android.runtimeMainActivity

获取运行期环境主Activity实例对象,方法原型如下:

InstanceObject plus.android.runtimeMainActivity();

此方法将获取程序的主Activity的实例对象,它是Html5+运行期环境主组件,用于处理与用户交互的各种事件,也是应用程序全局环境android.app.Activity的实现对象。android.app.Activity是一个特殊的类,需要在原生开发环境中注册后才能使用,所以使用new操作符创建对象无实际意义。

示例:

  1. 调用Activity的startActivity方法来拨打电话
    Java代码:
    import android.app.Activity;  
    import android.content.Intent;  
    import android.net.Uri;  
    //...  
    // 获取主Activity对象的实例  
    Activity main = context;  
    // 创建Intent  
    Uri uri = Uri.parse("tel:10086");  
    Intent call = new Intent("android.intent.action.CALL",uri);  
    // 调用startActivity方法拨打电话  
    main.startActivity(call);  
    //...

    NJS代码:

    // 导入Activity、Intent类  
    var Intent = plus.android.importClass("android.content.Intent");  
    var Uri = plus.android.importClass("android.net.Uri");  
    // 获取主Activity对象的实例  
    var main = plus.android.runtimeMainActivity();  
    // 创建Intent  
    var uri = Uri.parse("tel:10086");  
    var call = new Intent("android.intent.action.CALL",uri);  
    // 调用startActivity方法拨打电话  
    main.startActivity( call );  
    // ...

plus.android.currentWebview

获取当前Webview窗口对象的native层实例对象,方法原型如下:

InstanceObject plus.android.currentWebview();

Android平台完整Java类名为android.webkit.Webview,完整API请参考Android开发文档android.webkit.Webview

示例:

  1. 调用Webview的loadUrl方法跳转页面
    Java代码:
    import android.webkit.Webview;  
    //...  
    // 获取Webview对象  
    Webview wv = this;  
    // 跳转页面  
    wv.loadUrl("http://www.dcloud.io/");  
    //...

    NJS代码:

    // 导入Webview类  
    var Webview = plus.android.importClass("android.webkit.Webview");  
    // 当前Webview对象的实例  
    var wv = plus.android.currentWebview();  
    // 跳转页面  
    wv.loadUrl("http://www.dcloud.io/");  
    // ...

    完整API文档参考:HTML5+ API - Native.js for Android

API on iOS

为了能更好的理解NJS调用Objective-C Native API,我们在iOS平台用Objective-C实现以下测试类,将会在后面API说明中的示例来调用。
头文件njshello.h代码如下:

// 定义协议  
@protocol NjsHelloEvent <NSObject>  
@required  
-(void) onEventInvoked:(NSString*)name;  
@end  
// -------------------------------------------------------------  
// 定义类NjsHello  
@interface NjsHello : NSObject {  
    NSString *_name;  
    id<NjsHelloEvent > _delegate;  
}  
@property (nonatomic,retain) NSString *name;  
@property (nonatomic,retain) id delegate;  
-(void)updateName:(NSString*)newname;  
-(void)setEventObserver:(id<NjsHelloEvent >)delegate;  
-(void)test;  
+(void)testCount;  
@end

实现文件njshello.m源代码如下:

#import "njshello.h"  
// 实现类NjsHello  
@implementation NjsHello  
@synthesize name=_name;  
-(void)updateName:(NSString*)newname{  
    _name = [newname copy];  
}  
-(void)setEventObserver:(id<NjsHelloEvent >)delegate{  
    _delegate = delegate;  
}  
-(void)test{  
    NSLog(@"My name is: %@",_name);  
    [[self delegate]onEventInvoked:name];  
}  
-(void)dealloc{  
    [_name release];  
    [supper dealloc];  
}  
+(void)testCount{  
    NSLog( @"Static test count" );  
}  
@end

plus.ios.importClass

导入Objective-C类对象,方法原型如下:

ClassObject plus.ios.importClass( String classname );

导入类对象后,就可以通过“.”操作符直接调用对象(类对象/实例对象)的常量和方法。通过“.”操作符号调用方法时,不需要使用“:”来分割方法名。

  • classname:要导入的Objective-C类名,如果指定的类名不存在,则导入类失败,返回null。

注意:导入类对象可以方便的使用“.”操作符来调用对象的属性和方法,但也会消耗较多的系统资源。因此导入过多的类对象会影响性能,此时可以使用“高级API”中提供的方法在不导入类对象的情况下调用Native API。
示例:

  1. 导入类并创建实例对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // ...

ClassObject

调用plus.ios.importClass()方法导入类并返回ClassObject类对象,通过该类对象,可以创建类的实例对象。在Objective-C中类的静态方法会转换成NJS类对象的方法,可通过类对象的“.”操作符调用;

注意:由于Objective-C中类没有静态变量,而是通过定义全局变量来实现,目前NJS中无法访问全局变量的值。对于全局常量,在NJS中也无法访问,对于原类型常量可在文档中找到其具体的值,在JS代码中直接赋值;对于非原类型常量目前还无法访问。

示例:

  1. 导入类后调用类的静态方法
    Objective-C代码:
    #import "njshello.h"  
    // ...  
    int main( int argc, char *argv[] )  
    {  
    // 调用类的静态方法  
    [NjsHello testCount];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 调用类的静态方法  
    NjsHello.testCount();  
    // ...

InstanceObject

NJS中实例对象与Objective-C中的对象对应,调用plus.ios.importClass()方法导入类后,通过new操作符可创建该类的实例对象,或直接调用plus.ios.newObject方法创建类的实例对象,也可通过调用Native API返回实例对象。在Objective-C中对象的方法会转换成NJS实例对象的方法,可通过实例对象的“.”操作符调用;对象的属性则必须通过NJS实例对象的plusGetAttribute、plusSetAttribute方法操作。

示例:

  1. 导入类创建实例对象,调用对象的方法
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }  
    // ...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // ...
InstanceObject.plusGetAttribute

获取实例对象的属性值,方法原型如下:

Object instancebject.plusGetAttribute( String name );

获取实例对象后,就可以调用其plusGetAttribute方法获取对象的属性值。

  • name:要获取对象的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:如果实例对象中存在“plusGetAttribute”同名的方法,则只能通过plus.ios.invoke()方法调用。

示例:

  1. 导入类创建实例对象,获取对象的属性值
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    [hello updateName:@"Tester"];  
    // 获取其name属性值  
    NSString* name = hello.name;  
    NSLog(@"NjsHello Object's name: %@",name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    hello.updateName( "Tester" );  
    // 获取其name属性值  
    var name = hello.plusGetAttribute( "name" );  
    console.log( "NjsHello Object’s name: "+name );  // 输出“NjsHello Object’s name: Tester”  
    // ...
InstanceObject.plusSetAttribute

设置类对象的静态属性值,方法原型如下:

void instanceobject.plusSetAttribute( String name, Object value );

导入类对象后,就可以调用其plusSetAttribute方法设置类的静态属性值。

  • name:要设置的静态属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层类对象的静态属性区配,否则设置操作不生效,将保留以前的值。

注意:如果导入的类对象中存在“plusSetAttribute”同名的静态方法,则只能通过plus.android.invoke()方法调用。

示例:

  1. 导入类创建实例对象,设置对象的属性值
    Java代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 设置其name属性值  
    hello.name = @"Tester";  
    NSLog(@"NjsHello Object's name: %@",hello.name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }  
    //...

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 设置其name属性值  
    hello.plusSetAttribute( "name", "Tester" );  
    console.log( "NjsHello Object’s name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object’s name: Tester”  
    // ...

plus.ios.implements

在Objective-C中可以通过定义新类并实现Protocol的协议,并创建出新类对象作为代理对象,在NJS中则可实现协议快速创建代理对象,方法原型如下:

Object plus.ios.implements( String name, Object obj );

此方法返回一个NJS实例对象,映射到Native层中的代理对象,其父类为“NSObject”,并且实现obj中指定的协议方法。通常作为调用其它Native API的参数。

  • name:协议的名称,也可以是自定的字符串名称用于定义一个代理。
  • obj:JSON对象类型,代理实现方法的定义,JSON对象中key值为协议中定义的方法名称,必须保留方法名称中的“:”字符;value值为Function,方法参数必须与协议中定义方法的参数区配。

示例:

  1. 实现一个代理,并调用test方法触发调用代理的方法
    Objective-C代码:
    #import "njshello.h"  
    // 定义代理类NjsDelegate  
    @interface NjsDelegate: NSObject<NjsHelloEvent> {  
    -(void) onEventInvoked:(NSString*)name;  
    }  
    @end  
    // -------------------------------------------------------------  
    // 实现代理类NjsDelegate  
    @implementation NjsDelegate  
    -(void) onEventInvoked:(NSString*)name{  
    NSLog(@"Invoked Object's name:%@",name);  // 输出“Invoked Object’s name: Tester”  
    }  
    @end  
    // -------------------------------------------------------------  
    // 主函数  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    // 创建代理对象  
    NjsDelegate* delegate = [[NjsDelegate alloc] init];  
    // 设置监听对象  
    [hello setEventObserver:delegate];  
    // 调用test方法,触发代理事件  
    [hello test];  // 触发上面代理对象定义的onEventInvoked运行  
    // ...  
    }

    在NJS中不需要创建新的类对象,调用plus.ios.implements实现协议接口即可创建出代理对象,代码如下:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 实现协议“NjsHelloEvent”的代理  
    var hevent = plus.ios.implements( "NjsHelloEvent", {  
    "onEventInvoked:":function( name ){  
        console.log( "Invoked Object’s name: "+name ); // 输出“Invoked Object’s name: Tester”  
    }  
    } );  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // 设置监听对象  
    hello.setEventObserver( hevent );  
    // 调用test方法,触发代理事件  
    hello.test(); // 触发上面代理对象定义的匿名函数运行  
    // ...

plus.ios.deleteObject

释放NJS中实例对象中映射的Native对象,方法原型如下:

void plus.ios.deleteObject( Object obj );

NJS中所有的实例对象(InstanceObject)都可以通过此方法释放,会将Native层的对象使用的资源进行释放。

  • obj:要释放的实例对象,如果obj对象不是有效的实例对象,则不执行对象的是否资源操作。

注意:此方法是可选的,如果不调用此方法释放实例对象,则在页面关闭时会自动释放所有对象;若对象占用较多的系统资源,则在业务逻辑处理完成时应该主动调用此方法释放资源,以提到程序的运行效率。

示例:

  1. 创建实例对象使用完成后,显式操作销毁对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    // ...  
    // 使用完后销毁对象的实例  
    [hello release];  
    }

    NJS代码:

    // 导入测试类NjsHello  
    var NjsHello = plus.ios.importClass("NjsHello");  
    // 创建对象的实例  
    var hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    // ...  
    // 使用完后销毁对象的实例  
    plus.ios.deleteObject( hello );

plus.ios.currentWebview

获取当前Webview窗口对象的native层UIWebview实例对象,方法原型如下:

InstanceObject plus.ios.currentWebview();

UIWebview对象的API请参考Apple开发文档UIWebview

示例:

  1. 创建实例对象使用完成后,显式操作销毁对象
    Objective-C代码:
    // 获取当前Webview对象的实例  
    UIWebview* wv=self;  
    // 创建请求对象  
    NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.dcloud.io/"]];  
    // 跳转页面  
    [web loadRequest:req];  
    // 释放对象  
    // 系统自动回收  
    // ...

    NJS代码:

    // 导入UIWebview、NSURLRequest、NSURL类  
    var Webview = plus.ios.importClass("UIWebview");  
    var NSURLRequest = plus.ios.import('NSURLRequest');  
    var NSURL = plus.ios.import('NSURL');  
    // 获取当前Webview对象的实例  
    var wv = plus.ios.currentWebview();  
    // 创建请求对象  
    var req = NSURLRequest.requestWithURL(NSURL.URLWithString('http://www.dcloud.io/'));  
    // 跳转页面  
    plus.ios.invoke(wv,"loadRequest:",req);  
    // 释放对象(可选)  
    plus.ios.deleteObject(req);  
    plus.ios.deleteObject(wv);  
    // ...

    完整API文档参考:HTML5+ API - Native.js for iOS

完整业务演示

Android

在Android手机桌面上创建快捷方式图标,这是原本只有原生程序才能实现的功能。即使使用Hybrid方案,也需要原生工程师来配合写插件。
下面我们演示如何直接使用js在Android手机桌面创建快捷方式,在HelloH5+应用中Native.JS页面中“Shortcut (Android)”可以查看运行效果。
这段代码是使用原生Java实现的创建快捷方式的代码,用于参考比对:

import android.app.Activity;  
import android.content.Intent;  
import android.graphics.BitmapFactory;  
import android.graphics.Bitmap;  
// 创建桌面快捷方式  
void createShortcut(){  
    // 获取主Activity  
    Activity main = this;  
    // 创建快捷方式意图  
    Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");  
    // 设置快捷方式的名称  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "HelloH5+");  
    // 设置不可重复创建  
    shortcut.putExtra("duplicate",false);  
    // 设置快捷方式图标  
    Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/icon.png");  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);  
    // 设置快捷方式启动执行动作  
    Intent action = new Intent(Intent.ACTION_MAIN);  
    action.setComponent( main.getComponentName() );  
    shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT, action );  
    // 广播创建快捷方式  
    main.sendBroadcast(shortcut);  
}

使用NJS实现时首先导入需要使用到的android.content.Intent、android.graphics.BitmapFactory类,按照Java代码中的方法对应转换成JavaScript代码。
其中快捷方式图标是通过解析本地png文件进行设置,在JavaScript中需要使用plus.io.* API转换成本地路径传递给Native API,完整代码如下:

var Intent=null,BitmapFactory=null;  
var main=null;  
document.addEventListener( "plusready", function() {//"plusready"事件触发时执行plus对象的方法  
    // ...  
    if ( plus.os.name == "Android" ) {  
        // 导入要用到的类对象  
        Intent = plus.android.importClass("android.content.Intent");  
        BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory");  
        // 获取主Activity  
        main = plus.android.runtimeMainActivity();  
    }  
}, false);  
/**  
 * 创建桌面快捷方式  
 */  
function createShortcut(){  
    // 创建快捷方式意图  
    var shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");  
    // 设置快捷方式的名称  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "测试快捷方式");  
    // 设置不可重复创建  
    shortcut.putExtra("duplicate",false);  
    // 设置快捷方式图标  
    var iconPath = plus.io.convertLocalFileSystemURL("/icon.png"); // 将相对路径资源转换成系统绝对路径  
    var bitmap = BitmapFactory.decodeFile(iconPath);  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON,bitmap);  
    // 设置快捷方式启动执行动作  
    var action = new Intent(Intent.ACTION_MAIN);  
    action.setClassName(main.getPackageName(), 'io.dcloud.PandoraEntry');  
    shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT,action);  
    // 广播创建快捷方式  
    main.sendBroadcast(shortcut);  
    console.log( "桌面快捷方式已创建完成!" );  
}

注意:提交到云平台打包时需要添加Android权限才能在桌面创建快捷方式,在HBuilder工程中双击应用的“manifest.json”文件,切换到“代码视图”中在plus->distribute->google->permissions节点下添加权限数据:

"google": {  
    // ...  
    "permissions": [  
"<uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\"/>"  
    ]  
}

如下图所示:
manifest.json中Android权限 permissions

iOS

在iOS手机上登录game center,一个游戏中心服务,这是原本只有原生程序才能实现的功能。即使使用Hybrid方案,也需要原生工程师来配合写插件。
下面我们演示如何直接使用js在iOS手机上登录game center,在HelloH5+应用中Native.JS页面中的“Game Center (iOS)”可以查看运行效果。
注意手机未开通game center则无法登陆,请先点击iOS自带的game center进行配置。
这段代码是使用原生Objective-C实现的登录game center的代码,用于参考比对。原生Objective-C代码的头文件Test.h中代码如下:

@interface Test: NSObject  
// 游戏玩家登录状态监听函数  
- (void)authenticationChanged:(NSNotification*)notification;  
// 获取游戏玩家状态信息  
- (void)playerInformation:(GKPlayer *)player;  
// 登录到游戏中心  
- (void)loginGamecenter;  
// 停止监听登录游戏状态变化  
- (void)logoutGamecenter;  
@end  

实现文件Test.m中代码如下:  
@implementation Test  
// 游戏玩家登录状态监听函数  
- (void)authenticationChanged:(NSNotification*)notification  
{  
    // 获取游戏玩家共享实例对象  
    GKLocalPlayer *player = notification.object;  
    if ( player.isAuthenticated ) {  
        // 玩家已登录认证,获取玩家信息  
        [self playerInformation:player];  
    } else {  
        // 玩家未登录认证,提示用户登录  
        NSLog(@"请登录!");  
    }  
    // 释放使用的对象  
    [player release];  
}  
// 获取游戏玩家状态信息  
- (void)playerInformation:(GKPlayer *)player  
{  
    // 获取游戏玩家的名称  
    NSLog(@"Name: %@",player.displayName);  
}  

// 登录到游戏中心  
- (void)loginGamecenter  
{  
    // 监听用户登录状态变更事件  
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];  
    [nc addObserver:self  
           selector:@selector(authenticationChanged)  
               name:@"GKPlayerAuthenticationDidChangeNotificationName"  
             object:nil];  
    // 获取游戏玩家共享实例对象  
    GKLocalPlayer *localplayer = [GKLocalPlayer localPlayer];  
    // 判断游戏玩家是否已经登录认证  
    if ( localplayer.isAuthenticated ) {  
        // 玩家已登录认证,获取玩家信息  
        [self playerInformation:localplayer];  
    } else {  
        // 玩家未登录认证,发起认证请求  
        [localplayer authenticateWithCompletionHandler:nil];  
        NSLog(@"登录中...");  
    }  
    // 释放使用的对象  
    [localplayer release];  
    [nc release];  
}  

// 停止监听登录游戏状态变化  
- (void)logoutGamecenter  
{  
    // 取消监听用户登录状态变化  
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];  
    [nc removeObserver:self  
                  name:@"GKPlayerAuthenticationDidChangeNotificationName"  
                object:nil];  
    // 释放使用的对象  
    [nc release];  
}  
@end

使用NJS实现时可以按照Objective-C代码中的方法对应转换成JavaScript代码,最关键的代码是loginGamecenter方法中对用户登录状态的监听,需调用NSNotificationCenter对象的“addObserver:selector:name:object”方法,

  1. addObserver:后要求传入一个实例对象用于查找selector参数中指定的方法,在Objective-C中通常将对象自身(self)传入,但在NJS中没有此概念,因此需使用plus.ios.implements方法来创建一个新的对象:
    var delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged});
    第一个参数“NSObject”表示对象的类型,第二个参数中的JSON对象表明对象拥有的方法,“authenticationChanged”方法是delegate对象的方法。
  2. selector:后要传入一个类函数指针,在Objective-C中通过“@selector”指令可选择函数指针,在NJS中则需使用plus.ios.newObject方法来创建一个函数对象:
    plus.ios.newObject("@selector","authenticationChanged:")
    第一个参数需固定值为“@selector”,表示创建的是类函数指针对象,第二个参数。
    在"plusready"事件中导入GKLocalPlayer和NSNotificationCenter类,并调用登录方法longinGamecenter()。

完整JavaScript代码如下:

// 处理"plusready"事件  
var bLogin=false;  
document.addEventListener( "plusready", function() {  
    // ...  
    if ( plus.os.name == "iOS" ) {  
        GKLocalPlayer  = plus.ios.importClass("GKLocalPlayer");  
        NSNotificationCenter = plus.ios.importClass("NSNotificationCenter");  
        longinGamecenter();  
    } else {  
        alert("欢迎您");  
        bLogin = true;  
        setTimeout( function(){  
            plus.ui.toast( "此平台不支持Game Center功能!" );  
        }, 500 );  
    }  
}, false);  

var GKLocalPlayer=null,NSNotificationCenter=null;  
var delegate=null;  

// 游戏玩家登录状态监听函数  
function authenticationChanged( notification ){  
    // 获取游戏玩家共享实例对象  
    var player = notification.plusGetAttribute("object");  
    if ( player.plusGetAttribute("isAuthenticated") ) {  
        // 玩家已登录认证,获取玩家信息  
        playerInformation(player);  
        bLogin = true;  
    } else {  
        // 玩家未登录认证,提示用户登录  
        alert("请登录");  
        bLogin = false;  
    }  
    // 释放使用的对象  
    plus.ios.deleteObject(player);  
}  

// 获取游戏玩家状态信息  
function playerInformation( player ){  
    var name = player.plusGetAttribute("displayName");  
    alert( name+" 已登录!" );  
}  

// 登录到游戏中心  
function longinGamecenter(){  
    if ( bLogin ){  
        return;  
    }  
    // 监听用户登录状态变更事件  
    var nc = NSNotificationCenter.defaultCenter();  
    delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged});  
    nc.addObserverselectornameobject(delegate,  
        plus.ios.newObject("@selector","authenticationChanged:"),  
        "GKPlayerAuthenticationDidChangeNotificationName",  
        null);  
    // 获取游戏玩家共享实例对象  
    var localplayer = GKLocalPlayer.localPlayer();  
    // 判断游戏玩家是否已经登录认证  
    if ( localplayer.isAuthenticated() ) {  // localplayer.plusGetAttribute("isAuthenticated")  
        // 玩家已登录认证,获取玩家信息  
        playerInformation( localplayer );  
        bLogin = true;  
    } else {  
        // 玩家未登录认证,发起认证请求  
        localplayer.authenticateWithCompletionHandler(null);  
        alert( "登录中..." );  
    }  
    // 释放使用的对象  
    plus.ios.deleteObject(localplayer);  
    plus.ios.deleteObject(nc);  
}  

// 停止监听登录游戏状态变化  
function stopGamecenterObserver()  
{  
    // 取消监听用户登录状态变化  
    var nc = NSNotificationCenter.defaultCenter();  
    nc.removeObservernameobject(delegate,"GKPlayerAuthenticationDidChangeNotificationName",null);  
    plus.ios.deleteObject(nc);  
    plus.ios.deleteObject(delegate);  
    delegate = null;  
}

注意

  1. 提交到云平台打包时需要添加Game Center API的系统库(framework)才能正确调用,在HBuilder工程中双击应用的“manifest.json”文件,切换到“代码视图”中在plus->distribute->apple->frameworks节点下添加要引用的系统Framework:
    "apple": {  
    "devices": "universal",  
    "frameworks": [  
        "GameKit.framework"  
    ]  
    }

    ,如下图所示:
    HBuilder frameworks

  2. 正式发布提交到AppStore时,在配置苹果开发者网站上配置App ID需要选中“Game Center”服务:
    Game Center

开发注意和建议用途

Native.js的运行性能仍然不比纯原生应用;JS与Native之间的数据交换效率并不如在js内部的数据交换效率;基于如上原因,有几点开发建议:

  • 以标准web 代码为主,当遇到web能力不足的时候,调用Native.js。
  • 以标准web 代码为主,当遇到web性能不足的时候,需要分析,
    if ((原生进行运算的效率-js与原生通讯的损耗)>纯web的效率){
    使用Native.js
    }else{
    还应该使用纯js
    }
  • 应避免把程序设计为在短时间内并发触发Native.js代码

调试

使用safari和chrome的控制台调试HBuilder的5+App时,一样可以调试NJS对象,即可以在浏览器控制台中查看各种原生对象的属性和方法,如下图所示,57行设了断点,watch了Intent对象,并在右边展开了该对象的所有属性方法:
Chrome Debug
关于如何在浏览器控制台调试HBuilder的5+App,请参考HBuilder的5+App开发入门教程。

开发资源

iOS 官方在线文档:https://developer.apple.com/library/ios/navigation/
Android 官方在线文档:https://developer.android.com/reference/packages.html
演讲视频:http://v.youku.com/v_show/id_XNzYzNTcwNDI4.html

高级API

有前述的常用API,已经可以完成各项业务开发。此处补充的高级API,是在熟悉NJS后,为了提升性能而使用的API。高级API无法直接用“.”操作符使用原生对象的方法,在debug时也无法watch原生对象,但高级API性能高于常规API。
虽然导入类对象(plus.android.importClass和plus.ios.importClass)后,可以方便的通过“.”操作符来访问对象的常量、调用对象的方法,但导入类对象也需要消耗较多的系统资源,所以在实际开发时应该尽可能的减少导入类对象,以提高程序效率。可以参考以下依据进行判断:

  1. 如导入的类特别复杂,继承自很多基类,方法和属性特别多则考虑不导入类;
  2. 对导入类是否需要频繁操作,若导入类仅是为了实例化,并作为调用其它API的参数,则不应该导入类;
  3. 在同一页面中是否导入了很多类?如果导入太多则需要考虑减少导入类的数目。

如果我们不导入类对象则无法通过new操作符实例化类对象,这时可通过plus.ios.newObject()、plus.android.newObject()方法来创建实例对象,如下:

// iOS平台创建NSDictionary的实例对象  
var ns = plus.ios.newObject( "NSDictionary" );  

// Android平台创建Intent的实例对象  
var intent = plus.android.newObject( "android.content.Intent" );

API on Android

plus.android.newObject

不导入类对象直接创建类的实例对象,方法原型如下:

InstanceObject plus.android.newObject( String classname, Object...args );

此方法对Native层中对类进行实例化操作,创建一个类的实体并返回NJS层的实例对象。相比导入类对象后使用new操作符创建对象效率要高。

  • classname:要创建实例对象的类名,类名必须是完整的命名空间,使用“.”分隔符(如“android.app.AlertDialog”),如果需要创建内部类对象需要使用“$”分割符(如“android.app.AlertDialog$Builder”)。如果指定的类名不存在,则创建对象失败,返回null。
  • args:调用类构造函数的参数,其类型和数目必须与Native层Java类构造函数区配,否则无法创建类对象,将返回null。

注意:由于没有导入类对象,所以通过此方法创建的实例对象无法通过“.”操作符直接调用对象的方法,而必须使用plus.android.invoke方法来调用。

示例:

  1. 不导入类创建实例对象
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // ...

plus.android.getAttribute

不导入类对象,则无法通过类对象并访问类的静态属性,需调用以下方法获取类的静态属性值,方法原型如下:

Object plus.android.getAttribute( String|Object obj, String name );

此方法也可以获取类对象或实例对象的属性值,如果是类对象获取的则是类的静态属性,如果是实例对象则获取的是对象的非静态属性。

  • obj:若是String类型,表示要获取静态属性值的类名,类名必须是完整的命名空间(使用"."分割);若是ClassObject类型,表示要获取静态属性的类对象;若是InstanceObject类型,表示要获取属性值的实例对象。
  • name:要获取的属性名称,如果指定的属性名称不存在,则获取属性失败,返回null。

注意:同样导入类对象后也可以调用此方法,obj参数类型为ClassObject时,其作用与ClassObject.plusSetAttribute方法一致。obj参数类型为InstanceObject时,其作用与InstanceObject.plusSetAttribute方法一致。

示例:

  1. 不导入类对象获取类的静态常量属性
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 获取类的静态常量属性  
    int type = NjsHello.CTYPE;  
    System.out.printf( "NjsHello Final's value: %d", type );  // 输出“NjsHello Final's value: 1”  
    // 获取类的静态属性  
    int count = NjsHello.count;  
    System.out.printf( "NjsHello Static's value: %d", count );  // 输出“NjsHello Static's value: 0”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 访问类的静态常量属性  
    var type = plus.android.getAttribute( "io.dcloud.NjsHello", "CTYPE" );  
    console.log( "NjsHello Final's value: "+type ); // 输出“NjsHello Final's value: 1”  
    // ...
  2. 不导入类对象,创建实例对象,并获取其name属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 获取其name属性值  
    String name = hello.name;  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 获取其name属性值  
    var name = plus.android.getAttribute( hello, "name" );  
    console.log( "NjsHello Object's name: "+name );  // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.setAttribute

若没有导入类对象,则无法通过类对象设置类的静态属性值,需调用以下方法设置类的静态属性值,方法原型如下:

void plus.android.setAttribute( String|Object obj, String name, Object value );

此方法也可以设置类对象或实例对象的属性值,如果是类对象设置的则是类的静态属性,如果是实例对象则设置的是对象的非静态属性。

  • obj:若是String类型,表示要设置静态属性值的类名,类名必须是完整的命名空间(使用"."分割);若是ClassObject类型,表示要设置静态属性的类对象;若是InstanceObject类型,表示要设置属性值的实例对象。
  • name:要设置的属性名称,如果指定的属性名称不存在,则设置属性失败,返回null。
  • value:要设置的属性值,其类型必须与Native层obj对象的属性区配,否则设置操作不生效,将保留以前的值。

注意:同样导入类对象后也可以调用此方法,obj参数类型为ClassObject时,其作用与ClassObject.plusSetAttribute方法一致。obj参数类型为InstanceObject时,其作用与InstanceObject.plusSetAttribute方法一致。

示例:

  1. 不导入类对象设置类的静态属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 设置类的静态属性值  
    NjsHello.count = 2;  
    System.out.printf( "NjsHello Static's value: %d", NjsHello.count );  // 输出“NjsHello Static's value: 2”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 设置类的静态属性值  
    plus.android.setAttribute( "io.dcloud.NjsHello", "count", 2 );  
    console.log( "NjsHello Static's value: "+plus.android.getAttribute("io.dcloud.NjsHello","count") ); // 输出“NjsHello Static's value: 2”  
    // ...
  2. 导入类对象,创建实例对象,并设置其name属性值
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 设置其name属性值  
    hello.name = "Tester";  
    System.out.printf( "NjsHello Object's name: %s", hello.name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 设置其name属性值  
    plus.android.setAttribute( hello, "name", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

plus.android.invoke

若没有导入类对象,则无法通过实例对象的“.”操作符调用其成员方法,需通过以下方法调用实例对象的成员方法,方法原型如下:

Object plus.android.invoke( String|Object obj, String name, Object... args );

此方法也可以调用类对象或实例对象的方法,如果是类对象则调用的是类的静态方法,如果是实例对象则调用的是对象的普通成员方法。函数返回值是调用Native层方法运行后的返回值,Native对象的方法无返回值则返回undefined。

  • obj:若是String类型,表示要调用静态方法的类名,类名必须包含完整的包名;若是ClassObject类型,表示要调用静态方法的类对象;若是InstanceObject类型,表示要调用成员方法的实例对象。
  • name:要调用的方法名称,如果指定的方法不存在,则调用方法失败,返回值为null。
  • args:调用方法的参数,其类型和数目必须与Native层对象方法的函数区配,否则无法调用对象的方法,将返回null。

注意:同样导入类对象后也可以调用此方法,其作用与通过类对象或实例对象的“.”操作符调用方法作用一致。

示例:
1.不导入类对象,调用类的静态方法
Java代码:

import io.dcloud.NjsHello;  
//...  
public class Test {  
public static void main( String args[] ) {  
    // 调用类的静态方法  
    NjsHello.testCount();  
    //...  
}  
//...  
}

NJS代码:

// 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
// 调用类的静态方法  
plus.android.invoke( "io.dcloud.NjsHello", "testCount" );  
// ...
  1. 不导入类对象,创建实例对象,并调用其updateNmae方法
    Java代码:
    import io.dcloud.NjsHello;  
    //...  
    public class Test {  
    public static void main( String args[] ) {  
    // 创建对象的实例  
    NjsHello hello = new NjsHello();  
    // 调用updateName方法  
    hello.updateName( "Tester" );  
    System.out.printf( "NjsHello Object's name: %s", name );  // 输出“NjsHello Object's name: Tester”  
    //...  
    }  
    //...  
    }

    NJS代码:

    // 不调用plus.android.importClass("io.dcloud.NjsHello")导入类NjsHello  
    // 创建对象的实例  
    var hello = plus.android.newObject( "io.dcloud.NjsHello" );  
    // 调用updateName方法  
    plus.android.invoke( hello, "updateName", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

    完整API文档参考:HTML5+ API - Native.js for Android

API on iOS

plus.ios.newObject

不导入类对象直接创建类的实例对象,方法原型如下:

InstanceObject plus.ios.newObject( String classname, Object..args );

此方法会在Native层中对类进行实例化操作,创建一个类的实体并返回NJS层的类实例对象。相比导入类对象后使用new操作符创建对象效率要高。

  • classname:要创建实例对象的类名,如果指定的类名不存在,则创建对象失败,返回null。
  • args:调用类构造函数的参数,其类型和数目必须与Native层对象构造函数区配,否则无法创建类对象,将返回null。

注意:由于没有导入类对象,所以通过此方法创建的实例对象无法通过“.”操作符直接调用对象的方法,而必须使用plus.ios.invoke方法来调用。classname参数值为“@selector”表示需要创建一个函数指针对象,与Objective-C中的@selector指令功能相似,args参数为函数的名称,此时函数的名称需要包含“:”字符。

示例:

  1. 不导入类创建实例对象
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // ...  
    }

    NJS代码:

    // 未导入“NjsHello”类  
    // 创建对象的实例  
    var hello = plus.ios.newObject( "NjsHello" );  
    // ...

plus.ios.invoke

若没有导入类对象,则无法通过实例对象的“.”操作符调用其成员方法,需通过以下方法调用实例对象的成员方法,方法原型如下:

Object plus.ios.invoke( String|Object obj, String name, Object... args );

此方法也可以调用类对象或实例对象的方法,如果是类对象则调用的是类的静态方法,如果是实例对象则调用的是对象的普通成员方法。函数返回值是调用Native层方法运行后的返回值,Native对象的方法无返回值则返回undefined。

  • obj:若是String类型,表示要调用静态方法的类名,类名必须包含完整的包名;若是ClassObject类型,表示要调用静态方法的类对象;若是InstanceObject类型,表示要调用成员方法的实例对象。
  • name:要调用的方法名称,必须保留方法名称中的“:”字符,如果指定的方法不存在,则调用方法失败,返回值为null。
  • args:调用方法的参数,其类型和数目必须与Native层对象方法的函数区配,否则无法调用对象的方法,将返回null。

注意:同样导入类对象后也可以调用此方法,其作用与通过类对象或实例对象的“.”操作符调用方法作用一致。

示例:

  1. 不导入类创建实例对象,并调用updateName方法
    Objective-C代码:
    #import "njshello.h"  
    int main( int argc, char *argv[] )  
    {  
    // 创建对象的实例  
    NjsHello* hello = [[NjsHello alloc] init];  
    // 调用updateName方法  
    [hello updateName:@"Tester"];  
    NSLog(@"NjsHello Object's name: %@",hello.name);  // 输出“NjsHello Object's name: Tester”  
    // ...  
    }

    NJS代码:

    // 未导入“NjsHello”类  
    // 创建对象的实例  
    var hello = plus.ios.newObject( "NjsHello" );  
    // 调用updateName方法  
    plus.ios.invoke( hello, "updateName", "Tester" );  
    console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 输出“NjsHello Object's name: Tester”  
    // ...

    完整API文档参考:HTML5+ API - Native.js for iOS

性能优化

调整代码结构优化

前面章节中我们介绍如何通过NJS调用Native API来显示系统提示框,在真机运行时会发现第一次调用时会有0.5s左右的延时,再次调用则不会延时。这是因为NJS中导入类对象操作会花费较长的时间,再次调用时由于类对象已经导入过,会能很快执行完毕。因此可以调整代码结构进行优化,在页面打开后触发的“plusready”事件中进行类对象的导入操作,从而避免第一次调用的延时。

Android平台调整NJS代码结构如下:

// 保存Android导入对象和全局环境对象  
var AlertDialog=null,mainActivity=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // 程序全局环境对象,内部自动导入Activity类  
        mainActivity = plus.android.runtimeMainActivity();  
        // 导入AlertDialog类  
        AlertDialog = plus.android.importClass("android.app.AlertDialog");  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 创建提示框构造对象,构造函数需要提供程序全局环境对象,通过plus.android.runtimeMainActivity()方法获取  
    var dlg = new AlertDialog.Builder(mainActivity);  
    // 设置提示框标题  
    dlg.setTitle("自定义标题");  
    // 设置提示框内容  
    dlg.setMessage("使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    dlg.setPositiveButton("确定(或者其他字符)",null);  
    // 显示提示框  
    dlg.show();  
}  
//...

iOS平台调整NJS代码结构如下:

// 保存iOS平台导入的类对象  
var UIAlertView=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "iOS":  
        // 导入UIAlertView类  
        UIAlertView = plus.ios.importClass("UIAlertView");  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 创建UIAlertView类的实例对象  
    var view = new UIAlertView();  
    // 设置提示对话上的内容  
    view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框  
    view.show();  
}  
//...

使用高级API优化

前面章节中我们提到导入类对象会消耗较多的系统资源,导入过多的类对象会影响性能。在高级API中提供一组接口可以在不导入类对象的情况下调用Native API,从而提升代码运行性能。

Android平台使用高级API优化代码如下:

// 保存Android导入对象和全局环境对象  
var mainActivity=null;  
// H5+事件处理  
document.addEventListener("plusready",function(){  
    switch ( plus.os.name ) {  
        case "Android":  
        // 程序全局环境对象,内部自动导入Activity类  
        mainActivity = plus.android.runtimeMainActivity();  
        break;  
        default:  
        break;  
    }  
},false);  
//...  
/**  
 * 在Android平台通过NJS显示系统提示框  
 */  
function njsAlertForAndroid(){  
    // 由于Builder类是android.app.AlertDialog类的内部类,这里需要使用$符号分割  
    var dlg = plus.android.newObject("android.app.AlertDialog$Builder",mainActivity);  
    // 设置提示框标题  
    plus.android.invoke(dlg,"setTitle","自定义标题");  
    // 设置提示框内容  
    plus.android.invoke(dlg,"setMessage","使用NJS的原生弹出框,可自定义弹出框的标题、按钮");  
    // 设置提示框按钮  
    plus.android.invoke(dlg,"setPositiveButton","确定(或者其他字符)",null);  
    // 显示提示框  
    plus.android.invoke(dlg,"show");  
}  
//...

iOS平台使用高级API优化代码如下:

/**  
 * 在iOS平台通过NJS显示系统提示框  
 */  
function njsAlertForiOS(){  
    // 创建UIAlertView类的实例对象  
    var view = plus.ios.newObject("UIAlertView");  
    // 设置提示对话上的内容,这里的方法名称中必须包含':'字符  
    plus.ios.invoke(view,"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"  
        ,"自定义标题" // 提示框标题  
        , "使用NJS的原生弹出框,可自定义弹出框的标题、按钮" // 提示框上显示的内容  
        , null // 操作提示框后的通知代理对象,暂不设置  
        , "确定(或者其他字符)" // 提示框上取消按钮的文字  
        , null ); // 提示框上其它按钮的文字,设置为null表示不显示  
    // 调用show方法显示提示对话框,在JS中使用()语法调用对象的方法  
    plus.ios.invoke(view,"show");  
}  
//...
收起阅读 »

如何自定义mui控件样式

控件样式 mui

mui以iOS 7的UI为基础,补充了部分Android平台特有的UI组件,整体色系比较素雅;但在实际项目,往往需要根据UED的设计,调整色系及排版布局,此时就涉及mui控件样式的重定义问题,本文以hello mui中导航栏默认样式页面为示例,讲解如何自定义mui控件样式。原始界面如图:
图片
我们希望重定义为如下界面:
图片
操作步骤如下:
1、通过chrome浏览器模拟手机浏览器打开对应页面,初级用户参考这里这里
2、在需要重定义样式的控件(导航栏)上,点击右键,选择“审查元素”,会打开chrome控制台,控制台左侧会显示对应控件的DOM结构,右侧会显示作用在该控件上的CSS定义,如下图所示;在左侧DOM区,切换DOM节点,上方模拟器对应控件及右侧作用的CSS定义均会变化;


3、分析需要重定义的内容,从效果图上直观来看,需要将导航条背景色修改为蓝色,然后将返回图标及导航标题修改为白色;
4、通过DOM结构分析,应该较容易的看出,header节点表示导航条控件,那我们就DOM区选择header节点,然后分析右侧模拟器,会看到header上的所有css定义,找到background-color定义,如下所示:

5、单击background-color对应颜色值,修改成UED设计师提供的蓝色,比如#253ff2,这时上方模拟器上导航条会实时变成蓝色,如下:

6、确认颜色值正确后,将修改代码复制到对应页面中,本示例为titlebar.html,保存如下代码(需放在mui.min.css引用之后),这样就可以覆盖mui默认的背景色定义:

.mui-bar{  
    background-color: #253FF2;  
}

7、此时再刷新当前页面,就会看到背景色已变;
8、同样的方式,找到标题栏文字颜色定义,找文字颜色时要定位到对应文字的最小节点,对于如下的DOM节点,

<header class="mui-bar mui-bar-nav">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
    <h1 class="mui-title">导航栏</h1>  
</header>

我们应该优先看h1的css定义,会发现color定义为#000,修改为#fff即可,如下:


修改后效果:
图片
同样复制保存css定义,如下:

.mui-title{  
    color:#fff  
}

9、最后,还剩一个左侧返回箭头的颜色值,我们也以同样的方法修改,左侧DOM区选中<a>节点,然后在右侧css区查看css定义,找到color颜色定义的地方,然后同样修改为#fff,修改结果如下:

10、以同样方式拷贝css代码到html文件,最终复写的css代码为

.mui-bar{  
    background-color: #253FF2;  
}  
.mui-title{  
     color:#fff;  
}  
a{  
    color:#fff;  
}

经过如上几个步骤,我们就完成了导航条的自定义,当然在实际开发中,我们可以更为灵活,比如<a>的css定义牵扯范围太广,我们仅在返回的a节点上增加style属性,在style中定义color,例如:

<header class="mui-bar mui-bar-nav">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left" style="color: #fff;"></a>  
    <h1 class="mui-title">导航栏</h1>  
</header>
继续阅读 »

mui以iOS 7的UI为基础,补充了部分Android平台特有的UI组件,整体色系比较素雅;但在实际项目,往往需要根据UED的设计,调整色系及排版布局,此时就涉及mui控件样式的重定义问题,本文以hello mui中导航栏默认样式页面为示例,讲解如何自定义mui控件样式。原始界面如图:
图片
我们希望重定义为如下界面:
图片
操作步骤如下:
1、通过chrome浏览器模拟手机浏览器打开对应页面,初级用户参考这里这里
2、在需要重定义样式的控件(导航栏)上,点击右键,选择“审查元素”,会打开chrome控制台,控制台左侧会显示对应控件的DOM结构,右侧会显示作用在该控件上的CSS定义,如下图所示;在左侧DOM区,切换DOM节点,上方模拟器对应控件及右侧作用的CSS定义均会变化;


3、分析需要重定义的内容,从效果图上直观来看,需要将导航条背景色修改为蓝色,然后将返回图标及导航标题修改为白色;
4、通过DOM结构分析,应该较容易的看出,header节点表示导航条控件,那我们就DOM区选择header节点,然后分析右侧模拟器,会看到header上的所有css定义,找到background-color定义,如下所示:

5、单击background-color对应颜色值,修改成UED设计师提供的蓝色,比如#253ff2,这时上方模拟器上导航条会实时变成蓝色,如下:

6、确认颜色值正确后,将修改代码复制到对应页面中,本示例为titlebar.html,保存如下代码(需放在mui.min.css引用之后),这样就可以覆盖mui默认的背景色定义:

.mui-bar{  
    background-color: #253FF2;  
}

7、此时再刷新当前页面,就会看到背景色已变;
8、同样的方式,找到标题栏文字颜色定义,找文字颜色时要定位到对应文字的最小节点,对于如下的DOM节点,

<header class="mui-bar mui-bar-nav">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
    <h1 class="mui-title">导航栏</h1>  
</header>

我们应该优先看h1的css定义,会发现color定义为#000,修改为#fff即可,如下:


修改后效果:
图片
同样复制保存css定义,如下:

.mui-title{  
    color:#fff  
}

9、最后,还剩一个左侧返回箭头的颜色值,我们也以同样的方法修改,左侧DOM区选中<a>节点,然后在右侧css区查看css定义,找到color颜色定义的地方,然后同样修改为#fff,修改结果如下:

10、以同样方式拷贝css代码到html文件,最终复写的css代码为

.mui-bar{  
    background-color: #253FF2;  
}  
.mui-title{  
     color:#fff;  
}  
a{  
    color:#fff;  
}

经过如上几个步骤,我们就完成了导航条的自定义,当然在实际开发中,我们可以更为灵活,比如<a>的css定义牵扯范围太广,我们仅在返回的a节点上增加style属性,在style中定义color,例如:

<header class="mui-bar mui-bar-nav">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left" style="color: #fff;"></a>  
    <h1 class="mui-title">导航栏</h1>  
</header>
收起阅读 »

PC上使用chrome浏览器模拟手机浏览器

chrome

chrome浏览器提供了模拟手机浏览器的功能,这样在开发mobile app的过程中,就可以通过pc端的chrome浏览器快速模拟不同手机浏览器的显示效果。模拟方法如下:
1、通过pc端的chrome浏览器打开要预览的页面
2、在页面上右键,选择“审查元素”,打开chrome控制台;
3、选择右下角的“Show drawer”按钮,如下截图:


4、选择“Emulation”选项,如下图所示:

5、通过Model选择要模拟的手机类型,如图:

6、刷新当前页面,即可看到在手机上的模拟显示效果;
7、若要模拟手机端的touch事件,则勾选Sensors选项,并勾选“Emulate touch screen”即可,如下;

继续阅读 »

chrome浏览器提供了模拟手机浏览器的功能,这样在开发mobile app的过程中,就可以通过pc端的chrome浏览器快速模拟不同手机浏览器的显示效果。模拟方法如下:
1、通过pc端的chrome浏览器打开要预览的页面
2、在页面上右键,选择“审查元素”,打开chrome控制台;
3、选择右下角的“Show drawer”按钮,如下截图:


4、选择“Emulation”选项,如下图所示:

5、通过Model选择要模拟的手机类型,如图:

6、刷新当前页面,即可看到在手机上的模拟显示效果;
7、若要模拟手机端的touch事件,则勾选Sensors选项,并勾选“Emulate touch screen”即可,如下;

收起阅读 »

IOS平台以Widget方式集成HTML5+SDK方法

SDK

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

开发者可参考SDK中示例 “HBuilder-Integrate”
最新SDK下载地址

1 概述

HTML5+ SDK 可以按照“独立应用”、“Widget”和“WebView”三种方式进行集成,三种集成方式各有优点。
独立应用集成方式:使用独立应用方式,开发者需要将HTML5+SDK生成的首页面设置为当前View的subView。HTML5+ SDK将对应用进行管理。
Widget集成方式:运行方式和独立运行方式类似,开发者在集成时可在需要的位置启动HTML5+ SDK,显示指定的HTML5+应用。原生App中集成uni-app也需使用本集成方式。
Webview集成方式:用户可在任何页面将HTML5+ SDK的页面以Webview的形式独立显示,显示的Webview页面。

2 导入SDK Lib文件和头文件

在工程中引入HTML5+ SDK扩展功能的.a文件,开发者可以根据需求添加不同的扩展插件。必须要引入的是liblibPDRCore.a,liblibUI.a,liblibNativeUI.a,liblibNavigator.a,libcoreSupport.a,这几个库是HTML5+SDK运行的基础库文件。
注意: 开发者在使用示例工程时建议不要把工程从SDK目录里挪出来,如果要移动工程可以通过修改library search path ,framework search path 和head search path来解决报错。


工程中引入HTML5+ SDK 中提供的头文件。

3 添加代码编译测试

  1. 开发者初始化PDRCore句柄。
  2. 设置指定的View为HTML5+ SDK的父View,用于显示HTML5+ SDK的页面
  3. 调用PDRCore接口设置要启动应用的APPID
  4. 调用PDRCore句柄的start接口启动应用,HTML5+ Runtime将会根据用户 指定APPID启动指定的应用
- (void)viewDidLoad  
{  
[super viewDidLoad];  
PDRCore* pCoreHandle = [PDRCore Instance];  
[pCoreHandle setContainerView:self.view];  
[pCoreHandle setAutoStartAppid:@"HelloH5"];  
[pCoreHandle start];          
}

4 导入应用并配置

4.1 添加应用

在离线打包之前,用户需要将已经开发完成的HTML5+ 应用导入到Xcode工程中。

4.1.1 应用导入

需要将开发完成的HTML5+ 应用拷贝到Pandora/apps/[appid]/www目录下。

4.1.2 manifes.json文件编写

请参考manifest.json 文件配置指导手册

4.2 配置应用的插件功能

开发者需要将html5+ SDK里的PandoraAPI.bundle引入到开发中的工程中。开发者如开发了HTML5+扩展插件,需要修改PandoraAPI.bundle下的feature.plist文件,在文件中添加扩展插件JS类名和原生类名的对应关系。

6 常用类说明

PDRCore

常用接口说明:

+ (PDRCore*)Instance;

获取Core单例对象
返回值:
PDRCore 单例的PDRCore对象

- (int)setAppsRunPath:(NSString*)workPath;

设置应用运行时目录,应用运行时产生的文件在该目录下生成,当应用 runmode为liberate时将把资源拷贝到该目录
参数说明:
workPath 应用运行时目录
返回值:
int 0 成功

- (int)setAppsInstallPath:(NSString*)installPath;

设置runtime应用的安装目录,该地址为安装包中携带的应用资源位置
参数说明:
installPath 应用的安装目录
返回值:
int 0 成功

- (void)setInnerVersion:(NSString*)innerVersion;

设置HTML5+ SDK运行版本
参数说明:
HTML5+ SDK运行版本号

- (int)setDocumethPath:(NSString*)documentPath;

设置HTML5+ SDK文档目录
参数说明:
documentPath 应用的文档目录
返回值:
int 0 成功

- (int)setDownloadPath:(NSString*)downlaodPath;

设置runtime下载目录
参数说明:
downloadPath 应用下载文件的路径
返回值:
int 0 成功

- (int)setAutoStartAppid:(NSString*)appid;

设置runtiem启动时自动运行的APP
参数说明:
appid 默认启动应用的APPID
返回值:
int 0 成功

- (int)setContainerView:(UIView*)containerView;

设置runtime根视图的父亲View
参数说明:
containerView 要显示HTML5+SDK的父View
返回值:
int 0 成功

- (PDRCoreApp*)openAppWithAppid:(NSString*)appId  
                       withArgs:(NSString*__nullable)args  
                   withDelegate:(id<PDRCoreAppWindowDelegate>__nullable)delegate;

@param appId appId
@param args 传入启动参数,可以在页面中通过 plus.runtime.arguments 参数获取
@param delegate 代理
@return PDRCoreApp实例对象

- (int)regPluginWithName:(NSString*)pluginName  
             impClassName:(NSString*)impClassName  
                    type:(PDRExendPluginType)pluginType  
               javaScript:(NSString*)javaScript;

注册第三方扩展的HTML5+插件
参数说明:
pluginName 插件名称JS文件中定义的名字
impClassName 插件对应的实现类名
pluginType 插件类型 详情: PDRExendPluginType
javaScript js实现 为javascript文本
返回值:
int 0 成功

- (int)start;

正常启动runtime,使用改方法启动runtime具有全部功能,包括具有应用管理、窗口管理、插件管理、权限管理、资源管理等功能

- (int)startAsWebClient;

启动runtime,使用该方法启动的runtime不具有应用管理窗口管理功能,当需要显示页面时,需要自己创建PDRCoreAppFrame

PDRCoreAppFrame

常用接口说明

- (PDRCoreAppFrame*)initWithId:(NSString*)frameID loadURL:(NSString*)pagePath  frame:(CGRect)frame;

创建runtime页面
参数说明:
frameID 页面标示
pagePath 页面地址 支持http:// file:// 本地地址
frame 页面位置

@property(nonatomic, readonly)UIWebView *webView;

应用页面的WebView对象

继续阅读 »

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

开发者可参考SDK中示例 “HBuilder-Integrate”
最新SDK下载地址

1 概述

HTML5+ SDK 可以按照“独立应用”、“Widget”和“WebView”三种方式进行集成,三种集成方式各有优点。
独立应用集成方式:使用独立应用方式,开发者需要将HTML5+SDK生成的首页面设置为当前View的subView。HTML5+ SDK将对应用进行管理。
Widget集成方式:运行方式和独立运行方式类似,开发者在集成时可在需要的位置启动HTML5+ SDK,显示指定的HTML5+应用。原生App中集成uni-app也需使用本集成方式。
Webview集成方式:用户可在任何页面将HTML5+ SDK的页面以Webview的形式独立显示,显示的Webview页面。

2 导入SDK Lib文件和头文件

在工程中引入HTML5+ SDK扩展功能的.a文件,开发者可以根据需求添加不同的扩展插件。必须要引入的是liblibPDRCore.a,liblibUI.a,liblibNativeUI.a,liblibNavigator.a,libcoreSupport.a,这几个库是HTML5+SDK运行的基础库文件。
注意: 开发者在使用示例工程时建议不要把工程从SDK目录里挪出来,如果要移动工程可以通过修改library search path ,framework search path 和head search path来解决报错。


工程中引入HTML5+ SDK 中提供的头文件。

3 添加代码编译测试

  1. 开发者初始化PDRCore句柄。
  2. 设置指定的View为HTML5+ SDK的父View,用于显示HTML5+ SDK的页面
  3. 调用PDRCore接口设置要启动应用的APPID
  4. 调用PDRCore句柄的start接口启动应用,HTML5+ Runtime将会根据用户 指定APPID启动指定的应用
- (void)viewDidLoad  
{  
[super viewDidLoad];  
PDRCore* pCoreHandle = [PDRCore Instance];  
[pCoreHandle setContainerView:self.view];  
[pCoreHandle setAutoStartAppid:@"HelloH5"];  
[pCoreHandle start];          
}

4 导入应用并配置

4.1 添加应用

在离线打包之前,用户需要将已经开发完成的HTML5+ 应用导入到Xcode工程中。

4.1.1 应用导入

需要将开发完成的HTML5+ 应用拷贝到Pandora/apps/[appid]/www目录下。

4.1.2 manifes.json文件编写

请参考manifest.json 文件配置指导手册

4.2 配置应用的插件功能

开发者需要将html5+ SDK里的PandoraAPI.bundle引入到开发中的工程中。开发者如开发了HTML5+扩展插件,需要修改PandoraAPI.bundle下的feature.plist文件,在文件中添加扩展插件JS类名和原生类名的对应关系。

6 常用类说明

PDRCore

常用接口说明:

+ (PDRCore*)Instance;

获取Core单例对象
返回值:
PDRCore 单例的PDRCore对象

- (int)setAppsRunPath:(NSString*)workPath;

设置应用运行时目录,应用运行时产生的文件在该目录下生成,当应用 runmode为liberate时将把资源拷贝到该目录
参数说明:
workPath 应用运行时目录
返回值:
int 0 成功

- (int)setAppsInstallPath:(NSString*)installPath;

设置runtime应用的安装目录,该地址为安装包中携带的应用资源位置
参数说明:
installPath 应用的安装目录
返回值:
int 0 成功

- (void)setInnerVersion:(NSString*)innerVersion;

设置HTML5+ SDK运行版本
参数说明:
HTML5+ SDK运行版本号

- (int)setDocumethPath:(NSString*)documentPath;

设置HTML5+ SDK文档目录
参数说明:
documentPath 应用的文档目录
返回值:
int 0 成功

- (int)setDownloadPath:(NSString*)downlaodPath;

设置runtime下载目录
参数说明:
downloadPath 应用下载文件的路径
返回值:
int 0 成功

- (int)setAutoStartAppid:(NSString*)appid;

设置runtiem启动时自动运行的APP
参数说明:
appid 默认启动应用的APPID
返回值:
int 0 成功

- (int)setContainerView:(UIView*)containerView;

设置runtime根视图的父亲View
参数说明:
containerView 要显示HTML5+SDK的父View
返回值:
int 0 成功

- (PDRCoreApp*)openAppWithAppid:(NSString*)appId  
                       withArgs:(NSString*__nullable)args  
                   withDelegate:(id<PDRCoreAppWindowDelegate>__nullable)delegate;

@param appId appId
@param args 传入启动参数,可以在页面中通过 plus.runtime.arguments 参数获取
@param delegate 代理
@return PDRCoreApp实例对象

- (int)regPluginWithName:(NSString*)pluginName  
             impClassName:(NSString*)impClassName  
                    type:(PDRExendPluginType)pluginType  
               javaScript:(NSString*)javaScript;

注册第三方扩展的HTML5+插件
参数说明:
pluginName 插件名称JS文件中定义的名字
impClassName 插件对应的实现类名
pluginType 插件类型 详情: PDRExendPluginType
javaScript js实现 为javascript文本
返回值:
int 0 成功

- (int)start;

正常启动runtime,使用改方法启动runtime具有全部功能,包括具有应用管理、窗口管理、插件管理、权限管理、资源管理等功能

- (int)startAsWebClient;

启动runtime,使用该方法启动的runtime不具有应用管理窗口管理功能,当需要显示页面时,需要自己创建PDRCoreAppFrame

PDRCoreAppFrame

常用接口说明

- (PDRCoreAppFrame*)initWithId:(NSString*)frameID loadURL:(NSString*)pagePath  frame:(CGRect)frame;

创建runtime页面
参数说明:
frameID 页面标示
pagePath 页面地址 支持http:// file:// 本地地址
frame 页面位置

@property(nonatomic, readonly)UIWebView *webView;

应用页面的WebView对象

收起阅读 »

IOS平台以WebView方式集成HTML5+SDK方法

SDK 集成 iOS

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

开发者可参考SDK中示例 “HBuilder-Integrate”
最新SDK下载地址

1 概述

HTML5+ SDK 可以按照“独立应用”、“Widget”和“WebView”三种方式进行集成,三种集成方式各有优点。
独立应用集成方式: 使用独立应用方式,开发者需要将HTML5+SDK生成的首页面设置为当前View的subView。HTML5+ SDK将对应用进行管理。
Widget集成方式: 运行方式和独立运行方式类似,开发者在集成时可在需要的位置启动HTML5+ SDK,显示指定的HTML5+应用。
Webview集成方式: 用户可在任何页面将HTML5+ SDK的页面以Webview的形式独立显示,显示的Webview页面。

2 导入SDK Lib文件和头文件

在工程中引入HTML5+ SDK扩展功能的.a文件,开发者可以根据需求添加不同的扩展插件。必须要引入的是liblibPDRCore.a 和libcoreSupport.a,liblibPDRCore.a库是HTML5+SDK运行的基础库文件,libcoreSupport文件是PDRCore依赖的文件。
在引入lib文件将SDK/libs目录下的静态库文件拖入到Xcode工程里即可

注意开发者在使用示例工程时建议不要把工程从SDK目录里挪出来,如果要移动工程可以通过修改library search path ,framework search path 和head search path来解决报错。
在集成5+SDK时必须在Other link flags 里添加 -ObjC, 同时还需要引入如下几个库文件
liblibUI.a
liblibPDRCore.a
libcoreSupport.a
liblibNavigator.a

工程中引入HTML5+ SDK 中提供的头文件。
头文件在目录 IOS-SDK/SDK/Inc目录下

工程中必须要引入的系统库
点击TARGETS->General->Linked Frameworks and Librarys 添加工程依赖的库文件

WebKit.framework
CoreLocation.framework
AVFoundation.framework
QuickLook.framework
JavaScriptCore.framework
UserNotifications.framework (设置Status 为 Optional)
StoreKit.framework
AssetsLibrary.framework
AddressBook.framework
SystemConfiguration.framework
UIKit.framework
Foundation.framework
CFNetwork.framework
CoreAudioKit.framework
CoreAudio.framework
CoreMedia.framework
MediaPlayer.framework
CoreFoundation.framework
AudioToolbox.framework
CoreTelephony.framework
MobileCoreServices.framework
libstdc++.tbd
libxml2.tbd
libz.tbd

3 添加代码编译测试

使用Webview方式进行集成HTML5+SDK,开发者可以在任意位置显示支持HTML5+扩展API的WebView页面,具体步骤如下
1.获取PDRCore句柄
2.设置HTML5+ SDK运行方式
3.设置默认启动应用的APPID
4.创建PDRCoreAppFrame,并指定Webview的ID和URL,并指定打开的WebView的Frame。
5.设置PDRCoreFrame的WebView的Request,Webview会显示Request返回的页面
6.将PDRCoreFrame句柄设置为指定View的WebView

-(IBAction)ShowWebViewPageOne:(id)sender  
{  
    // 获取PDRCore句柄  
    PDRCore* pCoreHandle = [PDRCore Instance];  
    if (pCoreHandle != nil)  
    {  

        [pCoreHandle start];  
        // 设置拼写Webview将要打开文件的url  
        NSString* pFilePath = [NSString stringWithFormat:@"file://%@/%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/HelloH5/www/plus/audio.html"];  

        CGRect StRect = CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height - 20);  

        appFrame = [[PDRCoreAppFrame alloc] initWithName:@"WebViewID1" loadURL:pFilePath frame:StRect];  

        // 设置webview的Appframe  
            PDRCoreApp* activeApp= (PDRCoreApp*)pCoreHandle.appManager.activeApp;  
            [activeApp.appWindow registerFrame:appFrame];  

        // 将AppFrame设置为当前View的Subview  
        [self.view addSubview:appFrame];  
    }  
}

4 导入应用并配置

4.1 添加应用

在离线打包之前,用户需要将已经开发完成的HTML5+ 应用导入到Xcode工程中。

4.1.1 应用导入

需要将开发完成的HTML5+ 应用拷贝到Pandora/apps/[appid]/www目录下。

4.1.2 manifes.json文件编写

请参考manifest.json 文件配置指导手册

4.2 配置应用的插件功能

开发者需要将html5+ SDK里的PandoraAPI.bundle引入到开发中的工程中。开发者如开发了HTML5+扩展插件,需要修改PandoraAPI.bundle下的feature.plist文件,在文件中添加扩展插件JS类名和原生类名的对应关系。

5 常用类说明

PDRCore

常用接口说明:

+ (PDRCore*)Instance;

获取Core单例对象
返回值:
PDRCore 单例的PDRCore对象

- (int)setAppsRunPath:(NSString*)workPath;

设置应用运行时目录,应用运行时产生的文件在该目录下生成,当应用 runmode为liberate时将把资源拷贝到该目录
参数说明:
workPath 应用运行时目录
返回值:
int 0 成功

- (int)setAppsInstallPath:(NSString*)installPath;

设置runtime应用的安装目录,该地址为安装包中携带的应用资源位置
参数说明:
installPath 应用的安装目录
*返回值:**
int 0 成功

- (void)setInnerVersion:(NSString*)innerVersion;

设置HTML5+ SDK运行版本
参数说明:
innerVersion HTML5+ SDK运行版本号

- (int)setDocumethPath:(NSString*)documentPath;

设置HTML5+ SDK文档目录
参数说明:
documentPath 应用的文档目录
返回值:
int 0 成功

- (int)setDownloadPath:(NSString*)downlaodPath;

设置runtime下载目录
参数说明:
downloadPath 应用下载文件的路径
返回值:
int 0 成功

- (int)setAutoStartAppid:(NSString*)appid;

设置runtiem启动时自动运行的APP
参数说明:
appid 默认启动应用的APPID
返回值:
int 0 成功

- (int)setContainerView:(UIView*)containerView;

设置runtime根视图的父亲View
参数说明:
containerView 要显示HTML5+SDK的父View
返回值:
int 0 成功

- (int)setApp:(NSString*)appid documentPath:(NSString*)doucmentPath;

设置指定app的文档目录
参数说明:
appid 要设置的appid
doucmentPath 要设置的路径
返回值:
int 0 成功

- (int)regPluginWithName:(NSString*)pluginName  
             impClassName:(NSString*)impClassName  
                    type:(PDRExendPluginType)pluginType  
               javaScript:(NSString*)javaScript;

注册第三方扩展的HTML5+插件
参数说明:
pluginName 插件名称JS文件中定义的名字
impClassName 插件对应的实现类名
pluginType 插件类型 详情: PDRExendPluginType
javaScript js实现 为javascript文本
返回值:
int 0 成功

- (int)start;

正常启动runtime,使用改方法启动runtime具有全部功能,包括具有应用管理、窗口管理、插件管理、权限管理、资源管理等功能

- (int)startAsWebClient;

启动runtime,使用该方法启动的runtime不具有应用管理窗口管理功能,当需要显示页面时,需要自己创建PDRCoreAppFrame

PDRCoreAppFrame

常用接口说明

- (PDRCoreAppFrame*)initWithId:(NSString*)frameID loadURL:(NSString*)pagePath  frame:(CGRect)frame;

创建runtime页面
参数说明:
frameID 页面标示
pagePath 页面地址 支持http:// file:// 本地地址
frame 页面位置

@property(nonatomic, readonly)UIWebView *webView;

应用页面的WebView对象

继续阅读 »

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

开发者可参考SDK中示例 “HBuilder-Integrate”
最新SDK下载地址

1 概述

HTML5+ SDK 可以按照“独立应用”、“Widget”和“WebView”三种方式进行集成,三种集成方式各有优点。
独立应用集成方式: 使用独立应用方式,开发者需要将HTML5+SDK生成的首页面设置为当前View的subView。HTML5+ SDK将对应用进行管理。
Widget集成方式: 运行方式和独立运行方式类似,开发者在集成时可在需要的位置启动HTML5+ SDK,显示指定的HTML5+应用。
Webview集成方式: 用户可在任何页面将HTML5+ SDK的页面以Webview的形式独立显示,显示的Webview页面。

2 导入SDK Lib文件和头文件

在工程中引入HTML5+ SDK扩展功能的.a文件,开发者可以根据需求添加不同的扩展插件。必须要引入的是liblibPDRCore.a 和libcoreSupport.a,liblibPDRCore.a库是HTML5+SDK运行的基础库文件,libcoreSupport文件是PDRCore依赖的文件。
在引入lib文件将SDK/libs目录下的静态库文件拖入到Xcode工程里即可

注意开发者在使用示例工程时建议不要把工程从SDK目录里挪出来,如果要移动工程可以通过修改library search path ,framework search path 和head search path来解决报错。
在集成5+SDK时必须在Other link flags 里添加 -ObjC, 同时还需要引入如下几个库文件
liblibUI.a
liblibPDRCore.a
libcoreSupport.a
liblibNavigator.a

工程中引入HTML5+ SDK 中提供的头文件。
头文件在目录 IOS-SDK/SDK/Inc目录下

工程中必须要引入的系统库
点击TARGETS->General->Linked Frameworks and Librarys 添加工程依赖的库文件

WebKit.framework
CoreLocation.framework
AVFoundation.framework
QuickLook.framework
JavaScriptCore.framework
UserNotifications.framework (设置Status 为 Optional)
StoreKit.framework
AssetsLibrary.framework
AddressBook.framework
SystemConfiguration.framework
UIKit.framework
Foundation.framework
CFNetwork.framework
CoreAudioKit.framework
CoreAudio.framework
CoreMedia.framework
MediaPlayer.framework
CoreFoundation.framework
AudioToolbox.framework
CoreTelephony.framework
MobileCoreServices.framework
libstdc++.tbd
libxml2.tbd
libz.tbd

3 添加代码编译测试

使用Webview方式进行集成HTML5+SDK,开发者可以在任意位置显示支持HTML5+扩展API的WebView页面,具体步骤如下
1.获取PDRCore句柄
2.设置HTML5+ SDK运行方式
3.设置默认启动应用的APPID
4.创建PDRCoreAppFrame,并指定Webview的ID和URL,并指定打开的WebView的Frame。
5.设置PDRCoreFrame的WebView的Request,Webview会显示Request返回的页面
6.将PDRCoreFrame句柄设置为指定View的WebView

-(IBAction)ShowWebViewPageOne:(id)sender  
{  
    // 获取PDRCore句柄  
    PDRCore* pCoreHandle = [PDRCore Instance];  
    if (pCoreHandle != nil)  
    {  

        [pCoreHandle start];  
        // 设置拼写Webview将要打开文件的url  
        NSString* pFilePath = [NSString stringWithFormat:@"file://%@/%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/HelloH5/www/plus/audio.html"];  

        CGRect StRect = CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height - 20);  

        appFrame = [[PDRCoreAppFrame alloc] initWithName:@"WebViewID1" loadURL:pFilePath frame:StRect];  

        // 设置webview的Appframe  
            PDRCoreApp* activeApp= (PDRCoreApp*)pCoreHandle.appManager.activeApp;  
            [activeApp.appWindow registerFrame:appFrame];  

        // 将AppFrame设置为当前View的Subview  
        [self.view addSubview:appFrame];  
    }  
}

4 导入应用并配置

4.1 添加应用

在离线打包之前,用户需要将已经开发完成的HTML5+ 应用导入到Xcode工程中。

4.1.1 应用导入

需要将开发完成的HTML5+ 应用拷贝到Pandora/apps/[appid]/www目录下。

4.1.2 manifes.json文件编写

请参考manifest.json 文件配置指导手册

4.2 配置应用的插件功能

开发者需要将html5+ SDK里的PandoraAPI.bundle引入到开发中的工程中。开发者如开发了HTML5+扩展插件,需要修改PandoraAPI.bundle下的feature.plist文件,在文件中添加扩展插件JS类名和原生类名的对应关系。

5 常用类说明

PDRCore

常用接口说明:

+ (PDRCore*)Instance;

获取Core单例对象
返回值:
PDRCore 单例的PDRCore对象

- (int)setAppsRunPath:(NSString*)workPath;

设置应用运行时目录,应用运行时产生的文件在该目录下生成,当应用 runmode为liberate时将把资源拷贝到该目录
参数说明:
workPath 应用运行时目录
返回值:
int 0 成功

- (int)setAppsInstallPath:(NSString*)installPath;

设置runtime应用的安装目录,该地址为安装包中携带的应用资源位置
参数说明:
installPath 应用的安装目录
*返回值:**
int 0 成功

- (void)setInnerVersion:(NSString*)innerVersion;

设置HTML5+ SDK运行版本
参数说明:
innerVersion HTML5+ SDK运行版本号

- (int)setDocumethPath:(NSString*)documentPath;

设置HTML5+ SDK文档目录
参数说明:
documentPath 应用的文档目录
返回值:
int 0 成功

- (int)setDownloadPath:(NSString*)downlaodPath;

设置runtime下载目录
参数说明:
downloadPath 应用下载文件的路径
返回值:
int 0 成功

- (int)setAutoStartAppid:(NSString*)appid;

设置runtiem启动时自动运行的APP
参数说明:
appid 默认启动应用的APPID
返回值:
int 0 成功

- (int)setContainerView:(UIView*)containerView;

设置runtime根视图的父亲View
参数说明:
containerView 要显示HTML5+SDK的父View
返回值:
int 0 成功

- (int)setApp:(NSString*)appid documentPath:(NSString*)doucmentPath;

设置指定app的文档目录
参数说明:
appid 要设置的appid
doucmentPath 要设置的路径
返回值:
int 0 成功

- (int)regPluginWithName:(NSString*)pluginName  
             impClassName:(NSString*)impClassName  
                    type:(PDRExendPluginType)pluginType  
               javaScript:(NSString*)javaScript;

注册第三方扩展的HTML5+插件
参数说明:
pluginName 插件名称JS文件中定义的名字
impClassName 插件对应的实现类名
pluginType 插件类型 详情: PDRExendPluginType
javaScript js实现 为javascript文本
返回值:
int 0 成功

- (int)start;

正常启动runtime,使用改方法启动runtime具有全部功能,包括具有应用管理、窗口管理、插件管理、权限管理、资源管理等功能

- (int)startAsWebClient;

启动runtime,使用该方法启动的runtime不具有应用管理窗口管理功能,当需要显示页面时,需要自己创建PDRCoreAppFrame

PDRCoreAppFrame

常用接口说明

- (PDRCoreAppFrame*)initWithId:(NSString*)frameID loadURL:(NSString*)pagePath  frame:(CGRect)frame;

创建runtime页面
参数说明:
frameID 页面标示
pagePath 页面地址 支持http:// file:// 本地地址
frame 页面位置

@property(nonatomic, readonly)UIWebView *webView;

应用页面的WebView对象

收起阅读 »

Android平台以Widget方式集成HTML5+SDK方法

SDK

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

最新SDK下载地址
HTML5+ SDK 可以按照“独立应用”和“单页面”两种种方式进行集成,两种集成方式各有优点。

  • 独立应用集成方式:即Widget集成方式,开发者在集成后可在需要时启动HTML5+ SDK,显示指定目录下的5+ WebAPP。原生App中集成uni-app也需使用本集成方式。

  • 单页面集成方式:即Webview集成方式,用户可在需要时显示一个支持5+扩展API的Webview页面。使用单页面方式集成5+ SDK,在页面内不能调用plus.webview的API创建新的页面,其他5+API的使用不受影响。

集成代码请参考SDK内HBuilder-Integrate工程 点击下载最新SDK

集成步骤

一 将5+SDK导入现有原生工程

点击下载最新 5+ SDK
开发者需要根据WebApp中将要用到的5+API,将涉及的5+SDK的jar包和资源文件导入到现有的Android原生工程中,并根据需求修改工程的Androidmanifest.xml文件。
注意:集成SDK时需要将DCloudApplication配置到AndroidManifest.xml 的application节点中。也可继承DCloudApplication实现自己的application并配置。不配置DCloudApplication会导致SDK中业务逻辑无法正常运行!!!!

<application  
        android:name="io.dcloud.application.DCloudApplication" >

各API需要导入的资源和AndroidManifest.xml文件需要修改地方请点击查看以下文档
基础插件配置
推送插件配置
分享插件配置
授权登陆插件配置
地图插件配置
支付插件配置
定位插件配置

二 将WebApp导入现有原生工程

5+Webapp的导入现有工程的方法可以参考Android平台离线打包配置文档

三 5+SDK集成代码编写

1 创建一个继承自ICoreStatusListener的类,并实现其中的方法

ICoreStatusListener类用来监听5+内核的运行事件,当5+内核运行状态发生变化时会触发相应的方法,包含以下几个方法:

void onCoreReady(ICore arg0)
说明:
5+内核开始初始化时触发
SDK的方法必须在调用SDK.init()之后才可以调用,在使用中通常在onCoreReady方法触发时初始化5+SDK。
代码示例:

@Override  
    public void onCoreReady(ICore coreHandler) {  
        // 调用SDK的初始化接口,初始化5+ SDK  
        SDK.initSDK(coreHandler);  
        // 设置当前应用可使用的5+ API  
        SDK.requestAllFeature();  
    }

void onCoreInitEnd(ICore arg0)
说明:
5+内核初始化完成时触发
开发者要在5+内核初始化完成后才能调用SDK.startWebApp()等接口启动指定目录下的5+Webapp

boolean onCoreStop()
说明:
5+内核关闭时触发

Object onCreateSplash(Context pContextWrapper)
说明:
Splash页面创建时触发

void onCloseSplash()
说明:
Splash页面关闭时触发

2 调用SDK.startWebApp方法创建并启动5+WebApp

在5+内核初始化完毕后可调用SDK.startWebApp() 方法启动5+WebApp,方法会创建并返回一个IApp对象。开发者可通过调用IApp对象的方法获取该应用内已经打开的页面的IWebView对象也可以关闭这个5+WebApp。

应用创建后需要将应用的主页面添加到要显示该应用的父View中。

在调用startWebApp方法时可以传入一个IWebviewStateListener用来监听WebApp首页面加载的进度。

IApp对象创建成功后我们可以添加一个IAppStatusListener用来监听当前创建的IAPP对象的运行状态。

代码示例

    class WebappModeListener implements ICoreStatusListener, IOnCreateSplashView {  
    Activity activity;  
    View splashView = null;  
    ViewGroup rootView;  
    IApp app = null;  
    ProgressDialog pd = null;  

    public WebappModeListener(Activity activity, ViewGroup rootView) {  
        this.activity = activity;  
        this.rootView = rootView;  
    }  

    @Override  
    public void onCoreInitEnd(ICore coreHandler) {  

        // 表示Webapp的路径在 file:///android_asset/apps/HelloH5  
        String appBasePath = "/apps/HelloH5";  

        // 设置启动参数,可在页面中通过plus.runtime.arguments;方法获取到传入的参数  
        String args = "{url:'http://www.baidu.com'}";  

        // 启动启动独立应用的5+ Webapp  
        app = SDK.startWebApp(activity, appBasePath, args, new IWebviewStateListener() {  
            // 设置Webview事件监听,可在监监听内获取WebIvew加载内容的进度  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  
                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    // WebApp准备加载事件  
                    // 准备完毕之后添加webview到显示父View中,  
                    // 设置排版不显示状态,避免显示webview时html内容排版错乱问题  
                    View view = ((IWebview) pArgs).obtainApp().obtainWebAppRootView().obtainMainView();  
                    view.setVisibility(View.INVISIBLE);  
                    rootView.addView(view, 0);  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:  
                    pd = ProgressDialog.show(activity, "加载中", "0/100");  
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:  
                    // WebApp首页面加载进度变化事件  
                    if (pd != null) {  
                        pd.setMessage(pArgs + "/100");  
                    }  
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:  
                    // WebApp首页面加载完成事件  
                    if (pd != null) {  
                        pd.dismiss();  
                        pd = null;  
                    }  
                    // 页面加载完毕,设置显示webview  
                    app.obtainWebAppRootView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        }, this);  

        app.setIAppStatusListener(new IAppStatusListener() {  
            // 设置APP运行事件监听  
            @Override  
            public boolean onStop() {  
                // 应用运行停止时调用  
                rootView.removeView(app.obtainWebAppRootView().obtainMainView());  
                // TODO Auto-generated method stub  
                return false;  
            }  

            @Override  
            public void onStart() {  
                // 独立应用启动时触发事件  
            }  

            @Override  
            public void onPause(IApp arg0, IApp arg1) {  
                // WebApp暂停运行时触发事件  

            }  
        });  
    }  

    @Override  
    public void onCoreReady(ICore coreHandler) {  
        // 初始化SDK并将5+引擎的对象设置给SDK  
        SDK.initSDK(coreHandler);  
        //   
        SDK.requestAllFeature();  
    }  

    @Override  
    public boolean onCoreStop() {  
        // 当返回false时候回关闭activity  
        return false;  
    }  

    @Override  
    public Object onCreateSplash(Context pContextWrapper) {  
        splashView = new FrameLayout(activity);  
        splashView.setBackgroundResource(RInformation.DRAWABLE_SPLASH);  
        rootView.addView(splashView);  
        return null;  
    }  

    @Override  
    public void onCloseSplash() {  
        rootView.removeView(splashView);  
    }  
}

3 启动5+ 内核

5+SDK在使用前首先要进行5+内核的初始化,在启动时需要添加一个ICoreStatusListener用来监听5+内核启动状态。io.dcloud.feature.internal.sdk.SDK类的方法需要在5+内核初始化后才能够调用。

同时还需要在5+内核初始化后指定当前将要使用那种集成方式。独立应用集成方式使用SDK.IntegratedMode.WEBAPP 参数指定当前SDK将使用独立应用方式启动5+WebApp。

示例中5+ SDK是在Activity的onCreate方法中初始化,在具体使用中可在任意时刻初始化5+内核。

public class SDK_WebApp extends Activity {  

    boolean doHardAcc = true;  
    EntryProxy mEntryProxy = null;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        if (mEntryProxy == null) {  
            FrameLayout f = new FrameLayout(this);  
            // 创建5+内核运行事件监听  
            WebappModeListener wm = new WebappModeListener(this, f);  
            // 初始化5+内核  
            mEntryProxy = EntryProxy.init(this, wm);  
            // 启动5+内核  
            mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBAPP, null);  
            setContentView(f);  
        }  
    }  
}

4 传递Activity事件给5+ SDK

开发者在集成5+SDK时可根据需要将Activity的事件传递给5+SDK引擎,5+引擎会将事件传递给监听事件的Webview,由Webview进行事件处理。

@Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        return mEntryProxy.onActivityExecute(this, SysEventType.onCreateOptionMenu, menu);  
    }  

    @Override  
    public void onPause() {  
        super.onPause();  
        mEntryProxy.onPause(this);  
    }  

    @Override  
    public void onResume() {  
        super.onResume();  
        mEntryProxy.onResume(this);  
    }  

    public void onNewIntent(Intent intent) {  
        super.onNewIntent(intent);  
        if (intent.getFlags() != 0x10600000) {  
        // 非点击icon调用activity时才调用newintent事件  
            mEntryProxy.onNewIntent(this, intent);  
        }  
    }  

    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        mEntryProxy.onStop(this);  
    }  

    @Override  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyDown, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyDown(keyCode, event);  
    }  

    @Override  
    public boolean onKeyUp(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyUp, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyUp(keyCode, event);  
    }  

    @Override  
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyLongPress, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyLongPress(keyCode, event);  
    }  

    public void onConfigurationChanged(Configuration newConfig) {  
        try {  
            int temp = this.getResources().getConfiguration().orientation;  
            if (mEntryProxy != null) {  
                mEntryProxy.onConfigurationChanged(this, temp);  
            }  
            super.onConfigurationChanged(newConfig);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        mEntryProxy.onActivityExecute(this, SysEventType.onActivityResult, new Object[] { requestCode, resultCode, data });  
    }  

Android 平台5+SDK常用接口说明

Android 平台5+SDK常用接口及使用示例请点击

继续阅读 »

5+ SDKWidget方式集成WebView方式集成 将不再继续维护支持,相关功能已迁移到uni小程序 SDK,因此建议开发者尽快将应用升级到uni-app项目。

最新SDK下载地址
HTML5+ SDK 可以按照“独立应用”和“单页面”两种种方式进行集成,两种集成方式各有优点。

  • 独立应用集成方式:即Widget集成方式,开发者在集成后可在需要时启动HTML5+ SDK,显示指定目录下的5+ WebAPP。原生App中集成uni-app也需使用本集成方式。

  • 单页面集成方式:即Webview集成方式,用户可在需要时显示一个支持5+扩展API的Webview页面。使用单页面方式集成5+ SDK,在页面内不能调用plus.webview的API创建新的页面,其他5+API的使用不受影响。

集成代码请参考SDK内HBuilder-Integrate工程 点击下载最新SDK

集成步骤

一 将5+SDK导入现有原生工程

点击下载最新 5+ SDK
开发者需要根据WebApp中将要用到的5+API,将涉及的5+SDK的jar包和资源文件导入到现有的Android原生工程中,并根据需求修改工程的Androidmanifest.xml文件。
注意:集成SDK时需要将DCloudApplication配置到AndroidManifest.xml 的application节点中。也可继承DCloudApplication实现自己的application并配置。不配置DCloudApplication会导致SDK中业务逻辑无法正常运行!!!!

<application  
        android:name="io.dcloud.application.DCloudApplication" >

各API需要导入的资源和AndroidManifest.xml文件需要修改地方请点击查看以下文档
基础插件配置
推送插件配置
分享插件配置
授权登陆插件配置
地图插件配置
支付插件配置
定位插件配置

二 将WebApp导入现有原生工程

5+Webapp的导入现有工程的方法可以参考Android平台离线打包配置文档

三 5+SDK集成代码编写

1 创建一个继承自ICoreStatusListener的类,并实现其中的方法

ICoreStatusListener类用来监听5+内核的运行事件,当5+内核运行状态发生变化时会触发相应的方法,包含以下几个方法:

void onCoreReady(ICore arg0)
说明:
5+内核开始初始化时触发
SDK的方法必须在调用SDK.init()之后才可以调用,在使用中通常在onCoreReady方法触发时初始化5+SDK。
代码示例:

@Override  
    public void onCoreReady(ICore coreHandler) {  
        // 调用SDK的初始化接口,初始化5+ SDK  
        SDK.initSDK(coreHandler);  
        // 设置当前应用可使用的5+ API  
        SDK.requestAllFeature();  
    }

void onCoreInitEnd(ICore arg0)
说明:
5+内核初始化完成时触发
开发者要在5+内核初始化完成后才能调用SDK.startWebApp()等接口启动指定目录下的5+Webapp

boolean onCoreStop()
说明:
5+内核关闭时触发

Object onCreateSplash(Context pContextWrapper)
说明:
Splash页面创建时触发

void onCloseSplash()
说明:
Splash页面关闭时触发

2 调用SDK.startWebApp方法创建并启动5+WebApp

在5+内核初始化完毕后可调用SDK.startWebApp() 方法启动5+WebApp,方法会创建并返回一个IApp对象。开发者可通过调用IApp对象的方法获取该应用内已经打开的页面的IWebView对象也可以关闭这个5+WebApp。

应用创建后需要将应用的主页面添加到要显示该应用的父View中。

在调用startWebApp方法时可以传入一个IWebviewStateListener用来监听WebApp首页面加载的进度。

IApp对象创建成功后我们可以添加一个IAppStatusListener用来监听当前创建的IAPP对象的运行状态。

代码示例

    class WebappModeListener implements ICoreStatusListener, IOnCreateSplashView {  
    Activity activity;  
    View splashView = null;  
    ViewGroup rootView;  
    IApp app = null;  
    ProgressDialog pd = null;  

    public WebappModeListener(Activity activity, ViewGroup rootView) {  
        this.activity = activity;  
        this.rootView = rootView;  
    }  

    @Override  
    public void onCoreInitEnd(ICore coreHandler) {  

        // 表示Webapp的路径在 file:///android_asset/apps/HelloH5  
        String appBasePath = "/apps/HelloH5";  

        // 设置启动参数,可在页面中通过plus.runtime.arguments;方法获取到传入的参数  
        String args = "{url:'http://www.baidu.com'}";  

        // 启动启动独立应用的5+ Webapp  
        app = SDK.startWebApp(activity, appBasePath, args, new IWebviewStateListener() {  
            // 设置Webview事件监听,可在监监听内获取WebIvew加载内容的进度  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  
                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    // WebApp准备加载事件  
                    // 准备完毕之后添加webview到显示父View中,  
                    // 设置排版不显示状态,避免显示webview时html内容排版错乱问题  
                    View view = ((IWebview) pArgs).obtainApp().obtainWebAppRootView().obtainMainView();  
                    view.setVisibility(View.INVISIBLE);  
                    rootView.addView(view, 0);  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:  
                    pd = ProgressDialog.show(activity, "加载中", "0/100");  
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:  
                    // WebApp首页面加载进度变化事件  
                    if (pd != null) {  
                        pd.setMessage(pArgs + "/100");  
                    }  
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:  
                    // WebApp首页面加载完成事件  
                    if (pd != null) {  
                        pd.dismiss();  
                        pd = null;  
                    }  
                    // 页面加载完毕,设置显示webview  
                    app.obtainWebAppRootView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        }, this);  

        app.setIAppStatusListener(new IAppStatusListener() {  
            // 设置APP运行事件监听  
            @Override  
            public boolean onStop() {  
                // 应用运行停止时调用  
                rootView.removeView(app.obtainWebAppRootView().obtainMainView());  
                // TODO Auto-generated method stub  
                return false;  
            }  

            @Override  
            public void onStart() {  
                // 独立应用启动时触发事件  
            }  

            @Override  
            public void onPause(IApp arg0, IApp arg1) {  
                // WebApp暂停运行时触发事件  

            }  
        });  
    }  

    @Override  
    public void onCoreReady(ICore coreHandler) {  
        // 初始化SDK并将5+引擎的对象设置给SDK  
        SDK.initSDK(coreHandler);  
        //   
        SDK.requestAllFeature();  
    }  

    @Override  
    public boolean onCoreStop() {  
        // 当返回false时候回关闭activity  
        return false;  
    }  

    @Override  
    public Object onCreateSplash(Context pContextWrapper) {  
        splashView = new FrameLayout(activity);  
        splashView.setBackgroundResource(RInformation.DRAWABLE_SPLASH);  
        rootView.addView(splashView);  
        return null;  
    }  

    @Override  
    public void onCloseSplash() {  
        rootView.removeView(splashView);  
    }  
}

3 启动5+ 内核

5+SDK在使用前首先要进行5+内核的初始化,在启动时需要添加一个ICoreStatusListener用来监听5+内核启动状态。io.dcloud.feature.internal.sdk.SDK类的方法需要在5+内核初始化后才能够调用。

同时还需要在5+内核初始化后指定当前将要使用那种集成方式。独立应用集成方式使用SDK.IntegratedMode.WEBAPP 参数指定当前SDK将使用独立应用方式启动5+WebApp。

示例中5+ SDK是在Activity的onCreate方法中初始化,在具体使用中可在任意时刻初始化5+内核。

public class SDK_WebApp extends Activity {  

    boolean doHardAcc = true;  
    EntryProxy mEntryProxy = null;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        if (mEntryProxy == null) {  
            FrameLayout f = new FrameLayout(this);  
            // 创建5+内核运行事件监听  
            WebappModeListener wm = new WebappModeListener(this, f);  
            // 初始化5+内核  
            mEntryProxy = EntryProxy.init(this, wm);  
            // 启动5+内核  
            mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBAPP, null);  
            setContentView(f);  
        }  
    }  
}

4 传递Activity事件给5+ SDK

开发者在集成5+SDK时可根据需要将Activity的事件传递给5+SDK引擎,5+引擎会将事件传递给监听事件的Webview,由Webview进行事件处理。

@Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        return mEntryProxy.onActivityExecute(this, SysEventType.onCreateOptionMenu, menu);  
    }  

    @Override  
    public void onPause() {  
        super.onPause();  
        mEntryProxy.onPause(this);  
    }  

    @Override  
    public void onResume() {  
        super.onResume();  
        mEntryProxy.onResume(this);  
    }  

    public void onNewIntent(Intent intent) {  
        super.onNewIntent(intent);  
        if (intent.getFlags() != 0x10600000) {  
        // 非点击icon调用activity时才调用newintent事件  
            mEntryProxy.onNewIntent(this, intent);  
        }  
    }  

    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        mEntryProxy.onStop(this);  
    }  

    @Override  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyDown, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyDown(keyCode, event);  
    }  

    @Override  
    public boolean onKeyUp(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyUp, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyUp(keyCode, event);  
    }  

    @Override  
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {  
        boolean _ret = mEntryProxy.onActivityExecute(this, SysEventType.onKeyLongPress, new Object[] { keyCode, event });  
        return _ret ? _ret : super.onKeyLongPress(keyCode, event);  
    }  

    public void onConfigurationChanged(Configuration newConfig) {  
        try {  
            int temp = this.getResources().getConfiguration().orientation;  
            if (mEntryProxy != null) {  
                mEntryProxy.onConfigurationChanged(this, temp);  
            }  
            super.onConfigurationChanged(newConfig);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        mEntryProxy.onActivityExecute(this, SysEventType.onActivityResult, new Object[] { requestCode, resultCode, data });  
    }  

Android 平台5+SDK常用接口说明

Android 平台5+SDK常用接口及使用示例请点击

收起阅读 »

Android平台以WebView方式集成HTML5+SDK方法

SDK

5+ SDKWidget方式集成WebView方式集成 将不再继续维护与支持!仅支持离线打包APP

具体参考离线打包

5+ SDKWidget方式集成WebView方式集成 将不再继续维护与支持!仅支持离线打包APP

具体参考离线打包

crosswalk可以作为插件集成进来吗,和5+runtime各有什么优劣?

crosswalk

更新于2017年2月,intel官方已经宣布终止crosswalk项目。
此问题没有继续讨论的必要了。

====================================================================

有不少开发者问到crosswalk是否可以作为插件集成到HBuilder中。
首先,通过我们的原生插件集成方案,使用HBuilder的离线打包和插件开发模型,可以把crosswalk当作一个原生插件集成进来。
但是需要一点原生开发的知识,在问答系统里搜索"插件开发"就有教程。
注意即使集成后crosswalk的webview和5+的webview并不能同时出现,一个app里的某个页面,要不用cw渲染,要不用5+渲染。
其次,我们曾有想法要把crosswalk做一个标准插件,在cw上扩展5+的api,包括Native.js,但到了2016年后,团队已经放弃了这个想法,投入产出太低。

crosswalk的技术路线:是集成chromium内核替换Android webkit浏览器内核,由于chomuin的执行效率高于webkit,所以性能也得到提升。
cw主要解决的问题有2个:

  1. Android4.0-Android4.3之间的webview性能差的问题,主要是div动画、canvas、webgl。Android4以前用不了cw,Android4.4起用不着cw。
  2. 浏览器的兼容性问题。

对于5+而言,这2个问题并非本质问题。

  1. Android4.4已经成为主流,Android6.0也在快速增长,Android4.0-4.3之间的canvas、webgl改进价值虽然存在,但越来越弱。而div动画低效的问题在5+里一直是用原生view动画来解决,性能体验更好。
    目前如果是为了优化canvas,可以采用的一个方案是:把canvas界面独立出来,把cw通过sdk方式进入,然后独立的界面用cw渲染,如果需要5+api,通过跨窗体通信让其他webview执行5+api。
  2. 浏览器兼容性问题在手机上并不严重,都是webkit内核,不要用太新的css3语法,一般不会有兼容性问题。HBuilder也有浏览器兼容性语法提示,写代码时注意看下代码助手右侧的提示。

但引入cw进5+的问题很多:

  1. cw上扩展plus和Native.js工作量非常大
  2. cw上真机运行、云打包、sdk配套的工作量非常大
  3. cw的体积最小也得9M。而目前HBuilder基座如果不带个推那些插件,才1m的体积。

利弊权衡,我们决定放弃引入cw的计划,把精力投入到更容易产生效果的地方。

继续阅读 »

更新于2017年2月,intel官方已经宣布终止crosswalk项目。
此问题没有继续讨论的必要了。

====================================================================

有不少开发者问到crosswalk是否可以作为插件集成到HBuilder中。
首先,通过我们的原生插件集成方案,使用HBuilder的离线打包和插件开发模型,可以把crosswalk当作一个原生插件集成进来。
但是需要一点原生开发的知识,在问答系统里搜索"插件开发"就有教程。
注意即使集成后crosswalk的webview和5+的webview并不能同时出现,一个app里的某个页面,要不用cw渲染,要不用5+渲染。
其次,我们曾有想法要把crosswalk做一个标准插件,在cw上扩展5+的api,包括Native.js,但到了2016年后,团队已经放弃了这个想法,投入产出太低。

crosswalk的技术路线:是集成chromium内核替换Android webkit浏览器内核,由于chomuin的执行效率高于webkit,所以性能也得到提升。
cw主要解决的问题有2个:

  1. Android4.0-Android4.3之间的webview性能差的问题,主要是div动画、canvas、webgl。Android4以前用不了cw,Android4.4起用不着cw。
  2. 浏览器的兼容性问题。

对于5+而言,这2个问题并非本质问题。

  1. Android4.4已经成为主流,Android6.0也在快速增长,Android4.0-4.3之间的canvas、webgl改进价值虽然存在,但越来越弱。而div动画低效的问题在5+里一直是用原生view动画来解决,性能体验更好。
    目前如果是为了优化canvas,可以采用的一个方案是:把canvas界面独立出来,把cw通过sdk方式进入,然后独立的界面用cw渲染,如果需要5+api,通过跨窗体通信让其他webview执行5+api。
  2. 浏览器兼容性问题在手机上并不严重,都是webkit内核,不要用太新的css3语法,一般不会有兼容性问题。HBuilder也有浏览器兼容性语法提示,写代码时注意看下代码助手右侧的提示。

但引入cw进5+的问题很多:

  1. cw上扩展plus和Native.js工作量非常大
  2. cw上真机运行、云打包、sdk配套的工作量非常大
  3. cw的体积最小也得9M。而目前HBuilder基座如果不带个推那些插件,才1m的体积。

利弊权衡,我们决定放弃引入cw的计划,把精力投入到更容易产生效果的地方。

收起阅读 »

HBuilder支持php吗?php插件怎么用?

PHP

工具-插件安装,选php插件,可以编辑php文档。
支持php高亮着色、代码提示、转到定义。

  1. 插件安装失败?
    插件安装过程中万一出问题导致插件安装失败,请点击工具-插件安装,点里面的卸载插件链接,然后在新窗体选择aptana php插件。卸载并重启,然后再重新安装。

  2. 插件安装成功但仍然php不高亮?
    如果确定安装了php插件,但php文档打开还是不高亮,说明仍然是使用了HTML编辑器打开了php文档,那么在工具-选项,搜索文件关联,在里面配置php文件和PHP Editor的关联。
    也可以在项目管理器里对文件点右键,打开方式,里面选PHP Editor。

  3. php无法运行?使用ctrl+r运行在浏览器或使用ctrl+p运行在边改边看下不能识别?
    HBuilder内置的web服务器只能看简单的HTML、js、css,不具备php解析能力,需要配置外部专业web服务器才能解析php。
    需要在工具-选项里搜索web服务器,新建一个外部web服务器,配置tomcat等外部web服务器的参数。

继续阅读 »

工具-插件安装,选php插件,可以编辑php文档。
支持php高亮着色、代码提示、转到定义。

  1. 插件安装失败?
    插件安装过程中万一出问题导致插件安装失败,请点击工具-插件安装,点里面的卸载插件链接,然后在新窗体选择aptana php插件。卸载并重启,然后再重新安装。

  2. 插件安装成功但仍然php不高亮?
    如果确定安装了php插件,但php文档打开还是不高亮,说明仍然是使用了HTML编辑器打开了php文档,那么在工具-选项,搜索文件关联,在里面配置php文件和PHP Editor的关联。
    也可以在项目管理器里对文件点右键,打开方式,里面选PHP Editor。

  3. php无法运行?使用ctrl+r运行在浏览器或使用ctrl+p运行在边改边看下不能识别?
    HBuilder内置的web服务器只能看简单的HTML、js、css,不具备php解析能力,需要配置外部专业web服务器才能解析php。
    需要在工具-选项里搜索web服务器,新建一个外部web服务器,配置tomcat等外部web服务器的参数。

收起阅读 »