Day 10: PhoneGap —— 开发手机应用如此简单

简介: 我们发现了比较有趣的系列文章《30天学习30种新技术》,准备翻译,一天一篇更新,年终礼包。下面是第10天的内容。今天又是“30天学习30种新技术”的一天。长期以来,我觉得手机开发很恐怖,大部分应用都没有商业模式。事实上,编写手机应用从来都不能让我兴奋。然而,考虑到手机领域的飞速发展,以及更多的人通过手机而不是桌面访问互联网,我决定尝试下手机开发。我的手机开发之旅将从PhoneGap 起步。

编者注:我们发现了比较有趣的系列文章《30天学习30种新技术》,准备翻译,一天一篇更新,年终礼包。下面是第10天的内容。


今天又是“30天学习30种新技术”的一天。长期以来,我觉得手机开发很恐怖,大部分应用都没有商业模式。事实上,编写手机应用从来都不能让我兴奋。然而,考虑到手机领域的飞速发展,以及更多的人通过手机而不是桌面访问互联网,我决定尝试下手机开发。我的手机开发之旅将从PhoneGap 起步。

本文首先介绍 PhoneGap 的基本情况,接着我们使用 PhoneGap 开发一个手机应用。

image.png

手机应用

我们将为是“30天学习30种新技术”开发一个手机阅读器。用户可以将该应用安装在Android、Symbian、webOS或Windows设备上。该手机应用可以在 https://build.phonegap.com/apps/635001/share 下载。

这个应用可以做到:

给出所有已经发布的系列文章的列表。用户点击列表中的条目则调用浏览器打开链接。

image.png

读者可以使用该应用回馈想法。

image.png

什么是 PhoneGap?

PhoneGap是一个自由开源的手机开发框架,可以使用标准的 web 技术,即 HTML,CSS 和 JavaScript 进行手机应用开发。


它将应用的web资源封装在原生应用中,这些应用可以提交到各大应用商店。更重要的是,我们可以通过 PhoneGap 进行跨平台的手机应用开发。这意味着,在理想情况下,我们只需编写一次手机应用,就可以将该应用移植到多个平台。例如,我为Android设备编写了这个应用,然而使用 PhoneGap build,我同时为其他设备打包了应用。大多数标准的设备特性,例如照相、地理位置、存储等都由JavaScript API提供了。取决与目标设备,PhoneGap提供的JavaScript API会有所不同。


关于 PhoneGap 的一些事实:

  1. 2009年,Nitobi开发了PhoneGap框架。
  2. 2011年10月,Adobe 购买了 Nitobi(PhoneGap)背后的公司。
  3. Adobe 将 PhoneGap 贡献给 Apache 基金会。
  4. 此开源项目被命名为 Apache Cordova.
  5. PhoneGap 是 Adobe 完成的 开源项目 Apache Cordova 的实现。 PhoneGap的核心使用 Apache Cordova.
  6. PhoneGap使用基于插件的架构。所有设备的特性以插件方式提供。本文中,我们将使用一些插件。


为什么我关心PhoneGap?

  1. 没有必要为每个平台学习一种原生的开发环境。如果开发者打算针对多个平台,那么PhoneGap的跨平台特性可以节约开发者大量的时间和精力。我了解HTML、CSS和JavaScript,可以很平滑地进入手机世界。
  2. 对于开发基于REST API的CRUD手机应用,PhoneGap很好用。
  3. 它不强迫开发者选择特定的CSS库。开发者可以使用任何他们喜欢的移动端的库。本应用中我使用jQuery mobile。


PhoneGap Prerequisites

PhoneGap依赖NodeJS,我们需要使用npm来安装PhoneGap。Npm是NodeJS的包管理器,新版的NodeJS自带npm。你可以从官网下载最新版的NodeJS。

你需要同时安装目标机器的SDK。例如,如果我们创建Android程序,我们需要在操作系统上安装 Android 开发者工具。PhoneGap 使用 SDK 为目标平台构建包。


PhoneGap起步

使用如下命令安装PhoneGap:

sudo npm install -g phonegap

