黑大助手后端和Android端开发笔记

黑大助手 的功能:

黑大助手是一款为黑龙江大学在校学生服务的 Android 端应用,提供 查课表,查成绩,查考试,问答社区以及获取校内最新资讯的功能。
黑大助手后端和Android端开发笔记_第1张图片 黑大助手后端和Android端开发笔记_第2张图片

黑大助手后端和Android端开发笔记_第3张图片 黑大助手后端和Android端开发笔记_第4张图片

黑大助手后端和Android端开发笔记_第5张图片

下面开始整理整个项目从构思到完成这个过程的一些学习经历

由于学校官方移动应用 i黑大实在太难用,而且数据更新不及时,消息推送不及时,所以想着自己写一个应用,可以查看一些学生信息,比如课程表,成绩表,考试安排等信息,思路还是比较简单的,就是使用爬虫抓取数据,然后放在移动端展示

有了思路,接下来就是实现思路的过程,从思路来看,这个项目分为两部分:后端爬虫抓取数据,移动端展示数据

后端

其实最初设想的时候是没有考虑到使用后端的,爬虫可以直接在移动端实现,Java 中的 Jsoup 是一个很不错的网页解析库,用它可以在 Android 端很轻松的实现爬虫功能,但是考虑到可复用性可扩展性(比如要想在 iOS 上开发,那就还要再写一个 iOS 上的爬虫,或者校园网网页改版了,移动端也要跟着改爬虫代码…),最终没有选择这样的方案,最终决定搭建一个后台,用 Python 编写爬虫,解析成 json 格式的数据,提供接口给移动端调用

爬虫

目前主流的云服务器都支持 Python 开发环境,而且学习 Python 有一段时间了,正好用来练练手,于是选择使用 Python 写爬虫和部署后端应用。起初的模拟登录过程很容易实现,只需要观察页面跳转,和一些数据传递,这里推荐一下 Windows 上的 Fiddler,一款很好用的抓包工具,因为校园网的登录页面中,如果登录成功,需要打开新窗口,如果使用浏览器自带的开发者工具查看网络,观察起来会比较费劲,而如果使用 Fiddler4 查看,计算机上所有的网络请求都会被记录下来,按着顺序查看就可以了,这样子对于分析网页数据流程很方便。

抓取静态网页

前面模拟登录的过程就是分析网页流程的过程,通过模拟发送 GET 或者 POST 请求,获取返回数据,就能解析到需要的数据,使用了 Python 中的 Requests 库,当然这种简单的请求使用内置的 urllib 库也能实现,这里选择使用 Requests 的原因是校园网的一些安全措施,在某些页面需要 session 验证,而 Requests 中可以使用 session 类来实现,非常方便。

抓取动态网页

在校园网上的某些页面,比如显示课程表的页面,数据是通过 javascript 动态加载出来的,如果使用上面那种方法,直接构造请求获取数据,得到的网页中只是包含有 javascript 的代码,而不是它运行过后获取到的课程表数据,这样就没办法解析数据了,必须要执行这段 javascript 代码来获取数据。经过查阅资料,发现有两种方法来解决:

  • 第一种方法是模拟浏览器内核来执行这段代码,获取最后的结果数据,在 Python 中的实现就是使用 Selenium + PhantomJS,通过这两个库,可以模拟出浏览器环境来执行 javascript 代码,关于他们的使用,可以在官网查阅文档;

  • 第二种方法是分析 javascript 代码执行结果构造的请求,再模拟这个请求去抓取数据,而这个数据就是想要的课程表数据。

解析网页数据

在校园网上抓取到的静态网页数据,就是该网页的 HTML 代码,要想获取到指定内容,还需要一个解析器,在 Python 中使用的是 BeautifulSoup 完成解析工作,而如果通过抓取动态网页中通过 javascript 动态生成的数据,得到的是 json 数据,在 Python 中使用内置的 json 库就能实现解析。

到此,爬虫的工作就算是完成了,最终将网页内容解析成有价值的数据,接下来就是搭建服务器,将 Python 代码部署到服务器上

应用服务器搭建

Python 中服务器框架也有很多,这里选择的是 Flask,一款轻量级 Web 应用框架,用来搭建这个应用的后台是足够用了。选择云服务器的时候遇到一些问题,先后尝试过阿里云,新浪云,LeanCloud 云引擎,最终选择了 LeanCloud 云引擎,这三家服务器部署都还算简单,跟着教程走就能实现,这里选择 LeanCloud 云引擎的原因是考虑到移动端开发的便捷性,因为 LeanCloud 同时还提供一套移动应用开发 SDK,可以很方便的实现移动应用于后台通信

测试

