Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 利用Handler進行網絡請求

利用Handler進行網絡請求

編輯:關於Android編程

那麼我們就來用Handler制作一個簡易的網絡請求框架。
如下圖:
這裡寫圖片描述
解釋一下:UI在request的時候傳入UI中的Handler,同時將請求的Runnable推入到工作線程對應中的Handler,在工作線程中的Handler調用完畢之後,有通過傳遞過來的UI的Handler將數據傳送到UI,更新頁面。
其實核心就是UI中的Handler和工作線程中的Handler,UI中的Handler負責數據的傳遞,工作線程中的Handler負責請求的隊列和調度。那麼來看具體的代碼:
首先是工作線程:

public class SvrBgThread extends Thread {
    private static String TAG = "SvrBgThread";
    private Handler mBgTaskHandler;

    public static final class TaskCmd {
        /**
         * 接收後台消息
         */
        public static final int ACCEPT_MSG = 1;
        /**
         * 拒絕後台消息
         */
        public static final int REFUSE_MSG = ACCEPT_MSG + 1;
        /**
         * 退出後台線程
         */
        public static final int EXIT_TASK = REFUSE_MSG + 1;
    }

    private Runnable currentRunnable;

    @Override
    public void run() {
        synchronized (SvrBgThread.this) {
            Looper.prepare();
            mBgTaskHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    int what = msg.what;
                    switch (what) {
                        case TaskCmd.ACCEPT_MSG:
                            break;
                        case TaskCmd.REFUSE_MSG:
                            cancelTask(currentRunnable);
                            break;
                        case TaskCmd.EXIT_TASK:
                            Looper.getMainLooper().quit();
                            break;
                    }
                }

                @Override
                public void dispatchMessage(Message msg) {
                    if (msg.getCallback() != null) {
                        currentRunnable = msg.getCallback();
                    }
                    super.dispatchMessage(msg);
                }
            };
            Looper.loop();
        }
    }


    private void cancelTask(Runnable runnable) {
        if (runnable == null) {
            return;
        }
    }

    public synchronized void cancelCurrentTask() {
        cancelTask(currentRunnable);
    }

    public synchronized void cancelAllTask() {
        if (mBgTaskHandler == null)
            return;

        //移除所有命令
        mBgTaskHandler.removeCallbacksAndMessages(null);
        cancelCurrentTask();
    }

    public synchronized Handler getBgTaskHandler() {
        return mBgTaskHandler;
    }

    public synchronized void exit() {
        if (mBgTaskHandler == null)
            return;
        mBgTaskHandler.sendEmptyMessage(TaskCmd.EXIT_TASK);
    }
}

這裡在Thread中實例化Handler,這時,Handler中的消息隊列就准備好了,在Looper.loop()後,Looper就不斷從消息隊列中取出Message來執行。那麼這種耗時的網絡請求的執行就在工作線程中了(在主線程會ANR),並且請求的隊列也有了,請求的調度方式也有了,利用Android系統的Handler機制,我們免去了自己控制消息隊列的各種麻煩。
看請求的接口。

public interface IStudentProvider {
    String URL_STUDENT = "datainfo/getStudentInfo";
    /**
     * 統一的學生信息接口
     *
     * @param reqType
     * @param uiHandler
     * @param requestUrl
     * @param statisticTime
     * @param userName
     */
    boolean requestStudentInfo(Handler uiHandler,String url,int msgType);

}

看到接口中定義了請求一個學生的request的定義。
其中uiHandler就是UI中的Handler,負責數據傳回UI,msgType是該請求的標識,用來匹配請求(在ListView中Item的復用導致的數據變動,解決方法就是setTag後在匹配Tag,一樣的道理)
看實現

public class StudentProvider implements IStudentProvider {

    public static final String TAG = "StudentProvider";
    private SvrBgThread mSvrBgThread;

    private StudentProvider() {
        super();
        mSvrBgThread = new SvrBgThread();
    }

    private static class InstanceHolder {
        static final StudentProvider INSTANCE = new StudentProvider();
    }

    /**
     * 單例模式,延遲加載
     */
    public static StudentProvider getInstance() {
        return InstanceHolder.INSTANCE;
    }

    public void init() {
        mSvrBgThread.setName(TAG);
        mSvrBgThread.start();
    }


