原因
随着智能手机的普及,人们从智能终端获取新闻资讯的需求逐渐增加,用手机看新闻、报纸、刊物等已经成为大多数人每天必做的事情之一。人们迫切地希望可以有一款能随时提供最新资讯、重大新闻,可以将用户个性化订阅的新闻、报纸、刊物等快速、准确、方便地推送到智能手机用户的新闻资讯阅读软件。
当然其实是因为Android的课设给了一堆题目,又因为即刻的关系,自然而然选了RSS阅读器。
以下其实是课设报告的版本…所以看上去可能不是那么舒服
功能描述
新闻组的管理
用户能按照自己的需求来对新闻组进行CRUD等基本操作;
RSS新闻频道的管理
用户能够按照自己的需求对RSS新闻频道进行增删改查等基本功能的实现;
新闻频道阅读:
当用户打开一个RSS新闻频道时,能够准确无误的从网络上加载该频道的新闻列表;
新闻信息的阅读:
当用户觉得某一条新闻有趣时,能够打开新闻显示界面进行概要浏览,还能进去具体网页进行更为详细的了解;
新闻的更新:
当RSS更新以后,程序能做相应的更新,能与网络上的内容保持一致性。做到即时更新;
新闻的分享:
当用户认定某一条新闻比较有趣的时候,可用与别人通过SMS短信进行分享。
开发及运行环境
- 开发平台:Android
- 开发语言:Java(1.6以上)
- 开发工具:Android Studio(3.2以上)
- 构建工具:Gradle(3.4以上)
- 数据库系统:SQLite(3.0)
详细功能划分
基于团队人数少,项目结构紧凑等特点,将上面六点主要功能划分为总计个功能点,6类任务划分。(其实是因为一组有六个人)
应用基础
- 应用主题框架;
- 数据库(表)设计及生成;
- RSS源的解析;
- 实体类;
- 数据库协作类接口及实现;
订阅页及item页
- 显示已订阅的RSS源;
- 刷新RSS源的数据(主动刷新|定时刷新);
- 添加RSS源数据到本地;
- 点击某个RSS跳转到item页;
- item页显示该RSS下的所有item;
- 点击某个item跳转到详情页;
详情页
- 根据item的url加载内容;
- 直接在浏览器打开该url;
- 收藏到本地|从本地取消收藏;
- 调用系统api分享此url;
发现页
- 显示数据库中所有的RSS源及对应信息;
- 根据订阅状态显示不同图标;
- 更改订阅状态;
- 搜索RSS源;
我的页
- 设置应用阅读字体大小;
- 进入收藏页并显示收藏的文章信息;
- 点击收藏item进入详情页;
长按及分组
- 订阅页长按订阅界面项跳出选项(取消订阅,更改分组,删除此项);
- 发现页长按跳出更改分组,添加分组选项;
- 我的页点击“我的分组”显示RSS源的分组信息。
人员
团队成员共6人,故将上面的6个任务分别作安排。
这样划分可以保证每个人可以在一定程度上单独开发模块,同时可以使用Github来保证项目的一致性(说实话这是我第一次在课设的时候用Github,确实更节省很多不必要的功夫)
个人任务
应用主题框架
应用主题框架采用BottomNavigationBar+ViewPager+Fragment组合,实现结果和微信等主流应用类似,比较美观易用。
MainActivity中仅有1个BottomNavigationView和1个ViwePager组件,在代码中给Bar挂载上bottom menu。
1 |
|
menu的样式和效果在对应的xml文件中定义。
ViewPager添加3个Fragment。
1 | private void setupViewPager(ViewPager viewPager) { |
效果:
实体类
订阅页需要的数据为:已订阅RSS的名字,RSS的消息数;
发现页需要的数据为:所有的RSS的name,url,订阅状态,分组;
item页需要的数据为:某个RSS的name,item的title,description和time(图片不考虑);
详情页需要的数据为:所属RSS的title,item的url和time;
收藏页需要的数据为:已收藏的item的RSS的url,title,description和收藏时间。
结合以上,设计三个实体类:
RSSUrl
1
2
3
4
5int id;
String url;
String name;
String groupName;// 新闻组
SubscribeStatus status;// 是否订阅RSSItemBean
1
2
3
4
5
6
7
8
9
10// 标题
private String title;
// 发布者(为自定义name时默认为它)
private String author;
// 详情链接
private String link;
// 网站或栏目的简要介绍
private String description;
// 时间
private Date pubDate;FavorRSSItem
1
2
3
4
5
6// 此item的url
private String itemUrl;
private String titleName;
private String description;
// 收藏时间
private String favorTime;
RSS源的解析
RSS是基于XML(可扩展标志语言)的一种形式,并且所有的RSS文件都要遵守万维网联盟(W3C)站点发布的XML 1.0规范。一般来说,RSS文档的最顶层是一个
:网站或栏目的名称,一般与网站或栏目的页面title一致;
:网站或栏目的URL;:对网站或栏目的简要描述。
还可以使用一些如
一般一个RSS文件如下例:
1 |
|
但是各个RSS源的xml文档可能都有自己的特点,为了保证解析的简单稳定,我引入了一个专门用于帮助解析xml的包:Rome。
在utils包中,我设计了一个RSSUtil类,通过设置rss的url,并使用rome的一些类来获取title,link,description,item等数据,并提供get
方法。
1 | private void parseFromUrl(String url) throws IOException, FeedException { |
- 文章数:rssUtil.getFeedSize()
- RSS标题:rssUtil.getTitleName()
- RSS描述: rssUtil.getDescription()
- 文章项:rssUtil.getRssItemBeans()
为了保证数据在不影响主线程的情况下获取到,调用parseFromUrl()方法在新线程中,并使用CountDownLatch保证该线程已经被执行完成。
1 | private void getRSSData(final String rssUrl) { |
数据库(表)设计及生成
三个实体类中需要持久化的仅为RSSUrl和FavorRSSItem,因此基于他们的属性设计以下数据库:
数据库协作类接口及实现
数据库接口在后来的模块合并过程中增加了一些,增加的几个并没有在下面列出(因为过了一段时间忘了加了哪些了,可以参见源码)
DatabaseHelper类
继承SQLiteOpenHelper用于数据库和表的创建工作,并对外提供getWritableDatabase()和getReadableDatabase()方法。
BaseDALImpl类
数据库DAL基类,预设一些方法和成员,并提供preInsertRssUrl()将来自xml中的一些内置的RSS url链接杀入到RSS_URL表中。该方法在线程PreDatabaseThread的run()方法中被调用。
1 |
|
在MainActivity中start该线程。
1 | preDatabaseThread=new PreDatabaseThread(getApplicationContext()); |
FavorRSSItemDAL类
favor_rss_item表协作类接口,共以下5个接口。
- 显示所有收藏
1 | /** |
- 查询收藏
1 | /** |
- 查询某项item
1 | /** |
- 收藏
1 | /** |
- 取消收藏
1 | /** |
RSSUrlDAL类
rss_url表协作类接口,共以下9个接口。
- 查询所有
1 | /** |
- 获取所有已订阅内容
1 | ArrayList<RSSUrl> getSubscribe(ArrayList<RSSUrl> rssUrls); |
- 模糊查询
1 | /** |
- 查询rss_url某一项的内容
1 | /** |
- 添加
1 | /** |
- 添加
1 | /** |
更改标题名
1
2
3
4
5
6
7/**
* 更改标题名
* @param id 选中项的id
* @param name 新的名字
* @return 是否成功
*/
int updateName(Integer id, String name);更改组名
1
2
3
4
5
6
7/**
* 更改组名
* @param id 选中项的id
* @param groupName 新的组名
* @return 是否成功
*/
int updateGroupName(Integer id, String groupName);更改订阅状态
1
2
3
4
5
6
7/**
* 更改订阅状态
* @param id 选中项的id
* @param status 订阅状态
* @return
*/
int updateSubscribeStatus(Integer id,SubscribeStatus status);
补充
RSS解析的时间格式和收藏时插入数据库的时间格式均需要重新解析,转化为自己想要的格式;
因为RSS源的问题,会有数据空(titleName)和带html格式(description)的问题,前者已通过判断设值的方式完成。
1
2
3
4
5
6
7
8
9
10
11/**
* 获取标题,title为空时设为unknown
* @return
*/
public String getTitleName() {
titleName=feed.getTitle();
if(titleName==null||titleName.isEmpty()){
titleName="unknown";
}
return titleName;
}后者未完全实现。
1
2
3public String getDescription() {
return ClearStringUtil.clearDescription(feed.getDescription());
}可能因为RSSItem数据量过大而影响性能,但暂时未使用分批查询模式;
为了匹配某项以.xml结尾的rss源,增加url匹配;
1
2
3
4
5
6
7
8
9
10
11private void doParse(String rssUrl) {
if(!rssUrl.startsWith("https://")&&!rssUrl.startsWith("http://")){
System.out.println("add https:// for url");
rssUrl="https://"+rssUrl;
}
if (rssUrl.endsWith(".xml")) {
...
} else {
...
}
}数据库的增删改操作均有返回值,以判断是否执行成功。
总结
- 模块式的任务划分确实能提高工作效率,减少工作负担;
- 开发供其他人使用的接口时要描述清楚功能和参数;
- 开发环境尽量保持相同,否则会浪费大量时间和精力。