1. 程式人生 > >Android使用http協議與伺服器通訊

Android使用http協議與伺服器通訊

網上介紹Android上http通訊的文章很多,不過大部分只給出了實現程式碼的片段,一些注意事項和如何設計一個合理的類用來處理所有的http請求以及返回結果,一般都不會提及。因此,自己對此做了些總結,給出了我的一個解決方案。

首先,需要明確一下http通訊流程,Android目前提供兩種http通訊方式,HttpURLConnection和HttpClient,HttpURLConnection多用於傳送或接收流式資料,因此比較適合上傳/下載檔案,HttpClient相對來講更大更全能,但是速度相對也要慢一點。在此只介紹HttpClient的通訊流程:

1.建立HttpClient物件,改物件可以用來多次傳送不同的http請求

2.建立HttpPost或HttpGet物件,設定引數,每傳送一次http請求,都需要這樣一個物件

3.利用HttpClient的execute方法傳送請求並等待結果,該方法會一直阻塞當前執行緒,直到返回結果或丟擲異常。

4.針對結果和異常做相應處理

根據上述流程,發現在設計類的時候,有幾點需要考慮到:

1.HttpClient物件可以重複使用,因此可以作為類的靜態變數

2.HttpPost/HttpGet物件一般無法重複使用(如果你每次請求的引數都差不多,也可以重複使用),因此可以建立一個方法用來初始化,同時設定一些需要上傳到伺服器的資源

3.目前Android不再支援在UI執行緒中發起Http請求,實際上也不該這麼做,因為這樣會阻塞UI執行緒。因此還需要一個子執行緒,用來發起Http請求,即執行execute方法

4.不同的請求對應不同的返回結果,對於如何處理返回結果(一般來說都是解析json&更新UI),需要有一定的自由度。

5.最簡單的方法是,每次需要傳送http請求時,開一個子執行緒用於傳送請求,子執行緒中接收到結果或丟擲異常時,根據情況給UI執行緒傳送message,最後在UI執行緒的handler的handleMessage方法中做結果解析和UI更新。這麼寫雖然簡單,但是UI執行緒和Http請求的耦合度很高,而且程式碼比較散亂、醜陋。

基於上述幾點原因,我設計了一個PostRequest類,用於滿足我的http通訊需求。我只用到了Post請求,如果你需要Get請求,也可以改寫成GetRequest

package com.handspeaker.network;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.util.Log;

/**
 * 
 * 用於封裝&簡化http通訊
 * 
 */
public class PostRequest implements Runnable {
    
    private static final int NO_SERVER_ERROR=1000;
    //伺服器地址
    public static final String URL = "fill your own url";
    //一些請求型別
    public final static String ADD = "/add";
    public final static String UPDATE = "/update";
    public final static String PING = "/ping";
    //一些引數
    private static int connectionTimeout = 60000;
    private static int socketTimeout = 60000;
    //類靜態變數
    private static HttpClient httpClient=new DefaultHttpClient();
    private static ExecutorService executorService=Executors.newCachedThreadPool();
    private static Handler handler = new Handler();
    //變數
    private String strResult;
    private HttpPost httpPost;
    private HttpResponse httpResponse;
    private OnReceiveDataListener onReceiveDataListener;
    private int statusCode;

    /**
     * 建構函式,初始化一些可以重複使用的變數
     */
    public PostRequest() {
        strResult = null;
        httpResponse = null;
        httpPost = new HttpPost();
    }
    
    /**
     * 註冊接收資料監聽器
     * @param listener
     */
    public void setOnReceiveDataListener(OnReceiveDataListener listener) {
        onReceiveDataListener = listener;
    }

    /**
     * 根據不同的請求型別來初始化httppost
     * 
     * @param requestType
     *            請求型別
     * @param nameValuePairs
     *            需要傳遞的引數
     */
    public void iniRequest(String requestType, JSONObject jsonObject) {
        httpPost.addHeader("Content-Type", "text/json");
        httpPost.addHeader("charset", "UTF-8");

        httpPost.addHeader("Cache-Control", "no-cache");
        HttpParams httpParameters = httpPost.getParams();
        HttpConnectionParams.setConnectionTimeout(httpParameters,
                connectionTimeout);
        HttpConnectionParams.setSoTimeout(httpParameters, socketTimeout);
        httpPost.setParams(httpParameters);
        try {
            httpPost.setURI(new URI(URL + requestType));
            httpPost.setEntity(new StringEntity(jsonObject.toString(),
                    HTTP.UTF_8));
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 新開一個執行緒傳送http請求
     */
    public void execute() {
        executorService.execute(this);
    }

    /**
     * 檢測網路狀況
     * 
     * @return true is available else false
     */
    public static boolean checkNetState(Activity activity) {
        ConnectivityManager connManager = (ConnectivityManager) activity
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connManager.getActiveNetworkInfo() != null) {
            return connManager.getActiveNetworkInfo().isAvailable();
        }
        return false;
    }

    /**
     * 傳送http請求的具體執行程式碼
     */
    @Override
    public void run() {
        httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpPost);
            strResult = EntityUtils.toString(httpResponse.getEntity());
        } catch (ClientProtocolException e1) {
            strResult = null;
            e1.printStackTrace();
        } catch (IOException e1) {
            strResult = null;
            e1.printStackTrace();
        } finally {
            if (httpResponse != null) {
                statusCode = httpResponse.getStatusLine().getStatusCode();
            }
            else
            {
                statusCode=NO_SERVER_ERROR;
            }
            if(onReceiveDataListener!=null)
            {
                //將註冊的監聽器的onReceiveData方法加入到訊息佇列中去執行
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        onReceiveDataListener.onReceiveData(strResult, statusCode);
                    }
                });
            }
        }
    }

    /**
     * 用於接收並處理http請求結果的監聽器
     *
     */
    public interface OnReceiveDataListener {
        /**
         * the callback function for receiving the result data
         * from post request, and further processing will be done here
         * @param strResult the result in string style.
         * @param StatusCode the status of the post
         */
        public abstract void onReceiveData(String strResult,int StatusCode);
    }

}

程式碼使用了觀察者模式,任何需要接收http請求結果的類,都要實現OnReceiveDataListener介面的抽象方法,同時PostRequest例項呼叫setOnReceiveDataListener方法,註冊該監聽器。完整呼叫步驟如下:

1.建立PostRequest物件,實現onReceiveData介面,編寫自己的onReceiveData方法

2.註冊監聽器

3.呼叫PostRequest的iniRequest方法,初始化本次request

4.呼叫PostRequest的execute方法

可能的改進:

1.如果需要多個觀察者,可以把只能註冊單個監聽器改為可以註冊多個監聽器,維護一個監聽器List。

2.如果需求比較簡單,並希望呼叫流程更簡潔,iniRequest和execute可以合併

如果有更好的方法或者問題,歡迎隨時交流