    @Override
    public boolean requestStudentInfo(Handler uiHandler, String url, int msgType) {


        if (uiHandler == null || url == null) {
            return false
        }

        HttpGet httpGet = null;
        try {
            // 獲取Post對象,輸入參數以JSON格式放置在body中
            httpGet = HttpClientProxy.getJsonHttpGet(requestUrl);
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "Exception", e);
            return false;
        } catch (JSONException e) {
            Log.e(TAG, "Exception", e);
            return false;
        }
        Student student = new Student();
        // 創建http請求對象
        HttpRequestRunnable requestRunnable = new HttpRequestRunnable(uiHandler, student, msgType, httpGet );

        if (mSvrBgThread == null || !mSvrBgThread.isAlive()) {
            Log.e(TAG, "svrBgThread not initialized !");
            return false;
        }
        // 發送http請求
        return mSvrBgThread.getBgTaskHandler().post(requestRunnable);
    }
}

注意在init()方法中對工作線程的實例化,在requestStudentInfo方法中通過構造HttpRequestRunnable 具體的請求,隨後將該具體請求 推入到了工作線程中對應的Handler(mSvrBgThread.getBgTaskHandler().post(requestRunnable))中,當Handler取出該Runnable執行後就得到了請求的結果,其中HttpClientProxy提供具體的請求方式,比如post,get,put等
具體的HttpRequestRunnable :

public class HttpRequestRunnable implements Runnable {
    private Handler mMsgHandler;
    private IUserDataBuilder mUserData;
    private int msgType;
    private HttpUriRequest httpUriRequest;

    public HttpRequestRunnable(Handler msgHandler, IUserDataBuilder userData, int msgType, HttpUriRequest httpUriRequest) {
        this.mMsgHandler = msgHandler;
        this.mUserData = userData;
        this.msgType = msgType;
        this.httpUriRequest = httpUriRequest;
    }



    /**
     * 處理 服務端響應數據
     *
     * @param databuilder
     *            用戶數據構造器,解析數據後的結果會填入該類中
     * @param response
     *            http 響應結果
     */
    private void handleResponse(IUserDataBuilder databuilder, HttpResponse response) throws Exception
    {
        int StatusCode = response.getStatusLine().getStatusCode();

        switch (StatusCode)
        {
            case HttpStatus.SC_NOT_FOUND:
                Log.e(TAG, "Response StatusCode:" + StatusCode);
                databuilder.setServerRet(ServerRet.NOTFOUND);
                throw new Exception();
            case HttpStatus.SC_UNAUTHORIZED:
                Log.e(TAG, "Response StatusCode:" + StatusCode);
                databuilder.setServerRet(ServerRet.UNAUTHORIZED);
                throw new Exception();
            case HttpStatus.SC_OK:
                Log.d(TAG, "Response StatusCode:" + StatusCode);
                // 設置默認值,有的接口不會攜帶retCode字段,平台統一添加該字段
                databuilder.setServerRet(ServerRet.OK);
                // 解析返回數據
                JSONObject retJsonObject = createJSONFromHttpEntity(response.getEntity());
                if (retJsonObject == null)
                {
                    // 創建默認的JSON數據,保證解析框架正常
                    Log.e(TAG, "Create default Json data.");
                    retJsonObject = HttpUtil.createHttpJson(ServerRet.ILLEGAL_STATE_EXCEPTION);
                    databuilder.setServerRet(ServerRet.ILLEGAL_STATE_EXCEPTION);
                }
                databuilder.parseJson(retJsonObject);
                break;
            default:
                Log.e(TAG, "Response StatusCode:" + StatusCode);
                throw new Exception();
        }

    }


    /**
     * 提取HTTPEntity中的JSON對象
     *
     * @param httpEntity
     * @return
     */
    private JSONObject createJSONFromHttpEntity(HttpEntity httpEntity)
    {
        JSONObject jsonObj = null;
        String entity = null;
        try
        {
            entity = EntityUtils.toString(httpEntity, HttpClientProxy.ENCODING);
            jsonObj = new JSONObject(entity);
        }
        catch(Exception e)
        {
            Log.e(TAG,"Exception",e);
        }
        return jsonObj;
    }


    @Override
    public void run() {

        if (!isConditionMet()) {
            Log.e(TAG, "Invalid RequestRunnable:" + toString());
            return;
        }

        try {
            // 向遠程服務端發送數據請求並獲取請求結果
            HttpClient client = HttpClientProxy.httpClientBuilder();

            HttpResponse response = client.execute(mHttpUriRequest);

            handleResponse(mUserDatabuilder, response);

            client.getConnectionManager().shutdown();

        } catch (Exception e) {
            if (httpUriRequest.isAborted()) {
                mUserData.setServerRet(ServerRet.CLIENT_ABORT_REQUEST);
            } else {
                mUserData.setServerRet(transformException(e));
            }

            Log.e(TAG, "Exception", e);
        } finally {
            synchronized (mMsgHandler) {
                Message message = Message.obtain(mMsgHandler, msgType, mUserData);
                boolean success = mMsgHandler.sendMessage(message);
                if (!success) {
                    Log.e(TAG, "send message back to user fail,"
                            + "usually because the looper processing the message queue is exiting");
                }
            }
        }
    }

