什么是开发App的正确步骤?

在 iOS 开发中,写一个 App 很容易,但是要写好一个 App,确是要下另一番功夫。首先,我们来看一个 App 的开发要求:

写一个 App,显示出 Spotify 上 Lady Gaga 相关的所有音乐专辑,相关信息可以通过以下网址查到:

https://api.spotify.com/v1/search?q=lady+gaga&type=album

需求分析

首先拿到开发要求,最重要的是明确开发细节。这里面有很多我们不清楚的地方需要与产品经理和设计师交流:显示是要用 TableView 还是 CollectionView?每个音乐专辑的哪些信息需要显示?如果专辑数量过多,我们优先显示哪些专辑?这个 App 除了显示信息以外,还需要哪些拓展功能?这个产品的大小是否有要求?需要多少天完成?

经过讨论之后,大家的一致意见是做个如下的 App:

LadyGaga

于是我们就清楚了,是要做一个 tableView,每个 Cell 对应一个专辑信息,左边是图片,右边是专辑名。点击 Cell,可以看到相应的专辑大图。

构建架构

首先这个 App 比较简单,我们只要用最基本的 MVC 就可做好。

  • Model 部分:

只需要一个 Model, 为 Album,对应每一个专辑的信息;

  • View 部分:

主体的部分可以在 Storyboard 里面完成;

最好单独新建一个 UITableViewCell 的子类,用来对应设置专辑的UI;

  • ViewController 部分:

其中一个 ViewController 为 TableViewController,负责现实所有专辑的信息;

另一个 ViewController 负责展示 detail info,比如专辑的大图;

  • Network 部分:

负责从网络上 fetch 专辑信息;以及根据专辑的图片网址,fetch 图片数据;

基本架构

细节实现

  • Network 部分:

fetchAlbums 和 downloadImage 都用Apple 自带的 URLSession 和 JSONserialization 就可以实现,或者也可以用优秀的第三方库 AlamoFire。因为这个 App 比较简单,AlamoFire 优势不明显,且引入第三方库会增加 App 的体积,故而推荐使用前者。基本上就是实现下面两个函数:


  1. func fetchAlbums(with url: String, completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void) 
  2. func downloadImage(_ url: String) -> UIImage?  

对于第一个函数 fetchAlbums,因为网络请求是耗时耗力的工作,我们一般会将它们用后台线程而非主线程(UI线程)来处理,这样可以保持UI的流畅运行。用闭包则是为了异步多线程完成后可以回调,同时 error 是为了监视网络请求是否出错。

对于第二个函数 downloadImage,最简单的方法是通过 url 拿到对应的 data,然后通过相应的 data 拿到 image。返回为 optional 的原因是有可能 URL 有问题或者网络请求出错,此时返回 nil。