以上命令将全局安装 phonegap 包,这样任何目录下都可以使用 phonegap。

安装插件需要安装Cordova.

使用如下命令安装Cordova。

sudo npm install -g cordova

GitHub 仓库

今天的demo应用的代码在github上。


创建PhoneGap应用

phonegap命令行提供了使用模板创建新phonegap项目的命令:

phonegap create reader --id io.reader--name Reader30

上面的命令会创建一个reader目录。

io.readerReader30是可选的:io.reader提供了一个类似反域名的识别符,而Reader30提供了应用的显示文本。

应用的文件结构如下:

image.png

让我们看下这些生成的目录:

  1. merges文件夹存放专门针对某个特定平台的资源。例如,我们可能使用merges来为Android设备改变字体大小。
  2. platforms目录存放平台构建文件。
  3. plugins存放应用使用的插件。当我们安装一个插件的时候,插件会被存放在这个目录下。
  4. www目录存放应用资源,例如htmlcssjsimg。这是我们要花最多时间的目录。config.xml包含了生成和分发应用所需的元信息。元信息也包括应用名称、描述、作者详情、应用权限等信息。在这里可以看到一个完整的列表。

运行下列命令即可在android上运行应用:

phonegap run android

这是我们的android平台阅读器应用的第一次构建。如果有真实设备连接,应用会跑在真实设备上。如果没有设备,会启动Android虚拟器,应用会被部署到虚拟器上。

image.png

注意:Android虚拟器的性能相当差劲,所以我建议你总是连接上你的真实手机设备。Grant Shipley的博客提供了一些加速Android虚拟器的信息。


开发手机应用

如上所述,我们的应用有两个页面。我们先开发第一个。


博客列表

初始页面列出所有已发布文章的列表。我们修改下index.html,请从我的github仓库中复制css和javascript资源。

<!DOCTYPE html>

<html>

<head>

 <metacharset="utf-8">

 <metaname="viewport"content="width=device-width, initial-scale=1">

 <metaname="apple-mobile-web-app-capable"content="yes">

 <metaname="apple-mobile-web-app-status-bar-style"content="black">

 <title>Learn 30 technologies in 30 days</title>

 <linkrel="stylesheet"href="css/vendor/jquery.mobile-1.3.1.min.css">

 <linkrel="stylesheet"href="css/vendor/jquery.loadmask.css">

</head>

<body>

<divdata-role="page"id="mainPage">

       <divdata-role="header"data-position="fixed">

           <h1>30Technologies30Days</h1>

           <ahref="#feeback"data-icon="edit"data-theme="b"class="feedback ui-btn-right"data-role="button"data-inline="true"data-ajax="false">Feedback</a>

           <divdata-role="navbar">

           <ul>

               <li><ahref="#home"class="home ui-btn-active"data-icon="home">Home</a></li>

           </ul>

       </div>

       </div>

       <divid="main"data-role="content">

       </div>

       <divdata-theme="a"data-role="footer">

       <h3>

           © Shekhar Gulati 2013

       </h3>

   </div>

</div>

<scripttype="text/x-mustache-template"id="home-template">

 <ulid="blogs"data-role="listview"data-filter="true"data-filter-placeholder="Search blogs..."data-inset="true">

 </ul>

</script>

<scripttype="text/x-mustache-template"id="blog-template">

<li>

   <ahref="{{url}}"target="_blank">

       <h3>{{title}}</h3>

       <p><strong>{{publishedOn}}</strong></p>

   </li>

</script>

<scriptsrc="phonegap.js"></script>

<scriptsrc="js/vendor/jquery-1.9.1.min.js"></script>

<scriptsrc="js/vendor/jquery.mobile-1.3.1.min.js"></script>

<scriptsrc="js/vendor/jquery.ui.map.js"></script>

<scriptsrc="js/vendor/jquery.loadmask.min.js"></script>

<scriptsrc="js/vendor/jquery.timeago.js"></script>

<scripttype="text/javascript"src="js/vendor/mustache.js"></script>

<scripttype="text/javascript"src="js/app.js"></script>

</body>

</html>

