Thursday, January 31, 2019

Ứng dụng đọc tin tức tổng hợp

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.

1 comment:

  1. Yes, I completely understand your point of view; these qualifications are important for getting a software developer job. You need to have a bachelor's degree in computer science or a related degree. Also, knowledge of the software development life-cycle is necessary. You should check out Eiliana.com; they help you find good projects according to your qualifications.

    ReplyDelete