在测试接口的时候,有一些问题暴露了出来,比如云服务器访问校园网慢,服务器上要不要做数据缓存,具体的缓存策略是什么,测试的时候发现,一次接口调用的,从开始调用到返回数据,整个过程平均耗时10秒左右,而且期间偶尔会有连接超时的情况发生,这样的结果是很不满意的。于是尝试做服务器的缓存,大致的缓存策略是:用户首次登录时,会从校园网上抓取数据,返回给用户,并且保存到云服务器上,用户接下来的请求都直接从云服务器上获取,而服务器会每隔一个指定的时间去校园网抓取数据,判断有没有更新,有则自动更新到云服务器上。这个策略最终没有被采纳,原因是云服务器定时去校园网抓取数据这个功能无法实现,因为从校园网抓取数据,需要用户登录后返回的 Token 作为请求头才能获取到。而要实现定时抓取,就需要在 云服务器上存放用户的账号信息,这是不安全的,由于这是个人开发的项目,并没有经过学校同意,考虑到安全性,整个应用的流程最好都不要涉及到用户的校园网账号密码。具体的解决方案会在后面移动端开发的时候讲到。

数据库设计

因为要在服务器上做数据缓存,所以需要用到数据库,由于第一次做这个,在设计的时候难免会有遗漏某些字段或者考虑不够周全,没关系,后期再根据具体情况修改即可,初始设计的数据库中,用户表包含了用户登录信息(比如用户名,密码,当然密码是经过加密的)和用户资料,这样做的优点是逻辑编写方便,但是缺点是存在安全隐患(如果用户表可以查询用户资料,就需要公开查询权限,这样对于登录信息是不安全的)而且可扩展性低,所以后来修改用户表结构,将其分为两张表:登录信息表和用户信息表

  • 登录信息表:存放用户登录信息(用户名,密码,学号,指向对应用户信息的指针)

  • 用户信息表:存放用户的个人信息(学号,姓名,学院,学分绩点等)

这样一样,登录表的权限设为 Private,不允许查询,可以保证登录信息的安全。关于可扩展性,可以参阅这篇文章 ,其他的数据表设计就不多说,也就是简单的数据存储,没有什么难度

Android 端

移动端的开发暂时先开发 Android 端,接着做 iOS 端的开发

前期准备

在写代码之前,需要先确定项目架构和界面逻辑,UI 方面是遵循 Material Design 设计规范,很大程度上有模仿知乎 Android 端 3.0 版的界面,我还是很喜欢 Material Design 风格的,界面这块就暂时先这样,具体再修改也行的。关于项目架构,了解到 RxJava 最近讨论的比较多,于是了解了一下相关知识,顺带又了解到 Retrofit,Realm 等开源框架,于是选择尝试使用 RxJava + Retrofit + Realm + Picasso 这样一个开源框架组合来实现这个应用,项目结构中有一个问题倒是纠结了一下,就是:到底是使用一个 Activity 加上 多个 Fragment 来实现整个应用呢,还是多个 Activity 加多个 Fragment 实现,关于这个问题,知乎上有讨论,最终选择了后者。

用户体系

在写 Android 端的时候,最先做的功能是登录,因为这个应用是给黑大学生使用的,并且数据都是从校园网上抓取,所以需要使用到学生的校园网账号,而 LeanCloud 的云服务器上有一套自己的用户体系,现在就需要将校园网账号与 LeanCloud 服务器上的账号进行绑定,这样,整个应用只有登录功能,没有注册功能,用户安装应用后,直接使用校园网账号就能登录。要实现这个用户体系的绑定,需要解决以下两个问题

  • 什么时候在 LeanCloud 上注册,什么时候登录?

  • 校园网用户修改密码后怎样修改 LeanCloud 用户的密码?

对于第一个问题的解决方案是:在应用中,用户执行登录操作,接着在服务器端用这个账号密码去登录校园网,如果登录成功,再执行注册 LeanCloud 用户,此时,如果 LeanCloud 上已经存在当前用户,会报错,这时,如果报错,就执行登录,接着更新用户信息,返回给客户端,如果登录校园网错误,则直接返回,提示用户名或密码错误。

对于第二个问题的解决方案是:如果登录校园网成功,而登录 LeanCloud 失败返回错误信息为用户名或密码错误,这就表明用户修改了校园网上的账号,此时就执行 LeanCloud 用户修改密码的操作。这样就能解决用户修改密码的问题,以上,这样就能完成校园网用户体系与LeanCloud 用户体系的绑定。

数据更新

