概述:此篇为Android网络请求框架的第二弹,基于原始的请求Api HttpUrlConection的封装

一 我们为什么需要封装网络请求。

1让我们来看看,不使用封装的情况下,是如何使用HttpUrlConnection请求的。

a 让我们来回顾一些基本的概念。

①主线程不能执行耗时操作。
所以我们使用的HttpUrlConection需要在子线程中进行。
②子线程中不能修改UI。
所以我们需要将在子线程中请求的数据,传回到到主线程。
③基于以上两点,我们使用AsyncTask异步任务来做到以上两点。(这里对AsyncTask不熟悉的同学请自行百度用法)

b 一个请求的基本步骤。

① 开始一个异步任务。
HttpAsyncTask httpAsyncTask=new    HttpAsyncTask(context,method,params,listener);
httpAsyncTask.execute(url);
在构造方法传入基本参数了和一个接口 我们将会通过这个接口将数据传出去。
② 在主线程中得到结果
doGet(MainActivity.this, "get", url, null, new HttpResponseListener() {
                    @Override
                    public void onRequestResult(String response) {
                      //这里可以对UI进行操作使用 response
                    }
                });

2 从这里可以看到,我们每发出一个请求,都需要new一个异步任务。

二 封装之后一个网络请求的基本步骤 。

1 传入一个Url 和参数集合。

不管是get请求还是post请求 都会有Url 和参数集合不同个是get请求需要将url和参数集合拼接在一起,而post请求需要将参数拼接之后传入到请求体body中。

2 开始一个请求,显示一个正在请求的Loading

3 开始下载任务,我们需要传入 url和参数集合

4 结束一个请求,关闭Loading,在主线程中使用请求后的数据。

三 封装网络请求的步骤。

1定义一个请求结果的实体类 Response 包含四个字段。

private boolean error;
private int errorType;
private String errorMessage;
private String result;

这里isError是调用api成功与否,errorType是错误类型(如果成功则为0)errorMessage是错误信息(如果成功则为空)
result 是成功请求返回的数据结果(如果失败则返回为空) 我们这里的思想就是 只要发送一个请求 一定会返回一个Response对象 像没有网络和url不正确的情况 都会返回一个response对象 这里的error 就为true errorType 可以定义为-1 errorMessage则为“当前网络不可用,请稍后再试”result自然就为空了“”。其他异常情况同理

2定义接口 HttpRequestListener 提供 一系列请求过程需要用到的方法

public interface HttpRequestListener<T> {
    /**
     * 开始请求时
     */
    void onStartRequest();

    /**
     * 将一个字符串解析成一个对象传出来
     *
     * @param t
     */
    void onRequestResult(T t);

    /**
     * 请求结果
     *
     * @param response
     */
    void onRequestResult(Response response);

}

3 一个抽象类 HttpResponseListener实现HttpResponseListener接口,提供一些方法,在请求结果返回之前和返回之后调用。

public abstract class HttpResponseListener<T> implements HttpRequestListener<T> {
    private Class<T> cls;
    private Context context;
    private boolean isShowLoading;
    private CustomLoadingController controller;

    /**
     * 默认显示Loading
     *
     * @param context
     * @param cls
     */
    public void init(Context context, Class<T> cls) {
        this.context = context;
        this.cls = cls;
        this.isShowLoading = true;
    }

    /**
     * 由外界选择是否显示Loading
     *
     * @param context
     * @param cls
     * @param isShowLoading
     */
    public void init(Context context, Class<T> cls, boolean isShowLoading) {
        this.context = context;
        this.cls = cls;
        this.isShowLoading = isShowLoading;
    }


    /**
     * 开始请求之前的动作  比如显示进度条
     */
    @Override
    public void onStartRequest() {

        if (context != null && isShowLoading) {
            if (!((Activity) context).isFinishing()) { //判断当前页面是否关闭
                if (controller == null) {
                    controller = new CustomLoadingController(context);
                    controller.create();
                }
                controller.show();
            }
        }
    }

    @Override
    public void onRequestResult(Response response) {
        if (isShowLoading && controller != null) {
            controller.dismiss();
        }
        if (response.getErrorType() == -200) {
            showErrorMessage("当前网络不可用,请稍后再试");
        } else if (response.isError()) {
            Log.i("errorMessage", response.getErrorMessage());
        } else {
            String strResponse = response.getResult();
            T t = new Gson().fromJson(strResponse, cls);
            onRequestResult(t);
        }
    }

    public void showErrorMessage(String errorMessage) {
        Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();
    }


}

