与你相遇,好幸运
——RSS 阅读器 项目总结
学习Java已经快一个学期了。她是那么的善解人意,心思澄明,让我心生欢喜。终于我们有了第一次邂逅——RSS阅读器。有幸与你相遇,请多多指教。
RSS阅读器简介:
近几年来网络信息大爆炸,人们需要了解的信息铺天盖地,众多信息中我们不能花太多时间去逐条读取,人们期待能够像浏览报纸标题一样,首先快速获取新闻摘要然后再对感兴趣的新闻仔细阅读,于是一个很早就出现的技术开始普遍和流行,它就是RSS,目前很多网站出现了XML或RSS的标志,该标志通过超级链接直接接到一个被称为“RSS feed”的XML文件,这是网站在利用RSS来发布最新消息。这种做法得到大量网站的支持。
RSS也叫聚合RSS,是在线共享内容的一种简易方式(也叫聚合内容,Really Simple Syndication)。通常在时效性比较强的内容上使用RSS订阅能更快获取信息,网站提供RSS输出,有利于让用户获取网站内容的最新更新。用户可以通过RSS订阅软件,自动接收感兴趣的网站的更新内容,而不需要自己一个一个去浏览这些网站。
RSS阅读器是一个桌面程序,其核心功能是根据用户选择的RSS2.0新闻源,分析新闻源中重要的信息,并按照项目要求格式输出显示内容及存储文件。
项目目标:
(1)本系统为Java桌面应用程序,负责解析本机制定路径下的RSS2.0格式的新闻源及相应的输出。
(2)RSS阅读器项目的主要功能包括:RSS文件读取,RSS文件分析,RSS内容输出,定时更新。
(3)系统界面简介明了,操作方便,用户体验较好。
RSS阅读器项目相关知识:
GUI,集合类,线程,类与对象,异常处理,IO,XML,Java API,
类型 与工具与环境
类型 | 工具与环境 |
---|---|
开发工具 | Eclipse 3.3 + JDK1.6 |
Web服务器 | Tomcat6.0 |
数据库访问工具 | MySQL Tools for 4.0 |
程序框架 | Spring2.0+Hibernate3.0+Struts2.0 |
页面技术 | JavaScript、HTML、CSS |
运行环境 | 操作系统:Windows XP SP3 |
Framework:.NET Framework 2.0 | |
Internet Explorer 8.0 | |
数 据 库 | Orcale8.0 |
MySQL5.0 | |
XML解析器 | MSXML4.0 |
图形组件 | JFreeChart4.0 |
GUI
功能截图:
开始界面:
整体界面:
GUI(图形用户界面)相关代码
public class JMainFrame extends JFrame {
RSSService rssService = null;
FileDaoImpl fileDaoImpl = null;
//窗口属性
private final static int WIDTH = 800;
private final static int HEIGHT = 600;
private final static String TITLE = "RSS阅读器";
//窗口组件
private JTextArea jtaContent; //新闻文本区
private JButton jbExport; //导出按钮
private DefaultTableModel dtmTableModel; //表格数据模型
private JTable jtTable; //表格
private List<News> newsList; //新闻内容列表
private List<Channel> channelList; //频道列表
//构造函数
public JMainFrame() {
rssService = new RSSService(); //初始化
channelList = rssService.getChannelList();
this.setTitle(TITLE); //标题
this.setSize(WIDTH, HEIGHT); //大小
setCenter(); //窗口居中
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //窗口关闭
this.setContentPane(getJPMain()); // 添加到面板
}
//定义setCenter()方法,使窗口位于屏幕中央
private void setCenter() {
Toolkit kit = Toolkit.getDefaultToolkit(); //定义工具包
Dimension screension = kit.getScreenSize(); //获取屏幕的尺寸
this.setLocation((screension.width - WIDTH) / 2,
(screension.height - HEIGHT) / 2);
}
//创建主面板
private JPanel getJPMain() {
JPanel JpMain = new JPanel();
JpMain.setLayout(new BorderLayout());
//带滚动条的表格,带滚动条的文本区,分割面板
JpMain.add(getJSPClientArea(), BorderLayout.CENTER);
//菜单
setJMenuBar(getJMBMy());
//状态栏
JpMain.add(getJSB(), BorderLayout.SOUTH);
//工具栏
JpMain.add(getJTB(), BorderLayout.NORTH);
return JpMain;
}
//分割面板
private JSplitPane getJSPClientArea() {
JSplitPane jspVertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
jspVertical.setDividerLocation(280);
jspVertical.setLeftComponent(getJSPTable());
jspVertical.setRightComponent(getJSPContent());
return jspVertical;
}
//菜单
private JMenuBar getJMBMy() {
//创建菜单栏
JMenuBar jmbMy = new JMenuBar();
//创建“文件”菜单,并且设置相应的快捷键
JMenu fileJMenu = new JMenu("文件(F)");
fileJMenu.setMnemonic('f');
//“文件”菜单监听器
fileJMenu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < channelList.size(); i++) {
final Channel channel = channelList.get(i);
}
}
});
//创建“文件”菜单下面的菜单项
for (int i = 0; i < channelList.size(); i++) {
final Channel channel = channelList.get(i);
JMenuItem menuItem = new JMenuItem(channel.toString());
fileJMenu.add(menuItem);
//“文件”菜单下面的菜单项监听器
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String filePath = channel.getFilePath();
newsList = rssService.getNewsList(filePath);
showTable(newsList);
}
});
}
//创建“导出”菜单项
JMenuItem export = new JMenuItem("导出(E)");
export.setMnemonic('e');
//“导出”菜单项监听器
export.addActionListener(new ExportActionListener());
//创建“退出”菜单项
JMenuItem exit = new JMenuItem("退出(X)");
exit.setMnemonic('x');
//“退出”菜单项监听器
exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
//将“导出”菜单项“退出”菜单项添加到菜单中
fileJMenu.addSeparator();
fileJMenu.add(export);
fileJMenu.addSeparator();
fileJMenu.add(exit);
//创建“帮助”菜单并且设置相应的快捷键
JMenu helpJMenu = new JMenu("帮助(H)");
helpJMenu.setMnemonic('h');
//创建“关于RSS阅读器”菜单项
JMenuItem about = new JMenuItem("关于RSS阅读器");
about.setMnemonic('a');
//“关于RSS阅读器”菜单项监听器
about.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "欢迎联系加QQ:2544247346交流讨论",
"关于RSS阅读器", JOptionPane.INFORMATION_MESSAGE);
}
});
//将“关于RSS阅读器”菜单项“添加到”帮助“菜单中
helpJMenu.add(about);
//将“文件”菜单,“帮助”菜单添加到菜单栏中
jmbMy.add(fileJMenu);
jmbMy.add(helpJMenu);
return jmbMy;
}
//“导出”监听器
class ExportActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (newsList == null) {
JOptionPane.showMessageDialog(null, "请先选择频道", "提示",
JOptionPane.WARNING_MESSAGE);
} else if (rssService.save(newsList)) {
JOptionPane.showMessageDialog(null, "新闻信息保存成功", "成功",
JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "新闻信息保存失败", "失败",
JOptionPane.ERROR_MESSAGE);
}
}
}
//带滚动条的文本区
private JScrollPane getJSPContent() {
JScrollPane jspContent = null;
if (jtaContent == null) {
jtaContent = new JTextArea(); //创建新闻内容文本区
jtaContent.setLineWrap(true); //设置文本区的换行策略
jtaContent.setEditable(false); //设置文本区为不可编辑
jspContent = new JScrollPane(jtaContent); //设置带文本区的滚动面板
jspContent.setPreferredSize(new Dimension(780, 260));
}
return jspContent;
}
//带滚动条的表格
private JScrollPane getJSPTable() {
JScrollPane jspTable = null;
if (jtTable == null) {
dtmTableModel = new DefaultTableModel();
dtmTableModel.addColumn("主题");
dtmTableModel.addColumn("接收时间");
dtmTableModel.addColumn("发布时间");
dtmTableModel.addColumn("作者");
jtTable = new JTable(dtmTableModel);
jtTable.editCellAt(10,10);
//采用匿名内部类的形式为表格添加鼠标单击事件监听器
jtTable.addMouseListener(new MouseAdapter() {
//重写mouseClicked()方法处理鼠标点击事件
public void mouseClicked(MouseEvent e) {
//判断鼠标是否单击
if (e.getClickCount() == 1) {
int selectedRow = jtTable.getSelectedRow(); //获得鼠标单击行的位置
News selectedNews = newsList.get(selectedRow); //获得选中的新闻信息
jtaContent.setText(rssService.newsToString(selectedNews)); //将选中的新闻信息内容显示在面板
}
}
});
jspTable = new JScrollPane(jtTable);
}
return jspTable;
}
// Class MyTable extends JTable{
// public boolean isCellEditable(int row,int col){
// }
// }
//显示表格
private void showTable(List<News> newsList) {
//清空表格的内容
int rowCount = dtmTableModel.getRowCount();
while (rowCount > 0) {
dtmTableModel.removeRow(0);
rowCount--;
}
//遍历新闻内容列表,将相应的新闻内容显示到表格中
for (int i = 0; i < newsList.size(); i++) {
News news = newsList.get(i);
//按指定的时间格式,获得当前日期
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String currentDate = dateFormat.format(date);
//将标题的新闻、当前日期、发布日期和作者显示在表格中
String[] data = { news.getTitle(), currentDate, news.getPubDate(),
news.getAuthor() };
dtmTableModel.addRow(data);
}
}
//状态栏
private JPanel getJSB() {
JPanel jsb = new JPanel();
jsb.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel jlStatus = new JLabel("www.ruanko.com");
jsb.add(jlStatus);
return jsb;
}
//工具栏
private JToolBar getJTB() {
JToolBar jpNorth = new JToolBar();
jpNorth.add(getJBExport());
return jpNorth;
}
//导出按钮
private JButton getJBExport() {
if (jbExport == null) {
ImageIcon exportIcon = new ImageIcon("images/export.png");
jbExport = new JButton(exportIcon);
jbExport.setBorder(null);
jbExport.setToolTipText("导出新闻信息");
jbExport.addActionListener(new ExportActionListener());
}
return jbExport;
}
}
1、RSS文件读取
(1)功能描述
RSS文件读取,RSS文件解析,RSS文件导出
(2)功能截图
(3)开发思路和核心代码
public class RSSService {
private List<Channel> channelList;
private List<News> newsList;
private NewsDao rssDao;
public RSSService (){
rssDao=new FileDaoImpl();
channelList = new ArrayList<Channel>();
Channel channel1 = new Channel();
channel1.setName("腾讯 - 国际要闻");
channel1.setFilePath("NewsFiles/rss_newswj.xml");
channel1.setUrl("http://news.qq.com/newsgj/rss_newswj.xml");
Channel channel2 = new Channel();
channel2.setName("腾讯 - 国内新闻");
channel2.setFilePath("NewsFiles/rss_newsgn.xml");
channel2.setUrl("http://news.qq.com/newsgn/rss_newsgn.xml");
Channel channel3 = new Channel();
channel3.setName("新浪 - 体育新闻");
channel3.setFilePath("NewsFiles/sports.xml");
channel3.setUrl("http://rss.sina.com.cn/news/allnews/sports.xml");
Channel channel4 = new Channel();
channel4.setName("新浪 - 社会新闻");
channel4.setFilePath("NewsFiles/focus15.xml");
channel4.setUrl("http://rss.sina.com.cn/news/society/focus15.xml");
channelList.add(channel1);
channelList.add(channel2);
channelList.add(channel3);
channelList.add(channel4);
}
public List <Channel> getChannelList(){
return channelList;
}
public List<News> getNewsList(String filePath){
Document doc = load(filePath);
newsList = parse(doc);
return newsList;
}
public Document load(String filePath){
Document doc = null;
//1 指定解析器
SAXBuilder sb = new SAXBuilder(false);
//2 创建文件对象
File fXml = new File(filePath);
if(fXml.exists() && fXml.isFile()){
// 加载一个XML文件,得到Document对象
try{
// 3 调用build()
doc = sb.build(fXml);
}catch(JDOMException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
return doc;
}
private News itemToNews(Element item){
News news = new News();
String title = item.getChildText("title").trim();
String link = item.getChildText("link");
String author = item.getChildText("author");
String guid = item.getChildText("guid");
String pubDate = item.getChildText("pubDate");
String category = item.getChildText("category");
String description = item.getChildText("description").trim();
news.setTitle(title);
news.setLink(link);
news.setAuthor(author);
news.setGuid(guid);
news.setPubDate(pubDate);
news.setCategory(category);
news.setDescription(description);
return news;
}
private List<News> parse(Document doc){
List<News> newsList = new ArrayList<News>();
News news = null;
Element root = doc.getRootElement();
Element eChannel = root.getChild("channel");
List<Element> itemList = eChannel.getChildren("item");
for(int i = 0; i < itemList.size(); i++){
Element item = itemList.get(i);
news = itemToNews(item);
newsList.add(news);
}
return newsList;
}
public String newsToString(News news){
String content = null;
content = "标题:"
+ news.getTitle()
+ "\r\n"
+ "链接:"
+ news.getLink()
+ "\r\n"
+ "作者:"
+ news.getAuthor()
+ "\r\n"
+ "发布时间:"
+ news.getPubDate()
+ "\r\n"
+ "--------------------------------------------------------\r\n"
+ news.getDescription() + "\r\n" + "\r\n";
return content;
}
public boolean save(List<News> newsList) {
NewsDao dao = new FileDaoImpl();
return dao.save(newsList);
}
}
导出代码:
public class FileDaoImpl implements NewsDao {
private static String FILE_PATH = "NewsFiles/rss.txt";
public boolean save(List<News> newsList) {
boolean flag = true;
File file = new File(FILE_PATH);
FileWriter fw = null;
BufferedWriter bw = null;
try {
for (int i = 1; file.exists(); i++) {
//打开文件
file = new File("NewsFiles/rss_" + i + ".txt");
}
fw = new FileWriter(file, true);
bw = new BufferedWriter(fw);
RSSService rssService = new RSSService();
for (News news : newsList) {
String content = rssService.newsToString(news);
//写文件
bw.append(content);
}
bw.flush();
fw.flush();
flag = true;
} catch (IOException e) {
e.printStackTrace();
flag = false;
} finally {
try {
// 关闭文件
bw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
}
2、定时更新
(1)功能描述
自动下载相关的XML文件,并在RSS阅读器中更新
(2)开发思路和核心代码
public class UpdateThread extends Thread{
private final static int TIMEOUT = 5 * 1000; //超时5分钟
private final static int BUFFER_SIZE = 65536; //缓冲区大小64k
private final static int DELAY_TIME = 300 * 1000; //5分钟
private List<Channel> channelList;
public UpdateThread(){
RSSService rssService = new RSSService();
channelList = rssService.getChannelList();
}
public void run(){
//死循环,每5分钟更新一次RSS文件
while(true)
{
System.out.println("正在更新........" + new Date());
for(int i = 0; i < channelList.size(); i++)
{
Channel channel = channelList.get(i);
System.out.println("更新" + channel.getName());
update(channel.getUrl(), channel.getFilePath());
}
System.out.println("更新完毕........" + new Date());
//让线程休眠5分钟
try{
sleep(DELAY_TIME);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public void update(String urlPath, String filePath) {
// 创建HTTP连接
HttpURLConnection httpConn;
try {
URL url = new URL(urlPath);
httpConn = (HttpURLConnection)url.openConnection();
// 判断HTTP连接是否成功
int responseCode = httpConn.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
return;
}
} catch (MalformedURLException e) {
e.printStackTrace();
return;
} catch (IOException e) {
return;
}
// 1 在HTTP服务器上检查是否有新的RSS文件
File file = new File(filePath);
if (hasNewRSS(httpConn, file)) {
System.out.println("现在更新");
try {
httpConn.setConnectTimeout(TIMEOUT); // 设置一个指定的超时值
httpConn.connect(); // 打开到此URL引用的资源的通信链接
// 2 下载RSS文件, 并且保存到缓冲区中
InputStream in = httpConn.getInputStream();
byte[] byteBuffer = new byte[BUFFER_SIZE];
for (int i = 0; i < byteBuffer.length; i++) {
byteBuffer[i] = 0;
}
// 3 将RSS文件保存到文件中
int length = 0;
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
FileChannel channel = fileOutputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (length >= 0) {
length = in.read(byteBuffer);
for (int i = 0; i < length; i++) {
buffer.put(byteBuffer[i]);
}
}
buffer.flip();
channel.write(buffer);
// 关闭文件
channel.close();
in.close();
fileOutputStream.close();
// 断开HTTP连接
httpConn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("RSS文件已更新完毕!");
}
}
private boolean hasNewRSS(HttpURLConnection httpConn, File file) {
long current = System.currentTimeMillis();
long httpLastModified = httpConn.getHeaderFieldDate("Last-Modified", current);
long fileLastModified = file.lastModified();
if (httpLastModified > fileLastModified) {
return true;
} else {
return false;
}
}
}
关于界面和功能仍有很多不足,正在不断学习,不断改善。