okHttp 基础及封装

okHttp 基础及封装

@(Android 学习)[okhttp, 网络]
[TOC]

Google 在 6.0 版本里面删除了 HttpClient 相关 API,OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。

OkHttp 官网
OkHttp 官方 API 文档
OkHttp GitHub 地址

1. 添加依赖

compile 'com.squareup.okhttp:okhttp:2.4.0'

因为 okhttp 内部依赖 okio,因此还需要导入 okio:

compile 'com.squareup.okio:okio:1.5.0'

2. 使用教程

2.1 Http Get

在网络请求中,最常见的就是 http get 请求,具体用法如下:
1. 首先构造一个 Request 对象,参数最起码要有个 url,也可以通过 Request.Builder 设置更多的参数,比如:headermethod等;
2. 通过 request 的对象构造一个 Call 对象,类似于将请求封装成任务;
3. 最后,以异步的方式去执行请求,调用call.enqueue,将 call 加入调度队列,然后等待任务执行完成,在 Callback 中即可得到结果。

// 创建 OKHttpClient 对象
OkHttpClient mOkHttpClient = new OkHttpClient();
// 创建一个 Request
final Request request = new Request.Builder().url("https://github.com/hongyangAndroid").build();

// new call
Call mCall = mOkHttpClient.newCall(request);
// 请求加入调度
mCall.enqueue(new Callback() {
    @Override
    public void onFailure(Request request, IOException e) {

    }

    @Override
    public void onResponse(Response response) throws IOException {
//                String htmlStr = response.body().string();
    }
});

onResponse 回调的参数是 response,一般情况下,如果希望获得返回的字符串,可以通过 response.body().string() 获取;如果希望获得返回的二进制字节数组,则调用 response.body().bytes();如果希望获得返回的 inputStream,则调用 response.body().byteStream()。
可以看到能够拿到返回的 inputStream,表明支持大文件下载,有 inputStream 就可以通过 IO 的方式写文件。这也说明一个问题,onResponse 执行的线程并不是 UI 线程,如果希望操作控件,就需要使用 handler,如:

Override
public void onResponse(Response response) throws IOException {
    final String res = response.body().string();
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mTv.setText(res);
        }
    });
}

上面的是用异步的方式去执行,也支持阻塞的方式,就是调用 Call.execute() 方法,也同样返回一个 Response。


2.2 Http Post 携带参数

Post 方法中,整体用法跟 Get 方法相似,只是 Request 的构造不同。Post 的参数是包含在请求体中的,所以通过 FormEncodingBuilder 添加多个 String 键值对,然后构造 RequestBody,完成 Request 的构造:

FormEncodingBuilder builder = new FormEncodingBuilder();
builder.add("username", "Savage_Lin");

Request request = new Request.Builder().url(url).post(builder.build()).build();
mOkHttpClient.newCall(request).enqueue(new Callback() {});

2.3 基于 Http 的文件上传

当需要类似表单上传的时候,可以使用 MultipartBuilder 来构造 RequestBody 实现。

File file = new File(Environment.getExternalStorageDirectory(), "balabala.mp4");

RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"),
        file);

RequestBody requestBody = new MultipartBuilder().type(MultipartBuilder.FORM).addPart
        (Headers.of("Content-Disposition", "form-data: name \"username\""), RequestBody
                .create(null, "Savage-Lin")).addPart(Headers.of("Content-Disposition",
        "form-data: name=\"mFile\"; filename=\"wjd.mp4\""), fileBody).build();

Request request = new Request.Builder().url("server url").post(requestBody).build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
    // ...
});

上述代码向服务器传递了一个键值对 username:Savage-Lin 和一个文件。通过 MultipartBuilder 的 addPart 方法添加键值对或文件。
这里类似于凭借模拟浏览器行为的方式,详细可参考HTTP 网络请求原理
图片下载是通过回调的 Response 拿到 byte[] 然后 decode 成图片;文件下载是拿到 inputStream 做写文件操作,关于用法,可以参考泡网OkHttp使用教程


3. 对基本请求的封装

因为 okhttp 中每次网络请求方式基本相似,因此对其进行一次封装,方便后续的开发与维护,框架类图如下:


@Android 网络框架类图

其中,BasicHttp 类封装了基本的网络请求参数以及网络请求抽象方法,LSHttp 类通过 SubAPI 类中传递过来的参数,实现了具体的网络请求方法,发起具体的网络请求;BasicAPI 类封装发起网络请求所需的参数以及网络请求回调方法,LSAPI 类相当于 API 类的中间类,封装了同一类业务中相同的请求参数以及请求操作,具体的参数设置以及具体的回调方法在 SubAPI 类中实现。

3.1 BasicHttp 类

BasicHttp 类是一个抽象类,封装了网络请求所需的基本参数及配置,代码如下:

public abstract class BasicHttp implements Callback {

    public BasicAPI basicAPI;

    public String url;

    public Headers headers;

    public RequestBody requestBody;

    // 创建一个 OkHttp 的请求对象
    public Request request;

    public Request.Builder requestBuilder = new Request.Builder();

    public BasicHttp(BasicAPI basicAPI) {
        this.basicAPI = basicAPI;
    }

