一个简单易用的Http访问工具类for Android

本文介绍了一个简单易用的Http访问工具类,适用于Android开发。该工具类旨在简化Http请求,提供内置回调处理结果,支持token管理和错误处理。通过集成OkHttp库,实现了单例模式,减少了重复代码。文章还涵盖了工具类的设计思路、核心代码以及如何使用。通过示例展示了如何在实际项目中调用工具类进行Http请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    前言

    去年(2017)参加服务外包省赛的时候,负责App开发的我遇到了一个小难题——Http请求。虽说已经有成熟的HttpUrlConnection库供使用,但依然感到有些不方便:进行一次简单的请求并对结果进行处理,需要敲下不少代码;多次请求之间其实有很多重复代码。幸运的我碰巧接触到了郭霖大神的《第二行代码》,借(chao)鉴(xi)于郭大对OkHttp的讲解,最终有了本次的HttpUtil工具类。

    优势

    本工具类的优势在于用最少的代码进行Http的请求,并提供内置回调函数以处理返回结果,而且可以对token的添加与获取、常见网络错误(500,400)进行统一处理,废话不多说,来一起看看是怎么实现的吧。

    实现

    集成之前自然要导入okhttp的jar包,在android studio中可以很方便地在gradle中添加依赖

compile group: 'com.squareup.okhttp', name: 'okhttp', version: '2.7.5'

    点击可查看okhttp的最新版本

    同步之后我们先来看看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哦。源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值