在这个抽象类中 我们实现接口中的部分方法,比如 void onStartRequest(); 在一个请求开始之前的方法 在这个方法中 我们实现了 显示一个Loading 。其中 void onRequestResult(T t);方法并没有具体实现 在 void onRequestResult(Response response) 方法中是这样实现的:

if (isShowLoading && controller != null) {
            controller.dismiss();
        }
        if (response.getErrorType() == -200) {
            showErrorMessage("当前网络不可用,请稍后再试");
        } else if (response.isError()) {
            Log.i("errorMessage", response.getErrorMessage());
        } else {
            String strResponse = response.getResult();
            T t = new Gson().fromJson(strResponse, cls);
            onRequestResult(t);
        }

首先是关闭了Loading 然后对Response进行判断 当result中有值时 将字符串转换成对象 然后 通过 onRequestResult(t)方法调出去。

4 真正执行请求的异步任务 HttpAsyncTask 。

a 构造方法:

public HttpAsyncTask(Context context, String method, HashMap<String, Object> paramsMap, HttpResponseListener responseListener)

b请求之前的 onPreExecute

protected void onPreExecute() {
        super.onPreExecute();
        mResponseListener.onStartRequest();
    }

c 在子线程中执行耗时操作的 doInBackground

protected Response doInBackground(String... strings) {
        Response response = new Response();
        if (mContext != null) {
            if (strings[0] == null) {
                response.setError(true);
                response.setErrorType(-2);
                response.setErrorMessage("请求地址为空");
                return response;
            } else if (!NetUtils.isNetworkConnected(mContext)) {
                response.setError(true);
                response.setErrorType(-200);
                response.setErrorMessage("网络没有连接");
            } else {
                try {
                    return new DownUtils().getResponse(strings[0], method, mParamsMap);
                } catch (UnsupportedEncodingException e) {
                    response.setError(true);
                    response.setErrorType(-1);
                    response.setErrorMessage("下载异常");
                }
            }
        }
        return null;
    }

首先对网络和url进行判断 返回一个Response 。当网络和url没有问题时,执行下载操作 new DownUtils().getResponse(strings[0], method, mParamsMap);

d 在执行耗时操作后返回的onPostExecute

protected void onPostExecute(Response response) {
        if (response == null) {
            Response response1 = new Response();
            response1.setError(true);
            response1.setErrorType(-1);
            response1.setErrorMessage("请求异常");
            response1.setResult("");
            mResponseListener.onRequestResult(response);
        } else {
            mResponseListener.onRequestResult(response);
        }
    }

首先还是对Response进行判断 然后通过接口 mResponseListener.onRequestResult(response); 将请求结果传出去。我们已经在抽象类中进行了处理。

5 正在下载的工具类 DownUtils

public Response getResponse(String url, String method, HashMap<String, Object> paramsMap) throws UnsupportedEncodingException {
        if (method.equals("get")) {
            String newUrl = getUrl(url, paramsMap);
            return getResponseByGet(newUrl);
        } else if (method.equals("post")) {
            return getResponseByPost(url,getUrlData(paramsMap));
        } else {
            return null;
        }
    }

我们提供一个方法 传入了url 参数集合 标识字符串method 返回一个 Response .还提供了 一系列的下载方法 和拼接字符串的方法。根据项目需要我们还可以提供 一些加密的方法。

四 项目使用时需要注意的地方。

1 一个常量类Constant 保存用到的url

public class Constant {
    public static String LOGINURL = "http://192.168.31.141:8080/ServerDemo/loginServlet";
}

2 一个请求类

public class ApiClass {
    /**
     * 登录方法
     * @param context
     * @param params
     * @param listener
     */
    public void loginRequestByGet(Context context, HashMap<String, Object> params, HttpResponseListener listener){
        String url = Constant.LOGINURL;
        sendRequest(context,url,"get",LoginBean.class,params,listener);
    }


    /**
     * 发送请求
     * @param context
     * @param url
     * @param method
     * @param params
     * @param listener
     */
    public void sendRequest(Context context, String url,String method,Class cls, HashMap<String, Object> params, HttpResponseListener listener) {
        if (listener != null) {
            listener.init(context, cls);
            HttpAsyncTask httpAsyncTask = new HttpAsyncTask(context, method, params, listener);
            httpAsyncTask.execute(url);
        }
    }
}

3 具体使用的地方。

btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
              new ApiClass().loginRequestByGet(MainActivity.this, map, new HttpResponseListener<LoginBean>() {
                  @Override
                  public void onRequestResult(LoginBean loginBean) {
                      Log.i("xixi", loginBean.toString());
                  }
              });
            }
        });

五 总结

1其实写下来感觉有些细节的地方处理的并不好,但是总体的框架是可以的。

2 具体的细节根据具体的项目可以自由更改。