Ta
sẽ làm một ứng dụng đọc tin tức Android, lấy tin RSS Feed từ trang vnexpress
vì trang này ổn định, một số trang khác như 24h vào được nhưng bị lỗi crash sau
vài giây. Ta cũng sẽ thêm vào vài trang quốc tế khác như CNN, BBC.
Tạo
một project với tên getnet, gói ứng dụng là com.example.getnet,
thêm quyền vào file Manifest.xml.
<uses-permission android:name="android.permission.INTERNET"
/>
MinSDK
có thể từ 8.
Thêm
đoạn sau vào khai báo class Main.
android:screenOrientation="portrait"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
Bạn
dùng link sau để tải gói http client về.
Sau
đó trong Eclipse, nháy chuột phải vào project, Build Path, Configure Build
Path, Add External JARs, mở thư mục tải về, vào lib, nháy chọn hết các file jar
rồi OK là được.
Tạo
một class tên NewsItemFactory.
import org.xmlpull.v1.XmlPullParser;
import
org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class NewsItemFactory {
public static class NewsItem {
public String title;
public String link;
public String description;
@Override
public String toString() {
return title;
}
}
public static List<NewsItem>
parseFeed(XmlPullParser parser)
throws XmlPullParserException,
IOException {
List<NewsItem> items = new ArrayList<NewsItem>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
if (parser.getName().equals("rss") ||
parser.getName().equals("channel")) {
//Skip these items, but allow to drill inside
} else if (parser.getName().equals("item")) {
NewsItem newsItem = readItem(parser);
items.add(newsItem);
} else {
//Skip any other elements and their children
skip(parser);
}
}
return items;
}
private static NewsItem readItem(XmlPullParser parser) throws XmlPullParserException,
IOException {
NewsItem newsItem = new NewsItem();
//Must start with an <item> element to be valid
parser.require(XmlPullParser.START_TAG, null, "item");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("title")) {
parser.require(XmlPullParser.START_TAG, null, "title");
newsItem.title = readText(parser);
parser.require(XmlPullParser.END_TAG, null, "title");
} else if (name.equals("link")) {
parser.require(XmlPullParser.START_TAG, null, "link");
newsItem.link = readText(parser);
parser.require(XmlPullParser.END_TAG, null, "link");
} else if (name.equals("description")) {
parser.require(XmlPullParser.START_TAG, null, "description");
newsItem.description = readText(parser);
parser.require(XmlPullParser.END_TAG, null, "description");
} else {
//Skip any other elements, and their children
skip(parser);
}
}
return newsItem;
}
private static String readText(XmlPullParser parser) throws IOException,
XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
private static void skip(XmlPullParser parser) throws XmlPullParserException,
IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
}
Tạo
một class tên RestTask.
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.List;
import android.os.AsyncTask;
import android.util.Log;
import org.apache.http.NameValuePair;
import
org.apache.http.client.HttpResponseException;
public class RestTask extends AsyncTask<Void, Integer,
Object> {
private static final String TAG = "RestTask";
public interface ResponseCallback {
public void onRequestSuccess(String response);
public void onRequestError(Exception error);
}
public interface ProgressCallback {
public void onProgressUpdate(int progress);
}
private HttpURLConnection mConnection;
private String mFormBody;
private File mUploadFile;
private String mUploadFileName;
private WeakReference<ResponseCallback> mResponseCallback;
private WeakReference<ProgressCallback> mProgressCallback;
public RestTask(HttpURLConnection connection) {
mConnection = connection;
}
public void
setFormBody(List<NameValuePair> formData) {
if (formData == null) {
mFormBody = null;
return;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < formData.size(); i++) {
NameValuePair item = formData.get(i);
sb.append( URLEncoder.encode(item.getName()) );
sb.append("=");
sb.append( URLEncoder.encode(item.getValue()) );
if (i != (formData.size() - 1)) {
sb.append("&");
}
}
mFormBody = sb.toString();
}
public void setUploadFile(File file, String fileName) {
mUploadFile = file;
mUploadFileName = fileName;
}
public void
setResponseCallback(ResponseCallback callback) {
mResponseCallback = new
WeakReference<ResponseCallback>(callback);
}
public void
setProgressCallback(ProgressCallback callback) {
mProgressCallback = new
WeakReference<ProgressCallback>(callback);
}
private void writeMultipart(String boundary, String charset, OutputStream output, boolean writeContent) throws IOException {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(output, Charset.forName(charset)), 8192);
// Post Form Data Component
if (mFormBody != null) {
writer.write("--" + boundary);
writer.write("\r\n");
writer.write("Content-Disposition:
form-data; name=\"parameters\"");
writer.write("\r\n");
writer.write("Content-Type:
text/plain; charset=" + charset);
writer.write("\r\n");
writer.write("\r\n");
if (writeContent) {
writer.write(mFormBody);
}
writer.write("\r\n");
writer.flush();
}
// Send binary file.
writer.write("--" + boundary);
writer.write("\r\n");
writer.write("Content-Disposition:
form-data; name=\"" + mUploadFileName
+ "\";
filename=\""
+ mUploadFile.getName() + "\"");
writer.write("\r\n");
writer.write("Content-Type:
"
+ URLConnection.guessContentTypeFromName(mUploadFile
.getName()));
writer.write("\r\n");
writer.write("Content-Transfer-Encoding:
binary");
writer.write("\r\n");
writer.write("\r\n");
writer.flush();
if
(writeContent) {
InputStream input = null;
try {
input = new FileInputStream(mUploadFile);
byte[] buffer = new byte[1024];
for (int length = 0; (length = input.read(buffer)) > 0;) {
output.write(buffer, 0, length);
}
// Don't close the OutputStream
yet
output.flush();
} catch (IOException e) {
Log.w(TAG, e);
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
}
// This CRLF signifies the end of the binary data
chunk
writer.write("\r\n");
writer.flush();
// End of multipart/form-data.
writer.write("--" + boundary + "--");
writer.write("\r\n");
writer.flush();
} finally {
if (writer != null) {
writer.close();
}
}
}
private void writeFormData(String charset, OutputStream output) throws IOException {
try {
output.write(mFormBody.getBytes(charset));
output.flush();
} finally {
if (output != null) {
output.close();
}
}
}
@Override
protected Object doInBackground(Void... params) {
//Generate random string for boundary
String boundary = Long.toHexString(System.currentTimeMillis());
String charset = Charset.defaultCharset().displayName();
try {
// Set up output if applicable
if (mUploadFile != null) {
//We must do a multipart request
mConnection.setRequestProperty("Content-Type",
"multipart/form-data;
boundary=" + boundary);
//Calculate the size of the extra metadata
ByteArrayOutputStream bos = new ByteArrayOutputStream();
writeMultipart(boundary, charset, bos, false);
byte[] extra = bos.toByteArray();
int contentLength = extra.length;
//Add the file size to the length
contentLength += mUploadFile.length();
//Add the form body, if it exists
if (mFormBody != null) {
contentLength += mFormBody.length();
}
mConnection.setFixedLengthStreamingMode(contentLength);
} else if (mFormBody != null) {
//In this case, it is just form data to post
mConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;
charset=" + charset);
mConnection.setFixedLengthStreamingMode(mFormBody.length());
}
//This is the first call on URLConnection that
actually
// does Network IO. Even openConnection() is still just
// doing local operations.
mConnection.connect();
// Do output if applicable (for a POST)
if (mUploadFile != null) {
OutputStream out = mConnection.getOutputStream();
writeMultipart(boundary, charset, out, true);
} else if (mFormBody != null) {
OutputStream out = mConnection.getOutputStream();
writeFormData(charset, out);
}
// Get response data
int status = mConnection.getResponseCode();
if (status >= 300) {
String message = mConnection.getResponseMessage();
return new HttpResponseException(status, message);
}
InputStream in = mConnection.getInputStream();
String encoding = mConnection.getContentEncoding();
int contentLength = mConnection.getContentLength();
if (encoding == null) {
encoding = "UTF-8";
}
byte[] buffer = new byte[1024];
int length = contentLength > 0 ? contentLength : 0;
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
int downloadedBytes = 0;
int read;
while ((read = in.read(buffer)) != -1) {
downloadedBytes += read;
publishProgress((downloadedBytes * 100) / contentLength);
out.write(buffer, 0, read);
}
return new String(out.toByteArray(), encoding);
} catch (Exception e) {
Log.w(TAG, e);
return e;
} finally {
if (mConnection != null) {
mConnection.disconnect();
}
}
}
@Override
protected void onProgressUpdate(Integer... values) {
// Update progress UI
if (mProgressCallback != null && mProgressCallback.get() != null) {
mProgressCallback.get().onProgressUpdate(values[0]);
}
}
@Override
protected void onPostExecute(Object result) {
if (mResponseCallback != null && mResponseCallback.get() != null) {
if (result instanceof String) {
mResponseCallback.get().onRequestSuccess((String) result);
} else if (result instanceof Exception) {
mResponseCallback.get().onRequestError((Exception) result);
} else {
mResponseCallback.get().onRequestError(new IOException("Unknown Error Contacting
Host"));
}
}
}
}
Tạo
một class tên RestUtil.
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import org.apache.http.NameValuePair;
import android.util.Base64;
public class RestUtil {
public static RestTask obtainGetTask(String url)
throws MalformedURLException, IOException {
HttpURLConnection connection = (HttpURLConnection) (new URL(url))
.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoInput(true);
RestTask task = new RestTask(connection);
return task;
}
public static RestTask
obtainAuthenticatedGetTask(String url,
String username, String password) throws MalformedURLException,
IOException {
HttpURLConnection connection = (HttpURLConnection) (new URL(url))
.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoInput(true);
attachBasicAuthentication(connection, username, password);
RestTask task = new RestTask(connection);
return task;
}
public static RestTask
obtainFormPostTask(String url,
List<NameValuePair> formData) throws MalformedURLException,
IOException {
HttpURLConnection connection = (HttpURLConnection) (new URL(url))
.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoOutput(true);
RestTask task = new RestTask(connection);
task.setFormBody(formData);
return task;
}
public static RestTask
obtainAuthenticatedFormPostTask(String url,
List<NameValuePair> formData, String username, String password) throws MalformedURLException,
IOException {
HttpURLConnection connection = (HttpURLConnection) (new URL(url))
.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoOutput(true);
attachBasicAuthentication(connection, username, password);
RestTask task = new RestTask(connection);
task.setFormBody(formData);
return task;
}
public static RestTask
obtainMultipartPostTask(String url,
List<NameValuePair> formPart, File file, String fileName)
throws MalformedURLException, IOException {
HttpURLConnection connection = (HttpURLConnection) (new URL(url))
.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoOutput(true);
RestTask task = new RestTask(connection);
task.setFormBody(formPart);
task.setUploadFile(file, fileName);
return task;
}
private static void
attachBasicAuthentication(URLConnection connection, String username, String password) {
//Add Basic Authentication Headers
String userpassword = username + ":" + password;
String encodedAuthorization = Base64.encodeToString(userpassword.getBytes(), Base64.NO_WRAP);
connection.setRequestProperty("Authorization", "Basic
"+
encodedAuthorization);
}
}
Trong
thư mục layout, tạo một file lim.xml như
sau.
<?xml version="1.0"
encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="40sp"
android:orientation="horizontal"
>
<TextView
android:id="@+id/tem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3sp"
android:gravity="center_vertical"
android:textColor="#4B4542"
android:textSize="15sp"
/>
</RelativeLayout>
File
activity_main.xml như sau.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="#ffffff"
tools:context="com.example.getnet.MainActivity"
>
<LinearLayout
android:id="@+id/tem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
>
<TextView
android:id="@+id/te"
android:layout_width="80dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:clickable="true"
android:gravity="center"
android:onClick="on"
android:textColor="#00f"
android:textSize="17sp"
/>
<TextView
android:id="@+id/t2"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:gravity="center"
android:onClick="on2"
android:textColor="#0000cd"
android:textSize="17sp"
/>
<TextView
android:id="@+id/t3"
android:layout_width="80dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:clickable="true"
android:gravity="center"
android:onClick="on3"
android:textColor="#00f"
android:textSize="17sp"
/>
<TextView
android:id="@+id/t4"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:gravity="center"
android:onClick="on4"
android:textColor="#0000cd"
android:textSize="17sp"
/>
</LinearLayout>
<ListView
android:id="@+id/li"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/tem"
android:layout_gravity="center"
>
</ListView>
</RelativeLayout>
Class
MainActivity như sau.
import java.io.StringReader;
import org.xmlpull.v1.XmlPullParser;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.getnet.NewsItemFactory.NewsItem;
import com.example.getnet.RestTask.ResponseCallback;
public class MainActivity extends Activity implements ResponseCallback {
private static final String TAG = "FeedReader";
String FEED_URI = "https://vnexpress.net/rss/tin-moi-nhat.rss";
private ListView mList;
private ArrayAdapter<NewsItem> mAdapter;
private ProgressDialog mProgress;
TextView t,t2,t3,t4;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
t =
(TextView) findViewById(R.id.te);
t.setText("KHOAHOC");
t2 =
(TextView) findViewById(R.id.t2);
t2.setText("SPORT");
t3 =
(TextView) findViewById(R.id.t3);
t3.setText("CNN");
t4 =
(TextView) findViewById(R.id.t4);
t4.setText("BBC");
mList
= (ListView) findViewById(R.id.li);
mAdapter = new ArrayAdapter<NewsItem>(this, R.layout.lim, R.id.tem);
mList.setAdapter(
mAdapter);
mList.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?>
parent, View v, int
position, long id) {
NewsItem item = mAdapter.getItem(position);
Intent intent
= new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(item.link));
startActivity(intent);
}
});
}
public void on4(View v){
FEED_URI = "http://feeds.bbci.co.uk/vietnamese/rss.xml";
try{
RestTask task = RestUtil.obtainGetTask(FEED_URI);
task.setResponseCallback(this);
task.execute();
mProgress = ProgressDialog.show(this, "Searching", "Waiting For Results...", true);
}
catch (Exception e) {
Log.w(TAG, e);
}
}
public void on3(View v){
FEED_URI = "http://rss.cnn.com/rss/cnn_latest.rss";
try{
RestTask task = RestUtil.obtainGetTask(FEED_URI);
task.setResponseCallback(this);
task.execute();
mProgress = ProgressDialog.show(this, "Searching", "Waiting For Results...", true);
}
catch (Exception e) {
Log.w(TAG, e);
}
}
public void on2(View v){
FEED_URI = "https://vnexpress.net/rss/the-thao.rss";
try{
RestTask task = RestUtil.obtainGetTask(FEED_URI);
task.setResponseCallback(this);
task.execute();
mProgress = ProgressDialog.show(this, "Searching", "Waiting For Results...", true);
}
catch (Exception e) {
Log.w(TAG, e);
}
}
public void on(View v){
FEED_URI = "https://vnexpress.net/rss/khoa-hoc.rss";
try{
RestTask task = RestUtil.obtainGetTask(FEED_URI);
task.setResponseCallback(this);
task.execute();
mProgress = ProgressDialog.show(this, "Searching", "Waiting For Results...", true);
} catch (Exception e) {
Log.w(TAG, e);
}
}
@Override
public void onResume() {
super.onResume();
//Retrieve the RSS feed
try{
RestTask task = RestUtil.obtainGetTask(FEED_URI);
task.setResponseCallback(this);
task.execute();
mProgress = ProgressDialog.show(this, "Searching", "Waiting For Results...", true);
} catch (Exception e) {
Log.w(TAG, e);
}
}
@Override
public void onRequestSuccess(String response) {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
//Process the response data
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new
StringReader(response));
//Jump to the first tag
parser.nextTag();
mAdapter.clear();
for(NewsItem item
: NewsItemFactory.parseFeed(parser))
{
mAdapter.add(item);
}
mAdapter.notifyDataSetChanged();
} catch (Exception e) {
Log.w(TAG, e);
}
}
@Override
public void onRequestError(Exception error) {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
//Display the error
mAdapter.clear();
mAdapter.notifyDataSetChanged();
Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Chạy
thử, ấn các chữ ở trên để thấy tin tức thay đổi theo link mới.