    private boolean isConditionMet() {
        if (mUserData == null) {
            return false;
        }
        if (mMsgHandler == null) {
            return false;
        }
        if (httpUriRequest == null) {
            return false;
        }
        return true;
    }
}

在HttpRequestRunnable 中其構造函數的參數分別是UI中的Handler,請求結果對象,請求的標志,和請求的方式。該類主要負責請求的執行,在執行結束後將數據設置到UI的Handler中( Message message = Message.obtain(mMsgHandler, msgType, mUserData);)。
數據是如何解析的:
IUserDataBuilder :

public interface IUserDataBuilder {

    /**
     * 將JSON數據解析成用戶期望的數據類型
     *
     * @param jsonObject
     *            JSON對象
     * @return 解析成功返回true,否則返回時報
     * @throws Exception
     */
    boolean parseJson(JSONObject jsonObject) throws Exception;

    ServerRet getServerRet();

    void setServerRet();
}
public enum ServerRet {
    OK(000,"ok");


    private int retCode;
    private String msg;

    ServerRet(int retCode,String msg) {
        this.retCode = retCode;
        this.msg = msg;
    }
}

可以看到IUserDataBuilder ,所有實體類需要實現的接口,都必須有對解析Json的過程,還有設置與獲取請求的結果(成功與否)
ServerRet:請求結果的枚舉。
Student:

public class Student implements IUserDataBuilder {
    private ServerRet mServerRet;
    private String name;
    private int age;
    private String mAddr;
    @Override
    public boolean parseJson(JSONObject jsonObject) throws Exception {
        if (jsonObject == null){
            return false;
        }
        Student temp = HttpUtils.fromJson(jsonObject.toString(),Student.class);
        name = temp.getName();
        age = temp.getAge();
        mAddr = temp.getAddr();
        return true;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddr() {
        return mAddr;
    }

    @Override
    public ServerRet getServerRet() {
        return null;
    }

    @Override
    public void setServerRet() {

    }
    @Override
    public String toString() {
        return "Student{" +
                "mServerRet=" + mServerRet +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", mAddr='" + mAddr + '\'' +
                '}';
    }
}

可以實體類中的parseJson使用Gson來解析json,fromJson如下:

    public static  T fromJson(String jsonStr, Class mClass)
    {
        Gson mGson = new Gson();
        T mt = mGson.fromJson(jsonStr, mClass);
        return mt;
    }

至此解析結束,實體類數據已填充。
看看使用:

public class StudentInfoActivity extends Activity {
    public static final String TAG = "StudentInfoActivity";
    public static final String URL = "http://10.10.12.158:8080";
    public static final int MSGTYPE = 1;

    private static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            if (what == MSGTYPE) {
                Student student = (Student) msg.obj;
                if (student.getServerRet() == ServerRet.OK) {
                    Log.i(TAG, student.toString())
                } else {
                    Log.i(TAG, "student parse failed");
                }

            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        IStudentProvider studentProvider = StudentProvider.getInstance();
        studentProvider.requestStudentInfo(handler, URL, MSGTYPE);
    }
}

最後記得將StudentProvider .getInstance().init()方法放在Application的onCreate方法中,在程序啟動時將其初始化了。在退出是cancel。
總結:
在UI發送請求時,將UI中的Handler等傳入到具體的請求方法中(StudentProvider 的requestStudent方法中),在該方法中實現了具體的請求HttpRequestRunnable ,隨後將HttpRequestRunnable推入到工作線程的Handler對應的MessageQueue中,在Handler輪訓執行到HttpRequestRunnable後,將執行具體的請求同時將傳遞過來的JsonObject解析為具體的實體類,然後傳遞給UI的Handler,返回給UI。
弊端:相比較Volley,OkHttp,XUtuis等開源庫,使用Handler的請求麻煩,不易擴展,同時當連續執行多個請求時,該請求是在工作線程中串型執行的,並發性不好。
這裡旨在體會Handler的另外一種不常見的用法。深刻體會Handler機制的原理。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved