Android使用自己封裝的Http和Thread、Handler實現非同步任務

來源:互聯網
上載者:User

Android使用自己封裝的Http和Thread、Handler實現非同步任務
目錄結構如下:

Http協議的封裝:

使用http協議有request和response這兩個主要的域,下邊是Http協議封裝的結構圖

(1)HttpRequestInter.java:作為request域對象,應該可以獲得用戶端請求的地址和httpRequest對象,這樣的話才可以獲得用戶端請求的參數等資訊;另外public HttpResponseInter request() throws Exception; 使用這個方法,是當執行完request請求之後,返回一個response對象(這裡用介面表示)

/** * 請求的介面 * @author xuliugen */public interface HttpRequestInter {    //獲得httpRequest    public HttpUriRequest getHttpRequest();    //獲得http請求的url地址    public String getRequestURL();    //請求服務端:要返回一個response對象    public HttpResponseInter request() throws Exception;}

(2)HttpResponseInter.java作為和(1)中相對應的response對象,應該具有的方法:擷取返回的狀態代碼、擷取返回的流、擷取返回返回的string資料等,下邊的方法就是擷取相應的資料

/** * 響應的介面 * @author xuliugen */public interface HttpResponseInter {    //返回狀態代碼    public int statusCode();    // 向用戶端返迴流數    public InputStream getResponseStream() throws IllegalStateException,IOException;    //向用戶端返回位元組數組    public byte[] getResponseStreamAsByte() throws IOException;    //向用戶端返回JSON資料    public String getResponseStreamAsString() throws ParseException, IOException;}

(3)這是HttpRequestImpl.java介面的實作類別,我們可以看到我們不但實現了HttpRequestInter介面還實現了ResponseHandler 第二個就是用於當執行完request請求之後需要返回的資料,存放在一個response的Handler中。

public class HttpRequestImpl implements HttpRequestInter,ResponseHandler {    protected HttpUriRequest httpUriRequest;// 用於擷取request的url地址    private AbstractHttpClient abstractHttpClient; // client對象    // 構造犯法    public HttpRequestImpl(AbstractHttpClient httpClient) {        this.abstractHttpClient = httpClient;    }    // get方法    public HttpUriRequest getHttpRequest() {        return httpUriRequest;    }    //獲得request的url    public String getRequestURL() {        return httpUriRequest.getURI().toString();    }    //執行request請求,並返回??個response對象介面    public HttpResponseInter request() throws Exception {        return abstractHttpClient.execute(httpUriRequest, this);//傳入的ResponseHandler對象    }    /**     * 繼承ResponseHandler介面要實現的方法     * 執行完畢之後對response對象的處理介面     */    public HttpResponseInter handleResponse(HttpResponse response)throws ClientProtocolException, IOException {        //返回實現HttpResponseInter的類:返回給一個response介面        HttpResponseInter httpResponseInter = new HttpResponseImpl(response); //返回的時候需要response        return httpResponseInter;    }}

(4)然後下邊就是介面的實作類別:HttpResponseImpl.java 可以在構造方法中看到一個HttpResponse response對象,這就是在執行完request之後的handler返回的response對象。

/** * 介面的實作類別 * @author xuliugen */public class HttpResponseImpl implements HttpResponseInter {    private HttpResponse response; // HttpResponse對象    private HttpEntity entity; // HttpEntity試題對象    public HttpResponseImpl(HttpResponse response) throws IOException {        this.response = response;        HttpEntity tempEntity = response.getEntity();// 獲得伺服器端返回的entity        if (null != tempEntity) {            entity = new BufferedHttpEntity(tempEntity);        }    }    // 返回response對象的狀態代碼    public int statusCode() {        return response.getStatusLine().getStatusCode();    }    // 獲得結果的stream    public InputStream getResponseStream() throws IllegalStateException,            IOException {        InputStream inputStream = entity.getContent();        return inputStream;    }    // 獲得的結果轉化為string    public String getResponseStreamAsString() throws ParseException,            IOException {        return EntityUtils.toString(entity);    }    // 獲得的結果轉化為字元數組    public byte[] getResponseStreamAsByte() throws IOException {        return EntityUtils.toByteArray(entity);    }}

(5)ExecuteHttpPost.java這個類繼承了HttpRequestImpl.java在裡邊主要寫了兩個構造方法,構造方法就是實際的進行post請求的方法,和參數的設定:

/** * 這裡才是真正執行post請求的地?? *  * 繼承HttpRequestImpl 實現用戶端向伺服器端的請?? *  * @author xuliugen *  */public class ExecuteHttpPost extends HttpRequestImpl {    public ExecuteHttpPost(AbstractHttpClient httpClient, String url) {        this(httpClient, url, null);    }    public ExecuteHttpPost(AbstractHttpClient httpClient, String url,HttpEntity entity) {        super(httpClient);//父類中的httpClient        this.httpUriRequest = new org.apache.http.client.methods.HttpPost(url);// 初始化httpUriRequest        if (null != entity) {// 設定參數            ((HttpEntityEnclosingRequestBase) httpUriRequest).setEntity(entity);        }    }}

(6)另外一個重要的類就是用戶端的實現了:BaseHttpClient.java在這裡邊我們設定了一系列的方法,用於實現不同用戶端的要求方法,以及如何將用戶端請求的參數轉化為post請求的參數類型、將返回的資料轉化為相應的格式,方法的層疊調用,希望大家靜下心慢慢看。

/** * HttpClient用戶端的頂層類 */public class BaseHttpClient {    private AbstractHttpClient httpClient;    public static final int DEFAULT_RETIES_COUNT = 5;    protected int retriesCount = DEFAULT_RETIES_COUNT;    // 設定最大串連數    public final static int MAX_TOTAL_CONNECTIONS = 100;    // 設定擷取串連的最大等待時間    public final static int WAIT_TIMEOUT = 30000;    // 設定每個路由最大串連數    public final static int MAX_ROUTE_CONNECTIONS = 100;    // 設定連線逾時時間    public final static int CONNECT_TIMEOUT = 10000;    // 設定讀取逾時時間    public final static int READ_TIMEOUT = 10000;    /**     * 構造方法,調用初始化方法     */    public BaseHttpClient() {        initHttpClient();    }    /**     * 初始化用戶端參數     */    private void initHttpClient() {        //http的參數        HttpParams httpParams = new BasicHttpParams();        //設定最大串連數        ConnManagerParams.setMaxTotalConnections(httpParams,MAX_TOTAL_CONNECTIONS);        //設定擷取串連的最大等待時間        ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);        //設定每個路由最大串連數        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);        // 設定連線逾時時間        HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);        // 設定讀取逾時時間        HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);        HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);        SchemeRegistry schemeRegistry = new SchemeRegistry();        schemeRegistry.register(new Scheme(http, PlainSocketFactory.getSocketFactory(), 80));//設定連接埠80        schemeRegistry.register(new Scheme(https, SSLSocketFactory.getSocketFactory(), 443));//設定連接埠443        //就是管理SchemeRegistry的        ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);        httpClient = new DefaultHttpClient(clientConnectionManager, httpParams);        //建立http重新串連的handler        httpClient.setHttpRequestRetryHandler(new BaseHttpRequestRetryHandler(retriesCount));    }    /**     * 將參數轉化為 List 的集合     */    private List parseParams(HashMap params) {        if (params == null || 0 == params.size()){            return null;        }        List paramsList = new ArrayList(params.size());        for (Entry entry : params.entrySet()) {            paramsList.add(new BasicNameValuePair(entry.getKey(), entry.getValue() + ));        }        return paramsList;    }    /**     * 向伺服器端請求:當請求只有url 沒有參數的時候     */    public String post(String url) throws Exception {        return post(url, null); //調用有參數的時候執行的post並將參數設定為null    }    /**     * post請求之後返回T類型的結果     */    public  T post(String url, HashMap params, Class clz) throws Exception {        String json = post(url, params);        return JSONUtil.fromJson(json, clz); //轉化為具體的類型返回    }    /**     * 當請求有參數的時候,其他函數間接調用該方法     */    public String post(String url, HashMap params) throws Exception {        //將傳入的參數轉化為參數實體:將params轉化為enrity的對象:表單entity        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));        return request(url, entity).getResponseStreamAsString();    }    /**     * 將post執行的結果直接返回     */    public Result postAsResult(String url, HashMap params)throws Exception {        return post(url, params, Result.class);    }    /**     * 將post執行的結果一Stream的形式返回     */    public InputStream postAsStream(String url, HashMap params) throws Exception {        //將傳入的參數轉化為參數實體        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));        return request(url, entity).getResponseStream();    }    public HttpResponseInter request(String url, HttpEntity entity) throws Exception {        HttpRequestImpl httpRequestImpl = new ExecuteHttpPost(httpClient, url, entity);        return httpRequestImpl.request();    }}

(7)最後一個就是我們在httpClient中使用的一個BaseHttpRequestRetryHandler.java用於實現網路重複請求的次數

/** * http重新嘗試串連:主要用於完成嘗試重新串連 * @author xuliugen */public class BaseHttpRequestRetryHandler implements HttpRequestRetryHandler {    private int max_retry_count;// 最大嘗試串連的次數    public BaseHttpRequestRetryHandler(int maxretryCount) {        this.max_retry_count = maxretryCount;    }    private static HashSet> exceptionWhiteList = new HashSet>();    private static HashSet> exceptionBlackList = new HashSet>();    static {        exceptionWhiteList.add(NoHttpResponseException.class);        exceptionWhiteList.add(UnknownHostException.class);        exceptionWhiteList.add(SocketException.class);        exceptionBlackList.add(SSLException.class);        exceptionBlackList.add(InterruptedIOException.class);        exceptionBlackList.add(SocketTimeoutException.class);    }    public boolean retryRequest(IOException exception, int executionCount,HttpContext context) {        if (executionCount > max_retry_count){            return false;        }        if (exceptionBlackList.contains(exception.getClass())){            return false;        }        if (exceptionWhiteList.contains(exception.getClass())){            return true;        }        HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);        boolean idempotent = (request instanceof HttpEntityEnclosingRequest);        if (!idempotent) {            // 濡傛灉璿鋒眰琚涓烘槸騫傜瓑鐨勶紝閭d箞灝遍噸璿?            return true;        }        Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT);        boolean sent = (b != null && b.booleanValue());        if (!sent) {            return true;        }        return false;    }}
Service和AsyncTask的結合使用

大致流程如下:

(1)我們將任務統一的交給Service進行處理,這樣的話我們就需要一個Task實體<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">public class Task { private int taskId;// 任務ID private Map taskParams;// 參數 public static final int USER_LOGIN = 1; //自訂的一個任務ID //構造方法和get、set方法省略}

(2)下邊就是統一管理Task的Service,在Service中我們不僅需要統一的管理Task即是非同步任務,我們還需要負責管理更新介面的操作,因為更新介面的操作不能再住UI中進行,所以我們需要統一的管理activity,在Service中,我們執行非同步任務的操作使用過Thread和Handler實現的。

public class MainService extends Service implements Runnable {    // 任務隊列:用於存放任務的隊列    private static Queue tasks = new LinkedList();    // 將需要更新的UI添加到集合中    private static ArrayList appActivities = new ArrayList();    private boolean isRun;// 是否運行線程    Handler handler = new Handler() {        public void handleMessage(android.os.Message msg) {            switch (msg.what) {            case Task.USER_LOGIN: {// 使用者登入 :更新UI                //根據name找到activity:因為MAinActivity實現了MainActivityInter介面,所以可以強轉為MainActivityInter類型                MainActivityInter activity = (MainActivityInter) getActivityByName(MainActivity);                 activity.refresh(msg.obj.toString());                break;            }            default:                break;            }        };    };    /**     * 新增工作到任務隊列中     */    public static void newTask(Task t) {        tasks.add(t);    }    @Override    public void onCreate() {        isRun = true;        Thread thread = new Thread(this);        thread.start();        super.onCreate();    }    /**     * 讓服務一直遍曆執行     */    public void run() {        while (isRun) { // 去監聽任務            Task task = null;            if (!tasks.isEmpty()) { // 判斷隊列中是否有值                task = tasks.poll();// 執行完任務後把改任務從任務隊列中移除                if (null != task) {                    doTask(task); // TO DO :執行任務                }            }            try {                Thread.sleep(1000);            } catch (Exception e) {            }        }    }    // 處理任務    private void doTask(Task task) {        Message msg = handler.obtainMessage();        msg.what = task.getTaskId();        switch (task.getTaskId()) {        case Task.USER_LOGIN: { // 使用者登入            HashMap paramsHashMap =  (HashMap) task.getTaskParams();            //訪問網路,進行判斷使用者是否存在            String url = http://172.23.252.89:8080/igouServ/userlogin.action;            BaseHttpClient httpClient = new  BaseHttpClient();            try {                String result = httpClient.post(url, paramsHashMap);                msg.obj= result; //返回到handler進行處理            } catch (Exception e) {                e.printStackTrace();            }            break;        }        default:            break;        }        handler.sendMessage(msg);    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    /**     * 添加一個Activity對象到集合中     */    public static void addActivity(Activity activity) {        if (!appActivities.isEmpty()) {            for (Activity ac : appActivities) {                if (ac.getClass().getName().equals(ac.getClass().getName())) {                    appActivities.remove(ac);                    break;                }            }        }        appActivities.add(activity);    }    /**     * 根據Activity的Name擷取Activity對象     */    private Activity getActivityByName(String name) {        if (!appActivities.isEmpty()) {            for (Activity activity : appActivities) {                if (null != activity) {                    if (activity.getClass().getName().indexOf(name) > 0) {                        return activity;                    }                }            }        }        return null;    }    /**     * 退出系統     */    public static void appExit(Context context) {        // Finish 所有的Activity        for (Activity activity : appActivities) {            if (!activity.isFinishing())                activity.finish();        }        // 結束 Service        Intent service = new Intent(com.xuliugen.frame.task.MainService);        context.stopService(service);    }}

(3)為了可以讓Service統一的管理activity的話,我們可以書寫一個Interface介面MainActivityInter.java有兩個方法,其中一個就是為了重新整理介面,以便於我們在service中進行介面的操作

public interface MainActivityInter {    /**     * 初始化操作     */    public void init();    /**     * 重新整理UI     */    public void refresh(Object... params);}
測試步驟

(1)建立MainActivity.java 主要是為了類比一次登入操作,在這裡邊我們需要開啟服務,差UN該就愛弄一個任務,將任務加到Service管理的任務隊列中去,然後其他的操作就交給MainService.java(Service)進行操作了。

public class MainActivity extends Activity implements MainActivityInter {    private Button btn_login;    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn_login = (Button) this.findViewById(R.id.btn_login);        textView=  (TextView) this.findViewById(R.id.textView1);        // 啟動服務        Intent serviceIntent = new Intent(this, MainService.class);        startService(serviceIntent);        btn_login.setOnClickListener(new OnClickListener() {            public void onClick(View v) {                //構造參數傳給Task進行處理                Map paramsHashMap = new HashMap(2);                paramsHashMap.put(userName, xuliugen);                paramsHashMap.put(password, 123456);                Task task = new Task(Task.USER_LOGIN, paramsHashMap);                MainService.newTask(task);            }        });        // 將activity放入到activity隊列集合中        MainService.addActivity(this);    }    /******************** 以下兩個方法是MainActivityInter介面中的 ********************/    public void init() {    }    public void refresh(Object... params) {        //根據返回的參數進行更新UI         textView.setText(params[0].toString());    }}


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.