Ứng dụng tải RSS của các trang báo mạng.

Thảo luận trong 'Điện thoại, viễn thông' bắt đầu bởi g08ct, 18/9/12.

  1. g08ct New Member

    g08ct

    Tham gia ngày:
    15/8/12
    Bài viết:
    1,116
    Đã được thích:
    55
    Điểm thành tích:
    0
    Giới tính:
    Nam
    Chào mọi người, mình vừa có ý định tìm hiểu về cách đọc và hiển thị các RSS của các trang báo mạng, nhưng search với từ khóa "RSS" trong diễn đàn thì không thấy bài viết nào cả.

    Nhân tiện mình cũng đã vừa tìm hiểu xong nên chia sẻ cho mọi người nếu có ai có nhu cầu về thông tin này thì không cần phải tìm kiếm nhiều.

    Ứng dụng Pulse miễn phí trên Google Play là một ví dụ cho cái mình đang tìm hiểu.
    [​IMG]

    1 - Chúng ta tìm hiểu một chút về RSS
    RSS là một chuẩn chia sẻ tin tức Web với định dạng XML, với RSS ứng dụng của ta có thể lấy được các thông tin từ các website có cung cấp khả năng RSS, khi đó ứng dụng của ta xem như một cổng thông tin, nó sẽ cập nhật tất cả các thông tin từ các website khác.

    2 - Tổ chức, lưu trữ thế nào
    Nếu bạn nào đã tìm hiểu qua Notepad Tutorial của Google thì hẳn các bạn cũng thấy sự liên hệ với yêu cầu lưu trữ hiện tại của mình, do đó mình sẽ dùng cách lưu trữ dựa trên SQLite (nếu bạn nào chưa biết SQLite nhân tiện đây tìm hiểu luôn).
    Ứng dụng của mình có giao diện đơn giản thôi, đầu tiên mình sẽ có một list các feed (mỗi feed hiểu như là mỗi loại báo, hoặc một chủ đề nào đó của báo), khi user click vào mỗi feed thì các article sẽ hiện ra trên một list khác (các article là các tin trong một bài báo), và khi user click vào một article thì ứng dụng của ta sẽ load nội dung bài báo trên một webview mới, ngoài ra ứng dụng của ta còn cho phép thêm hay xóa các feed.
    Như vậy túm lại chúng ta có các class sau:
    + Gói lưu trữ
    - NewsDroidDB: lưu trữ các feed và các artical dựa trên SQLite
    - RSSHandler: đọc nội dung RSS với định dạng XML và trả về kết quả hiển thị cho các list view
    + Gói hiển thị:
    - FeedsList: main class - hiển thị list các feed
    - ArticlesList: hiển thị list các article
    - URLEditor: cho phép user hiển thêm bài báo mình thích

    3 - Bắt đầu với SQLite
    Trước hết hãy tạo 2 class nhỏ để tiện cho việc lưu trữ các thông tin của feed và article cái đã:

    HTML:
    public class Feed {         public long feedId;    public String title;    public URL url;} public class Article {     public long articleId;    public long feedId;    public String title;    public URL url;}
    Chúng ta bắt đầu với lớp NewsDroidDB để lưu trữ các feed và các article với SQLite
    ?1234567891011public class NewsDroidDB {     private static final String CREATE_TABLE_FEEDS = "create table feeds (feed_id integer primary key autoincrement, title text not null, url text not null);";    private static final String CREATE_TABLE_ARTICLES = "create  table articles (article_id integer primary key autoincrement, feed_id  int not null, title text not null, url text not null);";    private static final String FEEDS_TABLE = "feeds";    private static final String ARTICLES_TABLE = "articles";    private static final String DATABASE_NAME = "newsdroid";    private static final int DATABASE_VERSION = 2;     private SQLiteDatabase db;}
    Ở đây chúng ta có 2 dòng lệnh query để tạo 2 table cho feed và article, table feed có 3 cột và table article có 4 cột, chắc các bạn đọc qua cũng hiểu 2 dòng query này rồi nên mình không nói nhiều.
    Kế đến là khai báo tên của table feed và tên của table article, rồi đến tên của database của chúng ta, và version của database.
    Trước khi vào phần chính của NewsDroidDB, ta nên khai báo một lớp con DatabaseHelper extends SQLiteOpenHelper giúp ta tạo các table.
    HTML:
    public class NewsDroidDB {     private static final String CREATE_TABLE_FEEDS = "create table feeds (feed_id integer primary key autoincrement, title text not null, url text not null);";    private static final String CREATE_TABLE_ARTICLES = "create  table articles (article_id integer primary key autoincrement, feed_id  int not null, title text not null, url text not null);";    private static final String FEEDS_TABLE = "feeds";    private static final String ARTICLES_TABLE = "articles";    private static final String DATABASE_NAME = "newsdroid";    private static final int DATABASE_VERSION = 2;     private SQLiteDatabase db;     private static class DatabaseHelper extends SQLiteOpenHelper {         public DatabaseHelper(Context context) {            super(context, DATABASE_NAME, null, DATABASE_VERSION);        }         @Override        public void onCreate(SQLiteDatabase db) {            db.execSQL(CREATE_TABLE_FEEDS);            db.execSQL(CREATE_TABLE_ARTICLES);        }         @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            db.execSQL("DROP TABLE IF EXISTS notes");            onCreate(db);        }    }}
    hi tiết của lớp DatabaseHelper các bạn xem thêm phần Notepad Tutorial của Google nhá.
    Hàm khởi tạo của NewsDroidDB đơn giản thôi:
    ?

    public NewsDroidDB(Context ctx) {
    DatabaseHelper dbHelper = new DatabaseHelper(ctx);
    db = dbHelper.getWritableDatabase();
    }




    Giờ chúng ta đi tiếp đến các hàm thêm và xóa một feed:
    ?

    public boolean insertArticle(Long feedId, String title, URL url) {
    ContentValues values = new ContentValues();
    values.put("feed_id", feedId);
    values.put("title", title);
    values.put("url", url.toString());
    return (db.insert(ARTICLES_TABLE, null, values) > 0);
    }

    public boolean deleteArticle(Long feedId) {
    return (db.delete(ARTICLES_TABLE, "feed_id=" + feedId.toString(), null) > 0);
    }




    Hàm thêm một article có thêm một giá trị là feedID, cho biết tin được thêm thuộc bài báo nào.
    Tiếp theo là các hàm sẽ trả về một list các feed và list các article để hiển thị trên list view:
    ?

    public List<Feed> getFeeds() {
    ArrayList<Feed> feeds = new ArrayList<Feed>();
    try {
    Cursor c = db.query(FEEDS_TABLE, new String[] { "feed_id", "title", "url" }, null, null, null, null, null);
    int numRows = c.getCount();
    c.moveToFirst();
    for (int i = 0; i < numRows; i++) {
    Feed feed = new Feed();
    feed.feedId = c.getLong(0);
    feed.title = c.getString(1);
    feed.url = new URL(c.getString(2));
    feeds.add(feed);
    c.moveToNext();
    }
    } catch (SQLException e) {
    Log.e("NewsDroid", e.toString());
    } catch (MalformedURLException e) {
    Log.e("NewsDroid", e.toString());
    }
    return feeds;
    }

    public List<Article> getArticles(Long feedId) {
    ArrayList<Article> articles = new ArrayList<Article>();
    try {
    Cursor c = db.query(ARTICLES_TABLE, new String[] { "article_id", "feed_id", "title", "url" }, "feed_id="
    + feedId.toString(), null, null, null, null);
    int numRows = c.getCount();
    c.moveToFirst();
    for (int i = 0; i < numRows; i++) {
    Article article = new Article();
    article.articleId = c.getLong(0);
    article.feedId = c.getLong(1);
    article.title = c.getString(2);
    article.url = new URL(c.getString(3));
    articles.add(article);
    c.moveToNext();
    }
    } catch (SQLException e) {
    Log.e("NewsDroid", e.toString());
    } catch (MalformedURLException e) {
    Log.e("NewsDroid", e.toString());
    }
    return articles;
    }





    4 - Parse thông tin XML từ RSS

    Tiếp theo chúng ta xây dựng lớp RSSHandler giúp lấy thông tin từ RSS XML:
    ?

    public class RSSHandler extends DefaultHandler {

    // Used to define what elements we are currently in
    private boolean inItem = false;
    private boolean inTitle = false;
    private boolean inLink = false;

    // Feed and Article objects to use for temporary storage
    private Article currentActicle = new Article();
    private Feed currentFeed = new Feed();

    // Number of articles added so far
    private int articlesAdded = 0;

    // Maximum number of articles can download
    private static final int ARTICLES_LIMIT = 15;

    // The possible values for targetFlag
    private static final int TARGET_FEED = 0;
    private static final int TARGET_ARTICLES = 1;

    // A flag to know if looking for Articles or Feed name
    private int targetFlag;

    private NewsDroidDB droidDB = null;

    public void startElement(String uri, String name, String qName, Attributes atts) {
    if (name.trim().equals("title")) {
    inTitle = true;
    } else if (name.trim().equals("item")) {
    inItem = true;
    } else if (name.trim().equals("link")) {
    inLink = true;
    }
    }

    public void endElement(String uri, String name, String qName) throws SAXException {
    if (name.trim().equals("title")) {
    inTitle = false;
    } else if (name.trim().equals("item")) {
    inItem = false;
    } else if (name.trim().equals("link")) {
    inLink = false;
    }

    // Check if looking for feed, and if feed is complete
    if (targetFlag == TARGET_FEED && currentFeed.url != null && currentFeed.title != null) {
    // We know everything we need to know, so insert feed and exit
    droidDB.insertFeed(currentFeed.title, currentFeed.url);
    throw new SAXException();
    }

    // Check if looking for article, and if article is complete
    if (targetFlag == TARGET_ARTICLES && currentActicle.url != null && currentActicle.title != null) {
    droidDB.insertArticle(currentFeed.feedId, currentActicle.title, currentActicle.url);
    currentActicle.title = null;
    currentActicle.url = null;

    // Let's check if we've hit our limit on number of articles
    articlesAdded++;
    if (articlesAdded >= ARTICLES_LIMIT) {
    throw new SAXException();
    }
    }
    }

    public void characters(char[] ch, int start, int length) {
    String chars = (new String(ch).substring(start, start + length));
    try {
    // If not in item, then title/link refers to feed
    if (!inItem) {
    if (inTitle) {
    currentFeed.title = chars;
    }
    } else {
    if (inLink) {
    currentActicle.url = new URL(chars);
    } else if (inTitle) {
    currentActicle.title = chars;
    }
    }
    } catch (MalformedURLException e) {
    Log.e("NewsDroid", e.toString());
    }
    }

    public void createFeed(Context ctx, URL url) {
    try {
    targetFlag = TARGET_FEED;
    droidDB = new NewsDroidDB(ctx);
    currentFeed.url = url;

    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser sp = spf.newSAXParser();
    XMLReader xr = sp.getXMLReader();
    xr.setContentHandler(this);
    xr.parse(new InputSource(url.openStream()));
    } catch (ParserConfigurationException e) {
    Log.e("NewsDroid", e.toString());
    } catch (SAXException e) {
    Log.e("NewsDroid", e.toString());
    } catch (IOException e) {
    Log.e("NewsDroid", e.toString());
    }
    }

    public void updateArticles(Context ctx, Feed feed) {
    try {
    targetFlag = TARGET_ARTICLES;
    droidDB = new NewsDroidDB(ctx);
    currentFeed = feed;

    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser sp;
    sp = spf.newSAXParser();
    XMLReader xr = sp.getXMLReader();
    xr.setContentHandler(this);
    xr.parse(new InputSource(currentFeed.url.openStream()));
    } catch (ParserConfigurationException e) {
    Log.e("NewsDroid", e.toString());
    } catch (SAXException e) {
    Log.e("NewsDroid", e.toString());
    } catch (IOException e) {
    Log.e("NewsDroid", e.toString());
    }
    }
    }




    Lớp RSSHandler của ta kế thừa từ DefaultHandler, tại sao như vậy. Vì DefaultHandler là một handler rất tốt mà gói SAX cung cấp để parse file XML một cách dễ dàng.
    Các bạn chú ý ở 2 hàm createFeed()updateArticles(), một khi dòng lệnh sau được gọi xr.setContentHandler(this)xr.parse(new InputSource(url.openStream())) thì handle được chạy để đọc file XML.

    Giả sử ta có một đoạn XML như sau <tag>content</tag>.
    Khi đó khi handle đọc đến <tag> thì hàm startElement(...) được gọi với giá trị truyền vào của name là tag.
    Khi handle đọc đến content thì hàm characters(...) được gọi với giá trị truyền vào của ch[] chính là giá trị content.
    Khi handle đọc đến </tag> thì hàm endElement(...) được gọi với giá trị truyền vào của name là tag.

    OK, các bạn đã hiểu cách làm việc của DefaultHandler rồi, giờ đến việc parse file RSS như thế nào.
    Mình lấy ví dụ trang RSS của vnexpress như sau:

    HTML:
    <?xml version="1.0" encoding="utf-8"?><rss version="2.0">  <channel>    <title>Trang chủ - VnExpress.net</title>    <description>Trang chủ - VnExpress - Tờ báo điện tử có nhiều độc giả nhất Việt Nam</description>    <link>http://vnexpress.net/GL/Home/</link>    <copyright>VnExpress.net</copyright>    <generator>VnExpress.net:http://vnexpress.net/RSS</generator>    <pubDate>Wed, 22 Aug 2012 16<img src="http://vietandroid.com/images/ym_smilies/35.gif" alt="" title="35" class="inlineimg" border="0">13 GMT</pubDate>    <lastBuildDate>Wed, 22 Aug 2012 16<img src="http://vietandroid.com/images/ym_smilies/35.gif" alt="" title="35" class="inlineimg" border="0">13 GMT</lastBuildDate>             <item>            <title><=!=[=C=D=A=T=A=[ 'Điều tra, xử lý nghiêm tội phạm thâu tóm ngân hàng' ]=]=></title>            <description><=!=[=C=D=A=T=A=[ <a href="http://vnexpress.net/gl/xa-hoi/2012/08/dieu-tra-xu-ly-nghiem-toi-pham-thau-tom-ngan-hang/"><img src="http://vnexpress.net/Files/Subject/3b/bd/b3/51/3.jpg"></a>Chiều  22/8, Thủ tướng Nguyễn Tấn Dũng biểu dương Bộ Công an đã khởi tố, điều  tra để đấu tranh, ngăn chặn các hành vi vi phạm pháp luật nhằm thâu tóm,  gây mất ổn định hoạt động ngân hàng. ]=]=></description>            <link>http://vnexpress.net/gl/xa-hoi/2012/08/dieu-tra-xu-ly-nghiem-toi-pham-thau-tom-ngan-hang/</link>            <pubDate>Wed, 22 Aug 2012 14<img src="http://vietandroid.com/images/ym_smilies/43.gif" alt="" title="43" class="inlineimg" border="0">47 GMT</pubDate>        </item>         <item>            <title><=!=[=C=D=A=T=A=[ Ngân hàng Nhà nước cam kết hỗ trợ vốn cho ACB ]=]=></title>            <description><=!=[=C=D=A=T=A=[ <a href="http://vnexpress.net/gl/ebank/tin-tuc/2012/08/ngan-hang-nha-nuoc-cam-ket-ho-tro-von-cho-acb/"><img src="http://vnexpress.net/Files/Subject/3b/bd/b3/4d/acb-13.jpg"></a>Cuộc  họp bàn phương án đảm bảo an toàn hệ thống của lãnh đạo Ngân hàng Nhà  nước TP HCM kéo dài tới tối muộn 22/8. Ngân hàng Trung ương cũng phát đi  thông điệp sẵn sàng hỗ trợ vốn cho ACB để đảm bảo khả năng chi trả.>  <A  href="http://vnexpress.net/gl/phap-luat/2012/08/ong-nguyen-duc-kien-bi-bat/">Bầu Kiên bị bắt</A>> <A  href="http://ebank.vnexpress.net/gl/ebank/2012/08/ong-do-minh-toan-tam-dieu-hanh-acb/">ACB cử người điều hành thay Tổng giám đốc</A> ]=]=></description>            <link>http://vnexpress.net/gl/ebank/tin-tuc/2012/08/ngan-hang-nha-nuoc-cam-ket-ho-tro-von-cho-acb/</link>            <pubDate>Wed, 22 Aug 2012 13<img src="http://vietandroid.com/images/ym_smilies/17.gif" alt="" title="17" class="inlineimg" border="0">50 GMT</pubDate>        </item>   </channel></rss>
    Khi đó ứng dụng của ta chỉ việc đọc title của feed là Trang chủ - VnExpress.net..., feed này có 2 item với title và url như các bạn đã thấy.

    5 - Các thành phần còn lại
    Những thứ nặng ký mình đã trình bày hết ở trên rồi, phần còn lại là một chút giao diện đơn giản các bạn có thể xem source code đính kèm là sẽ hiểu ngay thôi, có gì thắc mắc mình sẽ giải đáp sau.

    Chúc các bạn thành công!
     
    Đang tải...
  2. takeshi90 Moderator

    takeshi90

    Tham gia ngày:
    27/12/12
    Bài viết:
    153
    Đã được thích:
    78
    Điểm thành tích:
    0
    Giới tính:
    Nam
    source đính kèm ở đâu vậy bạn, sao mình tìm hoài không thấy.,.
     
    3 people like this.
  3. Dluffi New Member

    Dluffi

    Tham gia ngày:
    2/4/13
    Bài viết:
    8
    Đã được thích:
    0
    Điểm thành tích:
    0
    Giới tính:
    Nam
    up code đi bn
     
  4. xuandinhgl New Member

    xuandinhgl

    Tham gia ngày:
    14/5/13
    Bài viết:
    6
    Đã được thích:
    0
    Điểm thành tích:
    0
    Giới tính:
    Nam
    Nơi ở:
    Da Nang, Vietnam
    Bạn nên để source trong thẻ PHP để dễ nhìn, code dài mà nhìn như vậy khó theo dõi quá
     

Chia sẻ trang này