上面的html导入了所需的css和javascript文件。它使用jQuery mobile达到原生效果。我们同时定义了一个mustache模板来呈现列表。

应用相关的javascript代码放在 app.js文件中:

$(document).ready(function(){

   homeView();

   $('.home').on('tap', renderHomeView);  

   $('.feedback').on('tap', renderFeedbackFormView);

});

functionrenderHomeView(event){

   event.preventDefault();

   homeView();

}

functionhomeView(){

   $('#main').empty();

   $('.home').addClass("ui-btn-active");

   $('#main').html(template("home"));

   var url = 'http://30technologiesin30days-t20.rhcloud.com/api/v1/blogs';

   $.mobile.loading( 'show',{});

   $.ajax({

       url : url,

       dataType : 'json',

       success : function(data){

           $.mobile.loading( 'hide',{});

           $.each(data , function(i , obj){

               var template = $("#blog-template").html();

               obj.publishedOn = $.timeago(obj.publishedOn);

               $("#blogs").append(Mustache.to_html(template,obj));

               $('#blogs').listview('refresh');

           });

       },

       error : function(XMLHttpRequest,textStatus, errorThrown) {  

           $.mobile.loading( 'hide',{text:"Fetching blogs.."});  

           alert("Something wrong happended on the server. Try again..");  

       }

   })

   $('#main').trigger('create');

}

functiontemplate(name) {

       returnMustache.compile($('#'+name+'-template').html());

}

functionshowNotification(message , title){

       if (navigator.notification) {

       navigator.notification.alert(message, null, title, 'OK');

       } else {

           alert(title ? (title + ": " + message) : message);

       }

}

上面的app.js做了这些事:

  1. 绑定了文档的就绪事件。一旦就绪,将渲染初始页面。
  2. 初始页面进行REST调用以获取所有博客。我们使用jQuery来调用REST。
  3. 接受信息后,我们创建一个列表。最后刷新下。


允许访问REST服务

PhoneGap默认不允许应用访问远程资源。这就意味着应用无法进行REST调用。为了允许应用进行REST调用,我们需要允许它访问。我们可以通过通配符*允许应用访问一切资源。请参阅文档了解更多信息。

config.xml下修改访问控制信息:

<access origin="*" />

安装插件

应用使用一些插件来访问特定设备的特性。

cordova plugin add org.apache.cordova.geolocation

cordova plugin add org.apache.cordova.dialogs

  1. 第一个命令安装了geolocation插件。Geolocation提供了设备位置的信息,例如经度和纬度。我们稍后将使用此特性。参阅文档 了解更多信息。
  2. 第二个命令安装了dialogs插件。dialogs插件提供了原生的设备视觉提醒。参阅文档了解更多信息。


反馈提交表单

第二个屏幕允许用户提交反馈。

我们增加了一个反馈表单,来记录用户的反馈。

<scripttype="text/x-mustache-template"id="feedback-form-template">

   <formaction=""id="feedbackForm">

           <divdata-role="fieldcontain">

               <labelfor="name">

                   Describe

               </label>

               <inputtype="text"name="name"id="name"placeholder="Full Name eg. Shekhar Gulati ">

           </div>

           <divdata-role="fieldcontain">

               <labelfor="description">

                   Describe

               </label>

               <textareaname="description"id="description"placeholder="Message for author.."></textarea>

           </div>

           <divid="checkboxes1"data-role="fieldcontain">

               <fieldsetdata-role="controlgroup"data-type="vertical">

                   <legend>

                       Share my location

                   </legend>

                   <inputid="sharemylocation"name="sharemylocation"type="checkbox"value="true">

                   <labelfor="sharemylocation">

                       Share

                   </label>

               </fieldset>

           </div>

           <buttonid="create-button"data-inline="true">Feedback</button>

       </form>

</script>

修改app.js文件,监听tap事件。

$(document).ready(function(){

   homeView();

   $('.home').on('tap', renderHomeView);  

   $('.feedback').on('tap', renderFeedbackFormView);

});

functionrenderFeedbackFormView(event){

   event.preventDefault();

   $('#main').empty();

   $('#main').html(template("feedback-form"));

   $('#main').trigger('create');

   $('#create-button').bind('tap',shareFeedback);

}

