实现
集成之前自然要导入okhttp的jar包,在android studio中可以很方便地在gradle中添加依赖
- compile group: 'com.squareup.okhttp', name: 'okhttp', version: '2.7.5'
同步之后我们先来看看okhttp的基本用法(get):
- new Thread(new Runnable() {
- @Override
- public void run() {
- Request.Builder builder = new Request.Builder()
- .url("http://www.baidu.com") //指定网址
- .get(); //指定请求类型
- Request request = builder.build();
- OkHttpClient client = new OkHttpClient();
- try {
- //实际进行请求的代码
- Response response = client.newCall(request).execute();
- //获取后端返回的json
- String result = response.body().string();
- Log.i("result", result);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }).start();
讲道理okhttp已经大大简化了http访问的步骤,但是由于此为异步操作,需要新开一个线程进行,再加上try catch操作,无意中又增加了不少代码量。可以预见的是,日后执行其它的访问操作只是换了网址和访问类型等变量而已,所以我们何不将重复代码整合在一起呢。
整合的思路就是将以上的访问操作写入工具类中,而工具类采用单例模式,减少资源消耗。调用者只需传入网址、封装request参数的RequestBody、封装了访问成功后操作的回调函数三个参数便可以进行一次完整的http请求。是不是感到一阵熟悉,没错,就是照着ajax来的。还有一个关键点:访问类型,由于其数量有限,我们将其封装为枚举类,下文会进行详细讲解。
- package zyz.com.httputiltest.http;
- import com.squareup.okhttp.OkHttpClient;
- import com.squareup.okhttp.Request;
- import com.squareup.okhttp.RequestBody;
- import com.squareup.okhttp.Response;
- import java.io.IOException;
- /**
- * Created by zhuang_ge on 2017/11/13.
- */
- public class HttpUtil {
- private OkHttpClient client;//所有请求都由同一个client进行,减少资源消耗
- private String token;
- private SharedPreferences sp;//加入持久化,可以对一些特殊变量进行保存,比如用户id、用于登录验证的token等
- private SharedPreferences.Editor editor;
- private HttpUtil() {
- client = new OkHttpClient();
- // sp = ContextApplication.getContext()
- // .getSharedPreferences("login_data", Context.MODE_PRIVATE);
- // editor = sp.edit();
- // token = sp.getString("token", "");
- }
- private static HttpUtil httpUtil = null;
- //单例模式
- public static HttpUtil getInstance() {
- if (httpUtil == null) {
- httpUtil = new HttpUtil();
- }
- return httpUtil;
- }
- public void sendRequestWithCallback(final RequestTypeEnum method, final String address, final RequestBody body, final HttpCallbackListener listener
- ) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- Request.Builder builder = new Request.Builder()
- .url(address);
- switch (method) {
- case POST:
- builder.post(body);
- break;
- case PUT:
- builder.put(body);
- break;
- case DELETE:
- builder.delete(body);
- break;
- default:
- builder.get();
- break;
- }
- Request request = builder.build();
- try {
- //实际进行请求的代码
- Log.i("url = ", address);
- Response response = client.newCall(request).execute();
- token = response.header("token");
- if (token == null) {
- token = "b06804b910ea4f96a714a84d686d8583";
- Log.i("", "没有token使用默认token");
- } else {
- Log.i("", "收到token:" + token);
- }
- String result = response.body().string();
- if (result != null && listener != null) {
- //当response的code大于200,小于300时,视作请求成功
- if (response.isSuccessful()) {
- listener.onFinish(result);
- } else {
- listener.onError(new ServerException(result));
- }
- }
- } catch (IOException e) {
- if (listener != null) {
- listener.onError(e);
- }
- }
- }
- }).start();
- }
- public void post(final String address, RequestBody body, final HttpCallbackListener listener) {
- sendRequestWithCallback(RequestTypeEnum.POST, address, body, listener);
- }
- public void get(String address, HttpCallbackListener listener) {
- sendRequestWithCallback(RequestTypeEnum.GET, address, null, listener);
- }
- public void delete(String address, RequestBody body, HttpCallbackListener listener) {
- sendRequestWithCallback(RequestTypeEnum.DELETE, address, body, listener);
- }
- public void put(String address, RequestBody body, HttpCallbackListener listener) {
- sendRequestWithCallback(RequestTypeEnum.PUT, address, body, listener);
- }
- }
以上就是本工具类的核心代码,是不是看一眼就懂了?还有一些其它类也一并贴上:
回调接口类:HttpCallbackListener.java
- package zyz.com.httputiltest.http;
- import android.util.Log;
- import java.io.IOException;
- public abstract class HttpCallbackListener {
- public abstract void onFinish(String response);//正常访问之后进行的操作
- public void onError(Exception e) {//出错后进行的操作
- if (e instanceof IOException) {
- // io 异常
- Log.e("网络错误", e.getMessage());
- return;
- }
- }
- }
作为一个回调函数,在这里只需定义两个无需实现的函数就行。之前我用的是接口(interface)类,但遇到一个问题,对于调用方来说,执行onError时无法知悉出现的错误到底是网络本身的错误(连接不上服务器),还是服务器返回的错误(500)。换成抽象类之后,就可以写上一些判断语句,并对网络错误进行统一处理(比如弹个toast)。
访问类型枚举类:RequestTypeEnum.java
- public enum RequestTypeEnum {
- GET, POST, PUT, DELETE
- }
由于需求里就这些,暂时就写下这四种类型了,有需要的同学可以自行添加。
自定义错误类:
- public class ServerException extends IOException {
- public ServerException(String message) {
- super(message);
- }
- }
一看就懂,不再赘述。
使用
调用起来十分方便。编写一个界面验证一下
按钮的点击事件
- public void onClick(View view) {
- if(view.getId() == R.id.confirm) {
- String url = urlEdit.getText().toString().trim();
- HttpUtil.getInstance().get(url, new HttpCallbackListener() {
- @Override
- public void onFinish(final String response) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- contentEdit.setText(response);
- }
- });
- }
- @Override
- public void onError(Exception e) {
- super.onError(e);
- Log.i("mainactivity","出错了");
- }
- });
- }
- }
值得注意的是onError方法内部的书写有点门道,由于我们之前在抽象类中已经对网络错误进行统一处理并return,如果你想要自己再处理一下网络错误,则可以将代码写在super.onError之前。
同时,因为安卓强制要求在UI主线程中进行视图刷新,所以我们需要在回调函数里调用runOnUIThread。想搞事情的可以在工具类中获取当前的context,将其强转为Activity,再调用Activity.runOnUIThread,在其中执行回调函数,这样一来所有的onFinish都是在Ui主线程中执行的。
运行测试一下:
成功的获取到了数据。
最后贴一下源码地址,欢迎start和follow哦。源码地址