2011-12-16

在 Android 裡使用 HttpGet 讀取網頁

主角為 HttpClient 與 HttpGet,HttpClient 是高貴的物件,整個 App 共用同一個 HttpClient,每一次存取網路建立一個新的 HttpGet 而不是建立新的 HttpClient(要包成 Singleton)。

不可以在 Activity 的 Main Thread 裡直接呼叫網路存取,因為很有可能出現五秒鐘的 ANR。

另外要在 AndroidManifest.xml 開啟網路權限 android.permission.INTERNET。

記得測試的時候,若要連到本機,不可以用 localhost 或者 127.0.0.1,因為這時的本機指的是手機或 Emulator,不是開發的電腦。

網路連線的 Exception 可以分成三類:
  • 傳輸:網路不通或者不穩。
  • 通訊協定:權限相關。
  • Timeout:包括 Connection timeout 與 Socket timeout,分別是 request timeout 與 response timeout。
對應的處理方法:
  • HttpClient 負責偵測並重試。
  • 開發時就會發現。
  • 唯一要處理的,使用重試機制。
SingleHttpClient
public class SingleHttpClient {

    private static final String TAG = "SingleHttpClient";
    private static HttpClient httpClient;

    private SingleHttpClient() {
        // singleton
    }

    public static synchronized HttpClient get() {
        if (httpClient == null) {
            Log.i(TAG, "建立 Singleton HttpClient...");
            HttpParams params = new BasicHttpParams();
            // 取得 connection
            ConnManagerParams.setTimeout(params, 1000);
            // request timeout
            HttpConnectionParams.setConnectionTimeout(params, 5000);
            // response timeout
            HttpConnectionParams.setSoTimeout(params, 10000);
            SchemeRegistry schreg = new SchemeRegistry();
            schreg.register(new Scheme("http",
                    PlainSocketFactory.getSocketFactory(), 80));
            schreg.register(new Scheme("https",
                    SSLSocketFactory.getSocketFactory(), 443));
            // 因為 HttpClient 是 Singleton,所以使用特別的 ConnectionManager
            ClientConnectionManager ccMgr = new ThreadSafeClientConnManager(
                    params, schreg);
            httpClient = new DefaultHttpClient(ccMgr, params);
        }
        return httpClient;
    }
}
HttpGetActivity
public class HttpGetActivity extends Activity {

    private static final String TAG = "HttpGetActivity";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try {
            String response = this.doGetWithRetry();
        }
        catch (Exception e) {
            Log.e(TAG, "不幸的事發生了");
        }
    }

    /**
     * 實做重試機制
     * @return
     * @throws Exception
     */
    private String doGetWithRetry() throws Exception {
        int retry = 3;
        int current = 0;
        while (current < retry) {
            current++;
            try {
                return this.doGet();
            }
            catch (IOException e) {
                Log.e(TAG, current + ".網路發生錯誤 - " + e.getMessage());
                if (current >= retry) {
                    Log.e(TAG, "重試失敗");
                    throw e;
                }
            }
            catch (Exception e) {
                Log.e(TAG, current + ".發生未知的錯誤 - " + e.getMessage());
                if (current >= retry) {
                    Log.e(TAG, "重試失敗");
                    throw e;
                }
            }
        }
        return null;
    }

    /**
     * 網路讀取
     * @return
     * @throws IOException
     */
    private String doGet() throws IOException {
        BufferedReader in = null;
        try {
            // 使用 Singleton 且 ThreadSafe 的 HttpClient
            HttpClient client = SingleHttpClient.get();
            HttpGet request = new HttpGet("http://cw1057.blogspot.com/");
            // 網址有參數的作法
            // request = new HttpGet("http://cw1057.blogspot.com/search?updated-max=2011-12-11T16:33:00%2B08:00&max-results=5");

            // 可以透過 HttpGet 來覆蓋 HttpClient 的設定,不會因為使用 Singleton 的 HttpClient 而受限
            HttpParams params = request.getParams();
            // 取得 connection
            ConnManagerParams.setTimeout(params, 1000);
            // request timeout
            HttpConnectionParams.setConnectionTimeout(params, 5000);
            // response timeout
            HttpConnectionParams.setSoTimeout(params, 10000);

            // 原始的方法:逐行讀取資料
            // HttpResponse response = client.execute(request);
            // in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            // StringBuilder html = new StringBuilder();
            // String line;
            // while ((line = in.readLine()) != null) {
            //         html.append(line + "\n");
            // }
            // in.close();
            // String responseText = html.toString();

            // 快速的方法:將 response 打包成 String
            String responseText = client.execute(request,
                    new BasicResponseHandler());
            Log.d(TAG,
                    "取得 HTML(" + responseText.length() + ") - "
                            + TextUtils.substring(responseText, 0, 10)
                            + " ... ");
            return responseText;
        }
        // 不處理 Execption,交由重試機制負責
        finally {
            if (in != null) {
                try {
                    in.close();
                }
                catch (IOException e) {
                }
            }
        }
    }
}

網路沒有開的情況:


正常的狀況,第二次再進來就沒有建立 HttpClient:

相關文章

沒有留言:

張貼留言