functionshareFeedback(event){

       event.preventDefault();

       $('#feedbackForm').mask();        

       var name = $('#name').val();

       var description = $('textarea#description').val();

       var sharemylocation = $("#sharemylocation:checked").val() === undefined ? "false" : "true";

       var data = {name:name , description:description , lngLat :[]};

       if(sharemylocation === "true"){

           navigator.geolocation.getCurrentPosition(function(position){

               var lngLat = [position.coords.longitude , position.coords.latitude];

               data.lngLat = lngLat;

               postFeedback(data);

           } , function(error){

                   alert('code: '    + error.code    + '\n' +

                     'message: ' + error.message + '\n');

                   $('#feedbackForm').unmask();

           });

       }else{

           postFeedback(data);

       }

}

functionpostFeedback(data){

   $.ajax({

               type : 'POST',

               url : 'http://30technologiesin30days-t20.rhcloud.com/api/v1/feedback',

               crossDomain : true,

               data : JSON.stringify(data),

               dataType : 'json',

               contentType: "application/json",

               success : function(data){

                   $('#feedbackForm').unmask();

                   $('#feedbackForm')[0].reset();

                   showNotification('Received your feedback', 'Info');

                   homeView();

               },

               error : function(XMLHttpRequest,textStatus, errorThrown) {    

                 $('#feedbackForm').unmask();

                 alert("Error status :"+textStatus);  

                 alert("Error type :"+errorThrown);  

               }

       });

}

当反馈表单提交后,我们从表单获取了数据。如果用户勾选了“分享我的位置”,我们会使用geolocation插件来获取用户的位置。最后,我们将POST请求提交到我们的REST服务。一旦成功提交,我们显示一个提醒。

运行应用

现在我们可以在设备上安装和运行应用了。使用如下命令:

phonegap run android

相关文章
|
7月前
|
Android开发 开发者 UED
个人开发 App 成功上架手机应用市场的关键步骤
个人开发 App 成功上架手机应用市场的关键步骤
|
1月前
|
Web App开发 定位技术 iOS开发
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
66 1
|
2月前
|
Web App开发 缓存 前端开发
拿下奇怪的前端报错(六):多摄手机webrtc拉取视频流会导致应用崩溃,从而无法进行人像扫描
本文介绍了一种解决手机摄像头切换导致应用崩溃的问题的方法。针对不支持facingMode配置的四摄手机,通过缓存和序号切换的方式,确保应用在特定设备上不会频繁崩溃,提升用户体验。
|
2月前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
965 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
|
3月前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
102 12
|
4月前
|
Web App开发 Android开发
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
实时数据传输在互联网中至关重要,不仅支持即时通讯如QQ、微信的文字与图片传输,还包括音视频通信。一对一通信常采用WebRTC技术,如《Android Studio开发实战》中的App集成示例;而一对多的在线直播则需部署独立的流媒体服务器,使用如SRT等协议。SRT因其优越的直播质量正逐渐成为主流。本文档概述了SRT协议的使用,包括通过OBS Studio和SRT Streamer进行SRT直播推流的方法,并展示了推流与拉流的成功实例。更多细节参见《FFmpeg开发实战》一书。
78 1
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
|
4月前
|
存储 监控 开发工具
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
78 0
|
5月前
|
存储 移动开发 Android开发
使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
|
5月前
|
前端开发 小程序
【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)
【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)
423 0
|
6月前
|
Web App开发 移动开发 编解码
FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
本文讨论了实时数据传输在互联网中的重要性,如即时通讯和在线直播。一对一通信通常使用WebRTC技术,但一对多直播需要流媒体服务器和特定协议,如RTSP、RTMP、SRT或RIST。RTMP由于其稳定性和早期普及,成为国内直播的主流。文章通过实例演示了如何使用OBS Studio和RTMP Streamer进行RTMP推流,并对比了不同流媒体传输协议的优缺点。推荐了两本关于FFmpeg和Android开发的书籍以供深入学习。
105 0
FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
下一篇
DataWorks