如何编写一款RSS阅读器App

终于上线第一个App,简单总结。对这个App,自己也算是“全栈开发”了。

先上源码

此次开发过程主要有两个产出:

  1. 番茄阅读App:精选了一些iOS开发者博客的订阅。AppStore地址
  2. 博客列表:按最后更新时间排序。每天自动刷新。

三个Tab页,第一页为订阅的文章列表,第二页为博客列表,第三页为我平时收集的各种文章、教程等网址。 如下图:

背景

  • 最初我是想做个大而全的开发者导航网站,想学什么都能找到最优质的网址,但没有那么多时间去搜集网址。由于工作是iOS开发,可以只关注iOS开发类的网址。
  • 于是想做个App,将收集的博客以及博客的RSS订阅信息通过App展示出来。
  • 目标不断的缩小,或许就是伪需求变为真实需求的过程。
  • 更深入折腾的背景,可以看看这篇文章

架构

  • Web服务器:用于收藏网址
  • 静态内容服务器:用户提供App访问接口
  • 爬虫:爬取不支持RSS/Atom订阅的博客文章列表
  • App:最终展现

Web服务器

Web服务器主要用于网址收集和删除。

为了能看到一个博客后,方便的收藏博客,可以编写Chrome插件。当浏览到一个博客网址后,点击Chrome插件,Chrome插件会自动收集当前博客的网址、标题、favicon。还可以添加订阅地址。

免去了使用文本方式记录博客的麻烦。

Web服务器使用Django实现(熟悉Python)。目前可以使用 http://iosblog.cc访问,由于App并不依赖这个域名,后期可能、可以随时更换。这个网址仅用于辅助收集,不用于公开使用。

静态内容服务器

阿里云或其他各种云的VPS最低配置一般一年600元左右,而且带宽性能都较差。可又为了能让App较快的获取数据,怎么办。想到平时的博客使用GitHub Pages。可以把平时收藏到的内容,定时导出为json文件放到GitHub Pages下(例如我的博客目录下)。

而阿里云服务器就不用担心带宽太小、配置太低的问题了。亚马逊的EC2最近还免费一年。

这一层服务器的存在,让我们可以随便更换Web服务器。App也不依赖Web服务器。

导出效果

爬虫

有的博客是有RSS或Atom订阅的,但还有很多博客没有订阅。例如现在很火的简书。简书上有很多不错的 iOS/OS X 开发者博客。

收藏了一些不错的简书博客后,可以写个小爬虫,只爬指定博客的文章列表。App中点击这类文章时,直接以浏览器的方式打开。

scrapy是Python很好的爬虫框架,使用起来简单易用。爬某个网址文章列表的代码如下:

    def parse(self, response):
        print response.url
        oid = self.url_to_oid[response.url]
        filepath = os.path.join(target_json_dir,'spider', 'jianshu', '%d.json'%oid)
        print filepath

        items = []
        for post in response.xpath('//div[@id="list-container"]/ul/li'):
            url = post.xpath('div/h4[@class="title"]/a/@href').extract_first()
            url = response.urljoin(url)

            title = post.xpath('div/h4[@class="title"]/a/text()').extract_first()
            title = title.strip()

            item = {}
            item['title'] = title
            item['link'] = url
            item['createtime'] = post.xpath('div/p[@class="list-top"]/span[@class="time"]/@data-shared-at').extract_first()
            item['image'] = post.xpath('a[@class="wrap-img"]/img/@src').extract_first()
            items.append(item)

Scrapy文档也很全,看完入门教程就能完成这个简书文章列表的爬取了。

有个小问题,可以不解决。简书博客首页的文章默认是加载10条(数不一定对),剩余文章是在向下滚动浏览时异步加载。由于是做订阅,只抓取最新的N条就足够了。(如何抓取异步加载的这些文章,我还没有研究。或许找到请求后台的地址,就很容易抓取了)

App

开源库

为了快速组装这个App,主要用了以下两个开源库。

  • MWFeedParser 可用于解析RSS/Atom,但使用过程中有些源的时间并不标准,可以直接修改源码,增加支持的时间。

  • KINWebBrowser 用于查看原文、或者打开不支持订阅的博客文章。

加载策略

当一下子拿到40多个订阅地址,如何能在让用户感觉不到太慢的前提下,把文章加载下来。

  • 服务器把更新比较频繁的、且优质的几个博客放在前面(排序靠前,增加zorder)
  • 首次加载动画,在加载5个博客后,动画缩小到不影响用户使用的地方,继续加载剩余博客。
  • 记录博客的最后文章更新日期,以后再次检测博客更新时,优先检查最近更新过的博客。(有一些开发者博客很久不更新文章了,但之前的文章写的很好。)尽量最快的显示出最新内容。
  • 所有博客都加载完成后,可以让用户知道。并能方便的加载最新内容。

其他可优化想法:

  • 每个博客一个权值,根据更新频率分配,在某些情况下可以直接忽略权值过低的博客。

订阅文章内容显示

RSS/Atom订阅拿到的文章内容是一段html代码。缺少css。程序内部要内置一套较为通用的css。

这里目前直接copy了已阅App的css代码。但这个css对有些文章展示的兼容不好。以后再优化啦。

加载动画

类似MBProgressSUD的加载动画都是全局阻塞方式。不能满足 先加载5个博客后继续显示进度且不应影响用户使用的需求。于是自己造一个。

最近看最强大脑,在学习魔方算法,想来可以做个类似魔方的动画。为了简单、快速可用,可以用 facebook的 pop 库,9个颜色挨个淡入淡出,再加个状态栏。

第三方服务

  • 统计:友盟
  • 崩溃分析:bugly
  • 热修复:JSPatch

总结

自己做iOS工作接近一年(中间暂停了3个月在家做奶爸了),这是第一个上架的App。虽然App较为简单,但这个想法的演变过程,值得我反思和总结。

开发这个App还有个目的,就是可以把自己平时学到的新知识、新技巧应用到这个App中来,作为自己的小实验室。