关于数据更新策略,前面有提过,由于 LeanCloud 服务器访问校园网慢,而且会有可能连接超时,导致不好的用户体验,这里对于数据更新的策略分为两个部分:

  • 对于课程表和成绩表数据,考虑到数据改动会很小,一般就每学期改动一次,因此对于这种数据的更新,采用的是 Android 端进行数据缓存,用户在 Android 端登录成功后,后台下载 课程表和成绩表数据(可能会耗时比较长,而且会有超时重试),这样在用户想要查看课表的时候,数据可以直接显示而无需等待,而当数据有更新时,需要用户手动点击刷新,这个过程可能会比较耗时,但是只需要等待这一次就好,一旦数据更新了,以后很长时间内都无需再更新,所以目前采用这种方式来更新课程表和成绩表的数据。

  • 对于考试安排表,它的特点是临近考试的那段时间(一般是一个月左右)才会有数据,因此,这里对考试安排表的数据更新策略是:Android 端会缓存,但是每次打开页面时会首先加载最新数据,如果没有最新数据或者加载失败,再加载缓存

问答模块

黑大助手提供了一个类似知乎的问答社区,当然,只是一个最简单的实现,即:

用户可以提问题,提出的问题会在首页按照时间顺序倒序排列,所有其他用户通过刷新,就能在首页看到最新的问题,点击进入问题详情页面,可以回答,一旦发送回答,会有推送通知给提问者。目前的版本就是实现了这样一个简单的问答模块功能,后期可能会加上点赞,加关注之类的功能。

关于问答模块,主要涉及到的技术点就是 RecyclerView 加载数据和数据的及时更新,RecyclerView 加载数据这个就很好完成,以前有过相关经验的,实现起来很容易,比如下拉刷新,上拉加载更多,加载不同类型的视图等这些功能,在 RecyclerViewAdapter 中实现即可。而关于数据的即时更新,主要是两点:

  • 一是提问发送完成或者回答完成返回时显示最新的提问或者回答,这个就是对Fragment 或者 Activity 生命周期的把握了,在返回到之前界面的时候,可以做一些数据更新操作,这样来显示最新的提问或者回答,而达到及时更新的目的

  • 二是回答完成,对应的问题的回答数变量的更新,因为每个问题下都会显示这个问题的回答数,之前的方案是在客户端实现这个逻辑,即:回答完成,点击发送时,会查找对应的问题,然后更新其回答数这个字段的值,返回界面时再刷新问题详情。而现在的逻辑是将这些操作都放在后端去执行,也就是定义云函数,检测回答数这个数据表,一旦有新增数据(也就是新增了回答),就查找其对应的问题,更新回答数字段的值,这样,关于数据即时更新的两点问题就解决了。

缓存

考虑到移动端数据即时更新以及流量消耗的问题,需要在移动端做好数据缓存。Android 端使用的网络通信框架是 Retrofit,其底层实现是用的 OkHttp 这个库,自带有缓存功能,因此,对于一些即时更新的数据(比如首页问答数据,新闻资讯数据,回答列表数据)可以直接使用 OkHttp 的缓存功能来实现缓存,而缓存策略也可以根据不同的网络情况做出不同的选择。OkHttp 的缓存实现也很简单,通过拦截响应头,设置其 “Cache-Control” 值即可,这样,可以根据当前网络情况来设置这个值,关于 OkHttp 缓存的设置,可以参考这篇文章。对于用户个人资料的缓存,在 Android 端使用的是 SharedPreferences,因为用户个人资料只有一条数据,如果新建一个数据表来存放,有点浪费空间的感觉,因此决定将其存放在 SharedPreferences 中,读写也是很方便的,并且,判断当前用户是否已登录也是通过判断 SharedPreferences 是否有数据作为依据的。在登录界面,如果用户登录成功,会将用户资料保存在 SharedPreferences 中,然后以后每次启动应用,检查 SharedPreferences 中是否有数据,有则说明用户已登录,并且检查其中存放的 SessionToken 和 SchoolToken 是否有效(SessionToken 是 LeanCloud 登录返回的验证,SchoolToken 是登录校园网返回的验证,每次请求都需要在请求头中加上这两个验证数据来验证身份),否则是没有登录,在用户点击退出登录时,会清空 SharedPreferences 的数据。以上,这样一个逻辑就实现了判断用户是否登录以及验证登录是否过期。关于其他数据的缓存,比如 课程表,成绩表,考试安排表等,都是通过数据库做的缓存,Android 中用到的是 Realm,由于是第一次使用 Realm,不是很上手,在这块遇到过一些问题,比如跨线程访问,多个实例操作等,当然,通过仔细阅读官方文档和查看官方示例代码都是能够解决的

RxJava 的体验

初次接触 RxJava,简单的学习了一下基本使用和相关思想,感觉差不多能上手了,结合 Retrofit,网络操作实现起来确实很棒,经过一些封装,整个应用的代码非常简洁漂亮,但是在结合 Relam 使用的时候,就并不是很好用了,因为 Realm 不允许跨线程通信,他有自己的异步操作实现

你可能感兴趣的:(android,爬虫,架构,服务器,应用开发)