从API设计的角度来说,以上的downloadImage并不是最佳设计。最佳的设计是我们能知道哪里出错了,比如下面这样:


  1. enum DownloadImageError: Error { 
  2.   case InvalidURL 
  3.   case InvalidData 
  4.   
  5. func downloadImage(_ url: String) throws -> UIImage { 
  6.   guard let aUrl = URL(string: url) else { 
  7.     throw DownloadImageError.InvalidURL 
  8.   } 
  9.   
  10.   do { 
  11.     let data = try Data(contentsOf: aUrl) 
  12.     if let image = UIImage(data: data) { 
  13.       return image 
  14.     } else { 
  15.       throw DownloadImageError.InvalidData 
  16.     } 
  17.   } catch { 
  18.     throw DownloadImageError.InvalidURL 
  19.   } 
  20. }  
  • ViewController 部分:

对于 AlbumsController,我们用到了代理模式(Delegate),即将 tableView 代理到了 AlbumsController 上。我们只要实现相应的 dataSource 和 delegate 方法即可。其中对于 dataSource 而言,有两个方法是必须实现的,它们是:


  1. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
  2. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell  

同时,AlbumsController 里面,还有两个数组,一个用来装专辑([Album]),一个用来装图片([UIImage?]),这样我们只需下载数据一次,并将其存入相应数组,之后就无需再次进行相关的网络请求了。也就是说,这两个数组起到了缓存的作用。

具体的实现是:首先在 viewDidLoad() 中请求服务器取出相应的数据。之后根据专辑数量设定 TableView 的相应行数。在具体的一行当中,我们可以根据 indexPath 确定相应的专辑。根据相应专辑的图片 URL ,我们可以拿到相应的图片,之后缓存进图片数组。由于我们复用了 TableView 的 Cell,所以如果不缓存图片而每次去进行网络请求,会因为延时很严重而会造成图片闪烁的后果。

最后两个 ViewController 之间的跳转可以用 navigationController 来实现。

  • View 部分:

自定义 AlbumCell 可以保证 App 的扩展性很好。同时,为了处理有些专辑名字过长 Label 显示不了的问题,可以用 autoshrink 来处理。

App 运行流程

优化拓展

上面的设计和实现比较理想化,现在我们要考虑一个边界情况,假如网络不稳定,怎么办?

一个简单的解决方法就是,当网络好的时候把数据下载下来,存入 cache 和 storage 中,之后即使网络中断、App 崩溃,我们都能从 storage 中拿到相应数据。

这里引入外观模式(Facade),创建一个新的 class 名为 LibraryAPI,提供两个接口:


  1. func getAlbums(completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void) 
  2. func getImage(_ url: String) throws -> UIImage  

这里的方法跟之前 Network 的不同之处在于:getAlbums 方法会先尝试从 storage 中取出相应数据,如果没有,则去访问 Network,之后再把从 Network 中拿到的值存入 storage 中。这里面的实现有点复杂,牵涉到两大模块和多线程操作,但是我们并不必关心方法内部的实现,而仅仅关心接口,这就是外观模式的优点。同时,LibraryAPI 这个 class 最好用单例模式(singleton),因为它应该被当做是全局 API 被各个 ViewController 来访问,同时这样设计也节省资源。

优化后的 App 流程

另外一个优化点在于,假如我们一开始拿到很多数据 —— 例如10000 个专辑,那么我们该怎么操作?

正确的做法是分页。我们可以先只拿20个,显示在 TableView 上。当用户快滑到底端的时候,我们可以再取下面20个,然后我们总共有40个在内存中可以显示,以此类推。这样做的好处是,我们无需下载所有的数据,以最快、最流畅的方式布局 TableView,同时根据用户的需求增加相应的专辑数据。

最后一个优化点在于,假如用户上下滑动很快,我们如何能够用最快速度加载图片?

答案是用 operationQueue 来处理,当前 cell 是可见的时候,我们就 resume 下载图片的进程,否则就 suspend。这样保证了我们用有限的内存和 CPU 去最高效的下载用户需要、当前要见的图片。

值得一提的是,大家还可以借鉴 ASDK 的思路来进一步优化程序。

总结

本文从一个简单的 tableView App 说起,谈论了开发一个 App 的4个步骤:需求分析、构建架构、细节实现、优化拓展。简单介绍了多线程和几种设计模式,希望对大家有所帮助。

作者:伯乐专栏/顾毅

来源:51CTO

时间: 2024-10-28 10:27:05

什么是开发App的正确步骤?的相关文章

开发app需要角色

开发app需要角色: 开发一款手机APP应用软件,需要多个流程.多种工作角色分工,简单说明如下: 1.开发流程包括: (1)用户需求分析 (2)产品原型设计 (3)UI视觉设计 (4)数据库搭建 (5)服务端开发 (6)iOS客户端开发/Android客户端开发 (7)APP测试 (8)上传到应用商店. iOS提交到苹果的App Store,安卓的提交到国内各大安卓应用商店. 2.对应的工作职位包括: (1)产品经理 (2)UI设计师 (3)数据库架构师 (4)服务端工程师 (5)iOS客户端工

jQuery Mobile和HTML5开发App推广注册页_jquery

jQuery Mobile和HTML5的组合可以直接开发web版的app,所以用到我当前app中的推广注册页的编写是很恰当的,其实只要你熟悉html4+jquery的组合开发,那么html5+jqueryMobile你会立刻上手. html5比html4多了很多的标签,特别是多媒体这块有了很好的支持,但是如果只是做一般的web手机页面,那么多数标签是用不上的,JqueryMobile与jquery的不同点就在一些事件名称上,当然这里封装的也是html5的原生事件,还要说一个关于html5提倡的一

小米随身wifi驱动安装的正确步骤

  小米随身wifi是很多网友都会选择wifi软,可以将电脑的网络发射成WiFi信号,让手机.平板电脑等其他平台设备进行信号的共享.不过,最近有网友表示,下载的小米随身wifi驱动安装不了,不知道是什么原因.如果您也遇到这个问题,就一起来了解一下今天的小米随身wifi驱动安装的正确步骤! 小米随身wifi 小米随身wifi驱动安装不了是怎么回事? 小米随身wifi支持windows xp, windows vista 32/64位,windows 7 32/64位 ,windows 8 32/6

开发APP不搞清楚这20个问题,必然沦为一场灾难

移动经济的高速增长极大刺激了企业和个人的APP开发热情,从卖野山鸡的到卖无人机的,从老大妈到小正太都跃跃欲试,更不要说那些传统企业的信息主管们了. 面对今天如过江之鲫的APP市场,很少有人意识到,移动开发其实是一个巨大的"坑",真正的挑战来自开发阶段的需求变更.产品推出后的迭代.app运营中的各种微调和如何支撑各种推广渠道的要求等.在动手开发APP之前,无论你是有现成的开发团队还是准备外包,都应当扪心自问以下20个问题: 1.你打算发布到哪些平台上?有计划吗?iOS/Android最近

使用jqMobi开发app基础之Scrolling的使用,停止和继续拖动有关的问题

在使用Scrolling开发app的过程中,遇到了很多问题,demo给的例子是下拉永远有数据的情况,而我的数据是有限的,也就是下拉一定次数后,下拉实际上就没有新数据,也就是不需要再继续下拉了.当上拉刷新数据后,才可以继续下拉.本以为只需要添加一个判断就可以了,谁知道当下拉到没有数据后,再上拉刷新,然后下拉竟然下拉不了了!! 具体代码参考http://blog.csdn.net/xuexiaodong009/article/details/18794909 核心代码精简如下: var myScro

使用jqMobi开发app基础之Scrolling的使用,拖动后大量空白的解决

在使用jqMobi开发app基础:Scrolling的使用,向上向下拖动,动态添加数据一文中,通过使用af.scroller.js解决了上拉刷新,下拉添加数据的功能,可是很奇怪,经过多次下拉后,发下底部的空白越来越多!! 基本的页面panel代码如下: <div id="jiekuanshenpi" title="借款审批" class="panel" data-load="loadFirstpage" data-tab

使用jqMobi开发app基础之Badge的使用

显示效果: 开发app基础之Badge的使用-mac app开发基础教程"> 红色的部分就是Badge,可以用来显示数量或者是其他的信息. 使用其实很简单,  $.ui.updateBadge("#" + id, res.Msg, "tr"); 第一个参数是需要显示的标签编号,第二个参数是需要显示的文字,第三个参数是需要显示的位置. 第三个参数可输入的字符及含义 bl - bottom left tl - top left br - bottom r

使用jqMobi开发app基础之如何拨打电话

拨打电话,按照常规思维,应该需要调用系统的API,想不到最终实现却很简单.确实通过a标签实现的!! 具体代码很简单,例如如下的链接点击后,主动跳转到手机的拨号页面,并且输入10086,用户只需要按一下绿色的拨号键,就可以拨打移动客服. <a id="submittongxunlu" class="button block" href="tel:10086">拨打移动客服</a> 返回栏目页:http://www.bianc

使用jqMobi开发app基础之使用 jQuery

如何在使用jqMobi开发app中使用jquery呢?由于jqMobi本身和jquery中的很多方法都是一样的,直接添加,肯定会冲突? 但由于jqMobi的插件很少,有时又不得不使用jquery,如何处理? 官网已有方法:添加jq.appframework.js,这个是把appframework.js变成了jquery的插件,自然就不会有冲突了. 下载地址 需要注意添加顺序 <script src="jquery.js"></script> <script