APP教编程的app收获怎么写

登录以解锁更多InfoQ新功能
获取更新并接收通知
给您喜爱的内容点赞
关注您喜爱的编辑与同行
966,690 六月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
Apple Watch两个月开发的一些收获总结
Apple Watch两个月开发的一些收获总结
0&他的粉丝
日. 估计阅读时间:
都知道硅谷人工智能做的好,你知道
也值得参考吗?QCon上海带你探索其中的奥义
相关厂商内容
相关赞助商
自己来现在的公司实习到今,主要做的工作几乎都和iOS8新特性有关,毕竟现在公司这个项目实在是太成熟了,摸熟悉也需要一个过程。包括之前的Today Widget,到后来的Handoff,包括因为要适配iPhone6做的适配方面的调研等等,都是从去年WWDC之后的新事物,转眼就到2015年的WWDC了,不知道今年会有哪些革新的新事物。
闲话说到这里吧,是时候总结一下这两个月的收获和掉坑了。
目前开发者网站上的这几部分我觉得是开发Watch 必须学习几遍的东西,还有苹果开发者论坛也是一个不错的交流地方。
1. Watch Main App
在iPhone上,主程序是大哥,其他的小扩展必须让路,但是在Watch上,是不是大哥还要看这个APP主要的功能。如果是一个阅读性质的APP,主程序在手表上作用还真不大,例如阅读新闻等等。如果是这类的应用,想在Watch上出彩,或者让用户使用的次数多一些,就要靠良好的Notification体验,以及极其方便用户生活的Glance了。
(1)以Page-Based方式启动Watch App
如上图,现在手上要做的一个交互是,App启动的时候是六个页面,用户可以左右滑动来切换,这里就需要在MainInterfaceController中使用下边这个方法了。
[WKInterfaceController reloadRootControllersWithNames:
_controllersArrays contexts:_contextsArray];
在Watch上页面之间转换传值,很重要的一个纽带就是这个context,传递有用的信息和标识,这个方法中,我传递进入六个controller的interface builder identifier,以及事前拼好的六个context。
因为Watch App 的打开可以是几种不同方式的,可以写一个统一的方法[self showController],在这个方法中去选择启动哪一个具体的Controller。我在.h文件中定义了一个枚举来定义不同的启动方式:
typedef enum
WKOpenForNormal,
//普通打开
WKOpenForComment,
//打开评论页
WKOpenForFavorite,
//打开收藏页
WKOpenForGlance
//打开来自glance的内容
因为用户如果选择了点击Glance 来查看具体的内容的话,Glance和MainApp是通过Handoff来实现通信的,我们可以在入口的控制器中的:
- (void)handleUserActivity:(NSDictionary *)userI
这个方法中去将WKOpenType赋值成WKOpenForGlance。
当然了,如果是从Notification来的,我们完全可以通过:
- (void)handleActionWithIdentifier:(NSString *)
identifier forRemoteNotification:(NSDictionary *)remoteN
这个方法来根据具体的用户点击的动作来区分不同的打开方式。
这里比较难处理的是,如果用户是从Glance进来的,退出这个控制器,还是要显示那六个页面的,这里我的解决方法是注册通知。在出来的控制器中的- (void)didD方法中post出来通知,来让主控制器重新打开六个Page页面。Notification同Glance。
(2)Watch App 与Host App 联合调试
因为程序中多处用到了下边这个方法,因此主程序和Watch App 联合调试就显得非常必要了,在Xcode的一个新beta 的release note中苹果介绍了一种方法。
+ (BOOL)openParentApplication:(NSDictionary *)userInfo reply:
(void(^)(NSDictionary *replyInfo, NSError *error))
首先run 起来Apple Watch App在模拟器中。
在iphone 模拟器中启动 demo App。
Xcode - Debug - Attach to Process 里找到host app 线程,Attach上。
完成以上三个步骤,主程序和手表程序上的端点都可以进行调试。
(3)申请数据方面
在开发初期,我是在extension中进行数据的申请,这样尝试了一段时间之后发现性能上优化的空间不大,而且写出了很多重复的代码。复用项目中已有的代码是我最好的选择,尤其是一些第三方用pod管理的库,但是考虑到公司的项目已经是非常成熟的了,一些管理的第三方库无法正常的使用,进而又去考虑写一个共用的框架,由于时间问题,项目有点大,抽筋抽骨的不是很合适,所以决定充分发挥openParent这个方法,将申请数据这块放在主程序中,顺便将所有需要&问&主程序的东西全部整理到一个类中,这样就可以充分发挥老代码的作用。
数据策略大致如下:首先为了优化Watch App 的启动速度,采用后台申请数据存起来,Watch每次去使用就可以了,最后处理一下冷启动的问题,这种情况是当安装了我们的软件,没有在iPhone上打开过,直接打开Watch上的程序的时候已然有数据,这么做的话除了第一次会启动的稍微慢一点点之外,剩下的启动速度就会快很多。
具体用到的方法是:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:
(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
我和同事做到这里的时候,就感觉是一个iPhone当做了服务器,而Watch则是一个终端,有什么需要的数据,我们两个人设计好协议,通过openparent这个方法沟通。比如说,软件运行当中如果想要知道一个用户是否登录了,因为没有登录是没有某些功能的,那么这个时候通过openparent咨询一下isLogin就好,判断一下是否登录。
Demo中 watch 端代码实现如下:
[WKInterfaceController openParentApplication:@{@&type&:@&isLogin&}
reply:^(NSDictionary *replyInfo, NSError *error) {}
Demo中 iphone 服务端代码实现如下:
#pragma mark - WatchKit Data
-(void)application:(UIApplication *)application
handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
NSString *type = userInfo[@&type&];
NSDictionary *para = userInfo[@&para&];
NSDictionary *replyI
if ([type isEqualToString:@&isLogin&]) {
int random = arc4random()%10 + 1;
NSString *whetherLogin = @&&;
if (random == 1) {
whetherLogin = @&YES&;
whetherLogin =@&NO&;
replyInfo = @{@&whetherLogin&:whetherLogin};
else if ([type isEqualToString:@&isFavorite&])
reply(replyInfo);
Demo中有三种协议,分别是是否登录,回复信息,是否收藏,当然都是假的,根据项目需求来进行改变,务必注意的是每一种情况都要回调reply(replyInfo);,否则这个方法实际上会响应失败。
而实际上,项目当中需要在Watch上显示很多图片的,这个就需要异步的申请一下,首要想到的还是SDWebImage这个经典框架,这里就可以在openParent里使用将data请求到,然后返回给Watch。
PS:最后的最后,我们发现使用App Group来通信数据更加的有效率,因此一部分数据的请求采用了App Group来实现。
(4)TableView 在Watch 上的使用
在SDK发布的初期,我以为新控件之一WKInterfaceGroup可以点击,因为目前来看watch上是没有图层的概念的,复杂的UI布局是相当困难的,布局方式和之前有很大的区别,包括在故事板中的布局方法。当初为了实现产品给过来的UI布局也是脑洞大开啊,比如各种嵌套Group,为了要实现demo中主页的这种感觉,我很自然的想到了,放一个group,背景放图片,其他控件放在group上就好了,解决了无法实现控件在控件之上的问题。但是这就需要group可以点击,盼星星盼月亮之后,Xcode6.2正式版出来之后彻底断了我这个念头,没办法,只能通过另一个控件WKInterfaceTable来实现了,每一页只有一行不就可以了么,只能这么干了。
WKInterfaceTable和UITableView使用上还是有一些不同的,也比UITableView的使用方便了很多。
首先你需要去定义一个Row类,这个Row类相当于一个cell,在这个Row上去布局,如果你的表格中呈现数据的方式不一样,那就要定义不同的Row类。
定义好之后,调用的时候需要使用如下方法:
#pragma mark - UI
- (void)setUpUI
[self.newsRowTabel setNumberOfRows:1 withRowType:@&RowForOneNews&];
for (int i = 0; i & self.newsRowTabel.numberOfR i++) {
JRWKNewsRow *newsRow = [self.newsRowTabel rowControllerAtIndex:i];
[newsRow.newsCategory setText:[NSString stringWithFormat:@&第%ld张&,_index+1]];
RowType唯一标识了一个Row类,这里我设置了只有一行,期间设置Row类中每一个属性的UI数据。
响应点击事件需要去实现:
#pragma mark - Table Row Select
-(void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
NSDictionary *contextDic = @{@&PicName&:_picName,@&index&:[NSNumber numberWithInteger:_index]};
[self presentControllerWithName:WKNEWSDETAILCONTROLLERIDENTIFIER context:contextDic];
这里去指定具体要呈现出来的是哪一个Controller。
如果表格中的一行不能点击的话,在故事板中设定的时候把selectable勾选掉就可以了。
(5)数据在Controller 间的传递
API中的几个关于Controller切换的方法当中几乎都有context参数,也就是说传递数据由我们决定了。在十二月份刚开始写程序的时候,我传递的是一个很大的字典,发现在程序启动的时候非常的慢,后来决定写一个模型管理类,controller之间只需要传递一个index就可以了。在demo中保留了完整的类。
(6)关于HandOff
HandOff在iOS8之后出现,着实是为了Apple Watch量身打造的好么,实在是太应景了,因此在Watch上合理的运用handoff 是一个顺理成章的事情,而WKInterfaceController也带上了相关的一些方法,实际上是要比iphone上的简单易用一些的。
另一方面,在Glance界面,进入到主App上的时候,handoff也起了决定性的作用,通过handoff将具体的信息交给主App去处理。
主要有两个Api,这个是update了全局的Activity,将我们需要传递的信息打包成一个userinfo即可。
- (void)updateUserActivity:(NSString *)type userInfo:
(NSDictionary *)userInfo webpageURL:(NSURL *)webpageURL;
下面这个我还记得是开发者watchkit论坛里有一位开发者问过这个问题,在watchkit里怎么没有干掉Activity这一个方法。后来苹果的工程师估计是采纳了。但实际的效果来看,这个方法作用不大,例如在公司的项目中,几乎每一个页面都是需要handoff的,给它invalidate之后,iphone左下角出现logo就会出现异常甚至是不出现的情况。因此如果不是已经很明确的话,轻易的不要用这个方法。
- (void)invalidateUserA
总之,Handoff是Watch和iPhone沟通的绝佳方式之一,苹果也一直很鼓励使用SDK新出的一些东西来补充自己的App的。不要再幻想(至少是现在)通过Watch上的一个按钮能够使得iPhone 上的Host App 能够打开并且显示在前台了。
(7)其他一些Tips
(1).dynamic notification中苹果是希望用户在通知中就把所有的信息都看完的,而不希望用户点击内容本身(实际上也是不能点击的)再进入到Watch app 内查看这个通知的内容的,恰恰相反的是,glance 的交互理念是相反的,也就是苹果估计用户点击glance页面本身(实际上是可以点击的)进入到Watch app中进行继续深度阅读的。
(2).关于WKTextInputMode,一开始选择的是WKTextInputModeAllowAnimatedEmoji,后来发现这个是动态的大表情,返回的是这个大表情的data,不太适合我们一一对应到iphone上的emoji表情,于是后来切换到了WKTextInputModeAllowEmoji。而WKTextInputModePlain只是显示了我们所&推荐的&那些回复文本选项。
typedef NS_ENUM(NSInteger, WKTextInputMode)
WKTextInputModePlain,
// text (no emoji) from dictation + suggestions
WKTextInputModeAllowEmoji,
// text plus non-animated emoji from dictation + suggestions
WKTextInputModeAllowAnimatedEmoji,
// all text, animated emoji (GIF data)
(3).- (void)becomeCurrentP 这个方法主要是在page based页面当中,如果第三页在启动的时候你想让它先出来,就要标识好,在awake里边获取到之后,调用这个方法,注意的是,这个第三页不是立马就出现在手表的表盘之上的,而是从第一页蹦到第二页,然后再第三页这样转的。
(4).推荐一个很好用的工具,叫做Bezel,它能够将模拟器中运行的watch app 映射到真实的手表里,表带的样式也分38mm以及42mm,有很多种,可以更好的查看自己的App在真实手表上的样子。更换表带也很方便,直接拖着下边的某一个样式到Bezel上就自动换了。举个例子,在开发的时候曾想左右留边,但是放在Bezel上就会发现手表自带黑边,于是留下的左右边就是很多余了。
2.Notification
从目前来看,手表上出现push应该是随着手机一起来的,也就是同时去显示在这两个设备上,除非一切外力因素,比如手表关闭了抬手查看通知等。在之前的blog中提到过定义category来区分推送通知,如果没有定义category的故事板的话,就会在手表上显示一个系统默认的简短的通知。上边说道,苹果还是鼓励在notification中将该阅读的内容都阅读完,即使增加按钮也要是一些比较简单的操作,比如说一个日程安排的软件,来了一个push,一个done,一个delete,加上系统的cancel,就可以了。
我尝试了在Dynamic notification中申请了一个图片资源,发现系统就选择去显示Static notification,因此在notification controller内进行的任务的能力有限,这个在开发的时候要慎重。
开发的时候,Xcode自动生成的Payload很重要,可以定义多个payload来进行相应的模拟,搭配不同的category,不同的category故事板。
我依然认为Glance 的地位在Watch上是最重要的,至少在第三方独立app登上Watch前,Glance应该是用户使用最频繁的一个功能。因此Glance上要呈现的东西不能太少,也不能太多,一定要简明扼要,要呈现出最重要的一些东西。例如说如果自己的App不是以天气为主的,放一个天气温度什么的就不是很合适,系统的天气和地图软件还是非常出色的,因此还是在Glance 只体现自己App里边独特的东西最好。
另外,Glance的UI布局是很讲究的,如果可以尽量要按照Xcode 给的Upper和Lower的模板进行UI布局。不能使用任何可以操作的空间,例如按钮这样的,因为Glance就一页(可以滚动也是禁止的),有点像是渲染出来的一张图片似的,因此加个按钮是没有意义的。
同Notification,Glance controller 中进行任务的能力也比较有限,因为众多的Glance会一同呈现出来,用户翻腾着每一个app 的Glance,这就要求用户一扫之后就要呈现出来,一个比较好的解决方法就是Glance要呈现的数据提前的申请好,用的时候拿出来,具体实现的方法也有很多。比如上边提到的App Group。
Glance 以及主App的通信是依靠Handoff来实现的,也就是说用户点击了Glance这个页面之后,进入到主App,要做的事情需要根据传过来的userinfo来决定的,主要就是下边这个方法。
[self updateUserActivity:XXXXX userInfo:userInfo webpageURL:nil];
在入口controller中实现方法,决定启动什么页面,呈现什么内容,可以放在willActivate里边。记住的是请求数据这块一定要放在awake里边,不要放在willActivate里边。
-(void)handleUserActivity:(NSDictionary *)userInfo
wkOpenType = JRWKOpenForG
if (userInfo) {
NSString *sourceString = [userInfo objectForKey:@&Source&];
NSString *picName = [userInfo objectForKey:@&PicName&];
if ([sourceString isEqualToString:@&Glance&]) {
_glancePicName = picN
根据wkopentype决定启动页面。
switch (wkOpenType) {
case JRWKOpenForGlancedemo:
//glance page
case JRWKOpenForNotificationdemo:
//notification page
[self showPageBaseddemoController];
//默认启动
Glance 在demo中的表现形式,demo已经整理好,放在了自己的Github上。
其实WatchKit的东西真不多,更多的是在一个新的平台遇到的各种问题和bug是最让人头疼的。随着真机的即将到来,开发工作也不再是抹黑前行,这些都是利好的消息。不知道什么时候可以有独立的第三方应用的支持,也不知道WatchKit会丰满到什么程度,总之我个人还是很看好Watch的未来的,毕竟苹果引领的穿戴设备的头。
为了更好地向读者输出更优质的内容,InfoQ将精选来自国内外的优秀文章,经过整理审校后,发布到网站。本篇文章作者为刘瑞,。本文已由原作者授权InfoQ中文站转载。
刘瑞,中国科学技术大学苏州研究院在读硕士,喜欢科技产品,也喜欢制作开箱、体验视频。大三起开始自学iOS开发。
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博(,),微信(微信号:)关注我们,并与我们的编辑和其他读者朋友交流。
Author Contacted
语言 & 开发
460 他的粉丝
架构 & 设计
1037 他的粉丝
135 他的粉丝
2 他的粉丝
26 他的粉丝
0 他的粉丝
0 他的粉丝
0 他的粉丝
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
InfoQ.com及所有内容,版权所有 ©
C4Media Inc. InfoQ.com 服务器由 提供, 我们最信赖的ISP伙伴。
极客邦控股(北京)有限公司
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
通过订阅此邮件,我们可能会根据您以往感兴趣的主题向您发送内容。关于更多详情,请参阅我们的。
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。||百姓网公众号微信扫码关注百姓网小程序微信扫扫立即体验扫码下载手机客户端免费抢油卡、红包、电影票服务范围服务内容本市现已开通 、、、、,点击城市名或切换城市即可访问  高新17年3月24日面议 高新17年3月11日  历城17年2月25日  高新17年3月21日面议 高新17年3月21日面议 高新17年3月22日  历城17年2月17日  历城17年2月18日面议 高新17年3月24日  高新17年3月4日  高新17年3月2日面议 高新17年3月10日  高新17年3月27日面议 高新17年3月18日面议 高新17年3月15日  历城17年2月20日面议 高新17年3月24日  高新17年3月10日面议 高新17年3月28日面议 高新17年3月25日面议 高新17年3月23日  高新17年3月15日面议 高新17年3月20日  高新17年3月30日  历城17年2月21日  高新17年3月13日面议 高新17年3月22日  高新17年3月6日  高新17年3月29日面议 高新17年3月27日面议 高新17年3月21日面议 历城17年3月2日  高新17年3月9日面议 高新17年3月9日  高新17年3月23日1元 槐荫17年2月25日  高新17年3月11日面议 高新17年3月25日面议 高新17年3月23日100000元济南点量软件有限公司 高新17年3月31日||||||||||沪公网安备16号0&G:143&GM:91
描述:请填写描述手机号:请填写手机号请填写手机号上传图片:打开微信,扫一扫右侧二维码,即可完成绑定 -->绑定后,您可以:1. 立即在手机上收到用户给您的留言2. 使用手机快速完成付费推广的续费动作3. 第一时间了解到百姓网付费推广最新的促销活动,以及享受微信端独特的促销活动4. 更快速地将信息通过微信分享给好友、同事、朋友圈5. 如果您是招聘类目用户,还能够第一时间接收到新简历通知下载APP无需登录实时接受私信提醒,联系更便捷!或点击下方先登录再进入私信联系要想使用APPCAN开发移动应用,首先要弄明白什么是APPCAN,APPCAN都具有哪些功能。
1、什么是APPCAN?
APPCAN是正益无线公司开发的一套Hybrid混合应用开发平台(AppCan MEAP移动开发平台)包括:移动应用开发系统SDK、APPCAN MAS移动业务整合系统;其中移动应用开发平台SDK是由移动集成开发环境IDE、移动应用开发支撑系统DSS组成。
2、APPCAN的功能
1)在线应用开发管理()2)可实现线上线下代码同步管理3)丰富的页面模板4)可进行真机调试5)可自定义插件6)简单易操作的IDE开发环境
要使用APPCAN进行开发的话,需要具备以下条件:1)掌握HTML+CSS基础2)掌握JAVASCRIPT基础3)了解jQuery等js框架4)了解SQL等数据库的基础操作,增删改查sql语句5)了解AJAX知识6)了解JSON知识7)了解一定的网络请求知识以上条件也可以不具备,但要有一点,静下心来学习,初期能照葫芦画瓢即可,熟能生巧。使用APPCAN开发,当然需要下载其IDE,下载地址见:
另外,可在开发者网站中注册一个账号,可用于登录IDE,也可以进入APPCAN开发者讨论中心()文档中心()是学习APPCAN中各种JAVASCRIPT SDK的官方资料。遇到问题可以培训中心问答()部分提问,也可以论坛中留下足迹,寻找您想要的答案。
阅读(...) 评论()1、先说说本人开发一个app的过程,准备条件:①前端:aui.css+api.js+doT.min.js+echo.min.js+hammer.min.js+swiper.min.js+zepto.min.js&&②后端:webservice
2、第一天解决窗口之间的跳转、请求数据load封装,将头部和底部写成共用的,这样就只要在之间套用Framegroup和Frame就可以了,其他的可以结合win+Frame来完成,这样的结构很利于开发进度。
3、第二天编写登录、注册、密码之类编写,登录与注册成功主要通过sendEvent事件广播出去,需要重新获取用户信息的地方addListenEvent做监听,先广播事件出去再关闭当前Win,主要熟悉事件机制传播sendEvent。
4、第三天主要处理banner的缓存,页面按钮缓存,按钮为何需要缓存主要模仿京东支付宝之类的app,逢年过节变换皮肤或者按钮达到那种效果,写缓存一个就是需要与你的目标建立对应关系,利用sqlite存入数据库,当判断没网或者有更新的地方就行更新。图片文件如果进行更新,删除原来的缓存文件,将新的缓存地址存到数据库,利用异步在默默地缓存文件,把命令放入队列进行处理。
5、后面就是进行一些app的业务处理、实时通讯、推送等等。最后准备引导页之类的干货,还可以使用缓存将广告嵌入进来,下次用户打开就可以弹出广告。话就说到这里!!!
6、js文件分享在附件里,不明白的可以咨询:& & O(∩_∩)O哈哈~&&apicloud真的不错,对于熟练来说几天出一个app很简单。。。。
1、db模块的封装是借助了 @ 的基础上再进行了修改。这里值得注意的是:他是利用文件fs进行copy的方式,就是在项目里先存放一个db文件(widget),然后利用fs的copyTo进行copy到fs://下,但是如果你的app打包的时候是加密处理的,那么即便你复制成功,复制后的那个db文件是不可以被操作了的,原因就是ac加密打包后的文件是不可以进行操作的!这点值得注意,换一个思维就是利用fs在app运行时去创建db文件,这样就解决了问题。感谢对db模块的封装,加快了开发速度!()2、js队列:
var ExecQueue = function() {&&
& & this.queue = [];&&
ExecQueue.prototype = {&&
& & add : function(fn, args, time) {&&
& && &&&this.queue.push( {&&
& && && && &fn : fn,&&
& && && && &args : args,&&
& && && && &time : time&&
& && &&&});&&
& & exec : function() {&&
& && &&&var delay = 0;&&
& && &&&for ( var i = 0; i & this.queue. i++) {&&
& && && && &var _this=&&
& && && && &var f=function(idx){&&
& && && && && & return function(){&&
& && && && && & _this.queue[idx].fn.apply(_this,_this.queue[idx].args);&&
& && && && && & }&&
& && && && &}(i);&&
& && && && &setTimeout(f, delay);&&
& && && && &delay += this.queue.&&
& && &&&}&&
3、request方法:
var server99Url = &&,reqTime = new Date(),reqMethod = &&,lastTimestamp =
function ajaxRequest(limit, method, datas, callBack) {
& & if(api.connectionType.toLowerCase() == 'none'){
& && &&&H.$toast('…(⊙o⊙)…断网了…');
& && &&&var ret = {code:'-1'};
& && &&&callBack(ret);
& & if(!limit) limit =
& & //判断相同方法请求不能大于3秒
& & if(limit){
& && && && &if(reqMethod==datas.method&&((new Date().getTime()-reqTime.getTime())/1000)&4){
& && && && && && &&&H.$toast('频繁访问,休息一下喝杯茶...');
& && && && && & var ret = {code:'-2'};
& && && && && & callBack(ret);
& && && && && &
& && && && &}
& & datas = H.$com.extend({&v&:&1.0&,&format&:&json&,&lastTimestamp&api.getStorage('lastTimestamp')},datas);
& & reqTime = new Date(),reqMethod = datas.
& & api.ajax({
& && &&&url: server99Url,
& && &&&method: method,
& && &&&cache: false,
& && &&&timeout: 60,
& && &&&dataType: 'json',
& && &&&data: {
& && && && &values: datas
& & }, function (ret, err) {
& && &&&try{ret.lastTimestamp && ($api.setStorage('lastTimestamp',ret.lastTimestamp));}catch(e){}
& && &&&callBack(ret, err);
4、db操作处理:
function ajaxBanner(){
& && &&&var content = $('.swiper-wrapper');
& & content.html(&&);
& && &&&//请求获取banner
& && &&&ajaxRequest(&&,&post&,{&method&:&接口方法&},function(ret, err){
& && && && && & if (ret && ret.code==0) {
& && && && &var jsonData =
& && && && &setTimeout(function () {
& && && && && && &&&jsonData && jsonData.list && jsonData.list.length&0 && dealbannerlist(jsonData.list);//异步
& && && && && &    }, 100);
& && &&&} else {
& && && && && & //读取db文件
& && && && && & if(api.systemType == &ios&) $sqlite_api.clearDb();
& && && && && & var optionId = $sqlite_api.createObj();
& && && && && && && && &$sqlite_api.config(optionId,'数据库表名称','数据库名称').order(optionId,&sort asc&).where(optionId,{&type&:0,&isShow&:1}).select(optionId,function(status,data){
& && && && && && && && && & if(status&&data.length&0){
& && && && && && && && && && && && &var tempHtml = doT.template($('#bannercacle-template').html())(data);
& && && && && && && && && & content.append(tempHtml);
& && && && && && && && && & api.parseTapmode();
& && && && && && && && && & }else{
& && && && && && && && && && && && &var tempHtml = '&div class=&swiper-slide& tapmode data-href=&&&&img src=&../image/index/banner.jpg& width=&100%& height=&150&/&&/div&';
& && && && && && && && && && &&&content.append(tempHtml);
& && && && && && && && && & }
& && && && && && && && &});
& && &&&});
function dealbannerlist(list){
& && &&&//获取数据库记录
& && &&&if(api.systemType == &ios&) $sqlite_api.clearDb();
& && &&&var optionId = $sqlite_api.createObj();
& && &&&$sqlite_api.config(optionId,'数据库表名称','数据库名称').order(optionId,&sort asc&).where(optionId,{&type&:0,&isShow&:1}).select(optionId,function(status,data){
& && && && &if(status&&data.length&0){
& && && && && && &&&for(var i=0;i&list.i++){
& && && && && && && && && & mark:
& && && && && && && && && && &&&for(var j=0;j&data.j++){
& && && && && && && && && && && && && & if(list.bannerId==data[j].id && list.imageUrl==data[j].url){
& && && && && && && && && && && && && && && && &list.curl = data[j].//TODO: 还需要判断缓存文件是否存在
& && && && && && && && && && && && && && && && &
& && && && && && && && && && && && && & }
& && && && && && && && && && &&&}
& && && && && && && && &}
& && && && &}
& && && && &var tempHtml = doT.template($('#banner-template').html())(list);
& && &&&$('.swiper-wrapper').append(tempHtml);
& && &&&setTimeout(function () {
& && && && && & bannercacle(list);//异步
& && &&&   }, 100);
& && &&&});
//缓存首页图片
function bannercacle(list){
& && &&&if(H.$com.isNullOrUndefined(list))
& && &&&var optionId9 = $sqlite_api.createObj();
& & $sqlite_api.config(optionId9,'数据库表名称','数据库名称').where(optionId9,{&type&:0}).save(optionId9,{&isShow&:0},function(status,msg){
& && && && &var execqueue = new ExecQueue();
& && && && && & $.each(list, function(index, item){
& && && && && && && && &var imgData = {
& && && && && && && && && && &&&imgId : H.$com.isNullOrUndefined(item.bannerId)?null:item.bannerId,
& && && && && && && && && && &&&imgUrl : item.imageUrl,
& && && && && && && && && && &&&imgHref : !item.href && H.$com.isNullOrUndefined(item.href)?'':item.href,
& && && && && && && && && && &&&imgSort : H.$com.isNullOrUndefined(item.sort)?0:item.sort,
& && && && && && && && && && &&&imgDesc : H.$com.isNullOrUndefined(item.title)?'':item.title,
& && && && && && && && && && &&&imgType : 0
& && && && && && && && &}
& && && && && && && && &execqueue.add(queueDealImg,[imgData],100);
& && && && && & });
& && && && && & execqueue.exec();
function queueDealImg(imgData){
H.$imageCache(function(ret, err){
& && && && && & if (ret && ret.status) {
& && && && && && && && &var reUrl = ret.
& && &&&var sql = 'create table 数据库表名称(id integer primary key autoincrement,url varchar(255),curl varchar(500),href varchar(50),'+
& && && && && & 'desc varchar(500),sort integer,isShow integer,type integer,add_time datetime)';
& && && && && & var optionId = $sqlite_api.createObj();
& && && && && & $sqlite_api.config(optionId,'','数据库名称').exec(optionId,sql,function(status,msg){
& && && && && & var optionId = $sqlite_api.createObj();
& && && && && && && && &$sqlite_api.config(optionId,'数据库表名称','数据库名称').limit(optionId,1).where(optionId,{&id&:imgData.imgId}).select(optionId,function(status,data){
& && && && && && && && && & if(status&&data.length&0){
& && && && && && && && && && && && &var url = data[0].//缓存地址
& && && && && && && && && && && && &var dId = data[0].
& && && && && && && && && && && && &if(url!=reUrl){
& && && && && && && && && && && && && && &&&//删除之前的文件
& && && && && && && && && && && && && && &&&api.require('fs').remove({path: url},function(ret,err){});
& && && && && && && && && && && && &}
& && && && && && && && && && && && &//更新数据
& && && && && && && && && && && && &var optionId = $sqlite_api.createObj();
& && && && && && && && && && && && &var datajson = {&url&:imgData.imgUrl,&curl&:reUrl,&href&:imgData.imgHref,&desc&:imgData.imgDesc,&sort&:imgData.imgSort,&type&:imgData.imgType,&isShow&:1};
& && && && && && && && && && && && &$sqlite_api.config(optionId,'数据库表名称','数据库名称').where(optionId,{&id&:dId}).save(optionId,datajson,function(status,msg){});
& && && && && && && && && & }else{
& && && && && && && && && && && && &var arr = [{&id&:imgData.imgId,&url&:imgData.imgUrl,&curl&:reUrl,&href&:imgData.imgHref,&desc&:imgData.imgDesc,&sort&:imgData.imgSort,&isShow&:1,&type&:imgData.imgType,&add_time&:H.$com.getNowDateFormat(&-&,&:&,true)}];
& && && && && && && && && && && && && & var optionId2 = $sqlite_api.createObj();
& && && && && && && && && && && && && & $sqlite_api.config(optionId2,'数据库表名称','数据库名称').add(optionId2,arr,function(status,msg){});
& && && && && && && && && & }
& && && && && && && && &});
& && && && && && & });
& && &&&},img99BannerUrl+&/&+imgData.imgUrl);
本帖子中包含更多资源&&&&您需要
才可以下载或查看,没有帐号?
<p id="rate_14" onmouseover="showTip(this)" tip="助人为乐!&云币 + 50
" class="mtn mbn">
<p id="rate_" onmouseover="showTip(this)" tip="赞一个!&云币 + 150
" class="mtn mbn">
UID:253945
大神太牛鼻了
xuhq 发表于
大神太牛鼻了
……… 小强
很不错&&谢谢分享
UID:108982
看了里面的源码,感觉有点问题啊
var filepath = u.getDbPath(u.storeArr[optionId].dbName);
& && &&&//先检查fs 有无数据库文件
& && &&&u.fs.exist({
& && && && &path: filepath
& && &&&},function(ret,err){
& && &&&& & & & //alert(&exist&&&+$api.jsonToStr(ret)+&& &&);
& && && && &if(ret.exist){
& && && && && & //找到数据库了 并查询看能用不
& && && && && & u._simpleOpenDb(optionId,filepath,success,fail);
& && && && &}else{
& && && && && & & & //通过fs创建文件
& & & & & & & & & & & & & & & & u.fs.createFile({
& & & & & & & & & & & & & & & && &&&path: filepath
& & & & & & & & & & & & & & & & },function( ret, err ){& && &&&
& & & & & & & & & & & & & & & && &&&//alert(&createFile&&newPath:&+filepath+&& & result:&+$api.jsonToStr(ret)+&& &err:&+$api.jsonToStr(err));
& && && && && && &&&if (ret.status) {
& && && && && && && && &u._simpleOpenDb(optionId,filepath,success,fail);
& && && && && && &&&}else {
& && && && && && && && &if(typeof fail == 'function'){
& && && && && && && && && & if(err.code != undefined){
& && && && && && && && && && &&&var msg = u.fsCode[err.code];
& && && && && && && && && & }else{
& && && && && && && && && && &&&var msg = '文件创建失败';
& && && && && && && && && & }
& && && && && && && && && & fail(msg);
& && && && && && && && &}
& && && && && && &&&}
& & & & & & & & & & & & & & & & });
& && && && &}
& && &&&});& &&&
db.openDatabase本来在没有数据库db文件的情况下会直接创建数据库文件,不需要fs模块再去创建一个db文件了吧,况且使用fs创建的db文件可以用db模块打开数据库吗?这一块我也有点迷糊了,像楼主请教一下。& && &&&
为什么不采用文件的方式处理缓存,将数据存储为json文件。感觉用db模块处理有点大材小用了。
UID:280762
我的全部是用JSON传输。
今朝有酒今朝醉 哈哈哈 来干来干
APICloud粉丝
我为APICloud代言!
连续签到100天
连续签到100天
我们一岁了!
祝中秋节快乐
装“哔”勋章在手,谁敢不从!
圣诞节疯狂起来!
我是一名APICloud老司机
去屎!去屎!
托马斯全旋
你具备了一种装逼必备技能!
我不是单身狗!
我是单身狗!
端午节勋章
APICloud大会专属勋章
APICloud大会专属勋章
2018年世界杯
goal!!!!!!
儿童节快乐
美好的童年
特别贡献的版主
(C) 2014 APICloud}

我要回帖

更多关于 关于编程的app 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信