    /**
     * 配置请求 URL 地址
     */
    public abstract void setURL();

    /**
     * 添加请求头部
     */
    public abstract void setHeaders();

    /**
     * 添加请求参数
     */
    public abstract void setParams();

    /**
     * 配置 Request
     */
    public abstract void setRequest();

    public void request() {

        setURL();

        setHeaders();

        setRequest();

        // 将请求加入请求队列
        App.getOkHttpClientInstance().newCall(request).enqueue(this);
    }

}

OkHttp官方文档并不建议我们创建多个 OkHttpClient,因此全局使用一个(如果有需要,可以使用 clone 方法,再进行自定义),在 App 类中实例化后调用,同时实现 Callback 接口用于处理请求回调。
LSHttp 类继承了 BasicHttp 类,实现了对 Request 参数的设置。代码如下:

public class LSHttp extends BasicHttp {

    private LSAPI api;

    // 创建一个 FormEncodingBuilder 对象,用于存放表单中的 item
    public FormEncodingBuilder bodyBuilder = new FormEncodingBuilder();

    public LSHttp(LSAPI api) {
        super(api);
        this.api = api;
    }

    @Override
    public void setURL() {
        if (api.getMethod() == RequestMethod.GET) {
            // if method is GET
            String param = "?";
            try {
                for (Map.Entry<String, String> entry :
                        api.getParams().entrySet()) {
                    param += entry.getKey() + "=" + entry.getValue() + "&";
                }
                url = api.getUrl() + param;
            } catch (Exception e) {
                url = api.getUrl();
            }
        } else {
            // if method is OTHER
            url = api.getUrl();
        }
    }

    @Override
    public void setHeaders() {
        headers = Headers.of(api.getHeaders());
    }

    @Override
    public void setParams() {
        for (Map.Entry<String, String> entry : api.getParams().entrySet()) {
            bodyBuilder.add(entry.getKey(), entry.getValue());
        }
        requestBody = bodyBuilder.build();
    }

    @Override
    public void setRequest() {
        if (api.getMethod().equals(RequestMethod.GET)) {
            requestBody = null;
        } else {
            setParams();
        }
        String method = api.getMethod().getMethod();
        request = requestBuilder.url(url).headers(headers).method(method, requestBody).build();
    }

    @Override
    public void onFailure(Request request, IOException e) {
        api.requestFailure(600L, "服务繁忙,请稍后再试");
    }

    @Override
    public void onResponse(Response response) throws IOException {
        JSONObject jsonResponse;

        if (response == null) {
            api.requestFailure(600L, "response null");
            return;
        } else {
            try {
                jsonResponse = new JSONObject(response.body().string());
            } catch (JSONException e) {
                e.printStackTrace();
                jsonResponse = new JSONObject();
            }
        }

        // 判断服务器是否返回请求数据
        boolean status = false;
        long code = -1;
        String msg = null;
        try {
            if (jsonResponse.has("status") && jsonResponse.has("code")) {

                status = jsonResponse.getBoolean("status");

                code = jsonResponse.getLong("code");

                if (jsonResponse.has("msg")) {
                    msg = jsonResponse.getString("msg");
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        if (200L == code && status) {
            // 请求成功
            JSONObject data;
            try {
                data = jsonResponse.getJSONObject("data");
                api.requestSuccess(data, msg);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } else {
            // 请求失败
            api.requestFailure(600L, "request error");
        }
    }

}

若是 GET 请求,则将参数拼接到 URL 中,同时将 requestBody 赋值为 null,若是其他请求,则将参数封装到 requestBody 中。同时对请求结果进行初次处理,若请求成功,且服务器返回的数据为 JSON 类型,如下:

<!--服务器返回的数据如下:-->
{"status":true,"code":200,"data":{"result":{"method":"put","param":"good!"}}}

解析出 data 中的数据,交由 API 类处理。不解析到 result 字段是因为服务器返回来的数据可能是一个 JSONObject 对象,也有可能是一个 JSONArray 数据,还有可能就是一个字段,因此解析到 data。
BasicAPI 类和 LSAPI 类封装了 Request 中所需参数的 getter/setter 方法以及对返回数据的处理抽象方法。具体的业务 API 类只需实现 getUrl()、getMethod()、success()、error()方法即可,如下:

public class HelloAPI extends LSAPI {

    public HelloAPI() {
        addParam("param", "hello");

        new LSHttp(this).request();
    }

    @Override
    public String getUrl() {
        return "http://api.6shangwang.com/region/app/v2/region/orgin";
    }

    @Override
    public void success(JSONObject data, String msg) throws Exception {
        Log.e("HelloAPI success", data.toString());
    }

    @Override
    public void error(Long code, String msg) {
        Log.e("HelloAPI error", msg);
    }

    @Override
    public RequestMethod getMethod() {
        return RequestMethod.POST;
    }

}

在需要发送该网络请求时,只需一行代码即可发送该网络请求:

new HelloAPI();

4. 对文件上传/下载的封装




以上,即是对 OkHttp 框架的简单介绍以及 Android 整合 OkHttp 的网络框架。


|center

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值