写一个简单版的OkHttp

本文介绍了如何实现一个简单的OkHttp,通过创建IHttpRequest接口、CallbackListener回调、HttpTask任务及ThreadPoolManager来管理网络请求。当请求失败时,提供重试机制。通过分析简单版OkHttp的构建过程,有助于理解OkHttp的内部工作原理。

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

关于OkHttp的名气,相信每一位Android开发者都了解并且使用过,为我们项目中网络请求提供了很大的便捷。OkHttp提供了对网络请求的封装,我们只要调用其暴露的Api,就可以轻松地实现网络相关功能。那关于它的内部实现原理是怎么样的呢,下面将通过一实现一个简单的OkHttp,带你进入OkHttp的内心世界。

OkHttp的使用
OkHttpClient client = new OkHttpClient();//创建okHttpClient对象
//创建网络请求
Request request = new Request.Builder()
                        .url("https://www.baidu.com")
                        .build();
//创建Call请求对象,通过call开启网络访问
Call call = client.newCall(request);

//1、同步请求
new Thread(new Runnable() {
    @Override
    public void run() {
       try {
          Response response = call.execute();
       } catch (IOException e) {
          e.printStackTrace();
       }
     }
 }).start();

//2、异步请求
call.enqueue(new Callback() {
     @Override
     public void onFailure(Call call, IOException e) {
          e.printStackTrace();
     }
     @Override
     public void onResponse(Call call, Response response) throws IOException {
         Log.d("response",response.toString());
     }
});

上面就是关于OkHttp的基本使用,通常情况下结合Retrofit一起使用,效果更佳。

简单版OkHttp

我们知道,在客户端会有多个网络请求,多个请求任务也会在同一时间发起请求,这种情况下OkHttp怎么处理的呢?某一个请求失败后又该怎么处理呢?下面我们就解决这些问题。
项目结构:
在这里插入图片描述
整体思路:通过IHttpRequest接口定义网络请求,JSonHttpRequest为其实现类;CallbackListener为网络请求回调接口,JsonCallbacklistener为其实现类;HttpTask为网络任务的封装,里面包含了IHttpRequest、CallbackListener对象;有了网络任务对象后,通过ThreadPoolManager来进行任务管理,创建任务队列管理任务,线程池处理任务;当请求失败后,可以选择将失败任务放入失败队列,再次放入线程池发起请求。

在这里插入图片描述
网络请求Request

public interface IHttpRequest<T> {
    //请求类型
    void setType(String type);
    //请求地址
    void setUrl(String url);
    //请求参数
    void setData(T data);
    //请求结果回调
    void setCallListener(CallbackListener callListener);
    //网络请求函数
    void execute();
}
public class JSonHttpRequest implements IHttpRequest<HashMap> {
    private String type;
    private String mUrl;
    private HashMap<String,String> data;
    private CallbackListener callbackListener;
    private HttpURLConnection mHttpUrlCon;

    @Override
    public void setType(String type) {
        this.type = type;
    }

    @Override
    public void setUrl(String url) {
        this.mUrl = url;
    }

    @Override
    public void setData(HashMap data) {
        this.data =  data;
    }

    @Override
    public void setCallListener(CallbackListener callListener) {
        this.callbackListener = callListener;
    }

    @Override
    public void execute() {
        URL url = null;
        try {
            url = new URL(mUrl);
            mHttpUrlCon = (HttpURLConnection) url.openConnection(); //打开网络连接
            mHttpUrlCon.setConnectTimeout(5000);
            mHttpUrlCon.setUseCaches(false);
            mHttpUrlCon.setInstanceFollowRedirects(true);//是否可以被重定向
            mHttpUrlCon.setReadTimeout(5000);//响应超时
            mHttpUrlCon.setRequestMethod(type);
            mHttpUrlCon.setDoInput(true);//是否可以输入数据
            mHttpUrlCon.setDoOutput(true);//是否可以输出数据
            mHttpUrlCon.setRequestProperty("content-type","application/x-www-form-urlencoded");//消息类型的设置
            mHttpUrlCon.connect();//开始连接

            //请求参数
            if (data != null && !data.isEmpty()){
                String param = "";
                for (String key: data.keySet()){
                    param += key + "=" + data.get(key) + "&";
                }
                param = param.substring(0,param.length()-1);
                OutputStream out = mHttpUrlCon.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out);
                osw.write(param);
                osw.flush();// 刷新
                out.close();
                osw.close();
            }

            if (mHttpUrlCon.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStream in = mHttpUrlCon.getInputStream();
                callbackListener.onSuccess(in);
            } else {
                callbackListener.onFailure();
            }
        } catch (Exception e){
            //e.printStackTrace();
            throw new RuntimeException("请求失败");
        } finally {
            mHttpUrlCon.disconnect();
        }
    }
}

代码的实现比较简单,主要获取网络请求所需要的一些参数,比如URL、请求类型GET/POST等。主要在实现execute()方法,通过HttpURLConnection开启实际的网络访问,并将访问结果通过callbackListener监听。

**结果回调CallbackListener **

public interface CallbackListener {
    //成功回调
    void onSuccess(InputStream inputStream);
    //请求失败回调
    void onFailure();
}
public class JsonCallbacklistener<T> implements CallbackListener {
    private Class<T> resquestClass;
    private DataCallback dataCallback;
    private Handler handler = new Handler(Looper.getMainLooper());

    public JsonCallbacklistener(Class<T> resquestClass,DataCallback callback) {
        this.resquestClass = resquestClass;
        this.dataCallback = callback;
    }

    @Override
    public void onSuccess(InputStream inputStream) {
        String reaponse = getResponse(inputStream);
        final T clazz = JSON.parseObject(reaponse,resquestClass);
        handler.post(new Runnable() {
            @Override
            public void run() {
                dataCallback.onSucess(clazz);
            }
        });
    }

    @Override
    public void onFailure() {

    }

    private String getResponse(InputStream inputStream) {

        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line = null;

        try {
            while ((line = reader.readLine()) != null){
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
}

在回调接口的函数里,通过Json处理,将网络请求结果以泛型类的形式返回给客户端。有了上面的请求request、CallbackListenner对象,那么我们就可以创建具体的网络请求任务HttpTask了。

**HttpTask **

public class HttpTask<T> implements Runnable,Delayed {

    private IHttpRequest mHttpRequest;
    public HttpTask(String url,String type,T requestData,IHttpRequest httpRequest,CallbackListener callbackListener) {
        mHttpRequest = httpRequest;
        mHttpRequest.setUrl(url);
        mHttpRequest.setType(type);
        mHttpRequest.setData(requestData);
        mHttpRequest.setCallListener(callbackListener);
    }

    @Override
    public void run() {
        try {
            mHttpRequest.execute();
        } catch (Exception e){
            //重试机制
            ThreadPoolManager.getInstance().addDelayTask(this);
        }
    }


    private long delaytime;
    private int reTryCount;

    public long getDelaytime() {
        return delaytime;
    }

    public void setDelaytime(long delaytime) {
        this.delaytime = System.currentTimeMillis() + delaytime;
    }

    public int getReTryCount() {
        return reTryCount;
    }

    public void setReTryCount(int reTryCount) {
        this.reTryCount = reTryCount;
    }

    @Override
    public long getDelay(@NonNull TimeUnit unit) {
        return unit.convert(delaytime - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(@NonNull Delayed o) {
        return 0;
    }
}

可以看到,HttpTask实现了Runnable接口,为一个新的线程任务。为了重试机制,同时又实现了Delayed 接口,通过setDelaytime方法设置Task在加入到重试队列后的延迟时间。

**ThreadPoolManager **

public class ThreadPoolManager {

    private static ThreadPoolManager mInstans;
    private ThreadPoolExecutor mThreadPool;//线程池
    private LinkedBlockingQueue<Runnable> mTaskQueue = new LinkedBlockingQueue<>();//任务等待队列

    private ThreadPoolManager(){
        mThreadPool = new ThreadPoolExecutor(3, 5, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                addTask(r);
            }
        });
        mThreadPool.execute(callRun);
        mThreadPool.execute(callDelayRun);
    }

    public static ThreadPoolManager getInstance(){
        if (mInstans == null){
            synchronized (ThreadPoolManager.class){
                if (mInstans == null){
                    mInstans = new ThreadPoolManager();
                }
            }
        }
        return mInstans;
    }

    //讲网络请求Task加入等待队列
    public void addTask(Runnable runnable){
        if (runnable != null){
            mTaskQueue.add(runnable);
        }
    }

    //创建“叫号”线程,不断的从队列里获取新的任务
    public Runnable callRun = new Runnable() {
        Runnable runnable = null;
        @Override
        public void run() {
            while (true){
                try {
                    runnable = mTaskQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (runnable != null){
                    mThreadPool.execute(runnable);
                }
            }
        }
    };

    //重试队列
    private DelayQueue<HttpTask> DelayQueue = new DelayQueue<>();
    //将请求失败的Task加入重试队列
    public void addDelayTask(HttpTask task){
        if (task != null){
            task.setDelaytime(5000);
            DelayQueue.offer(task);
        }
    }

    //重试机制的“叫号”线程
    public Runnable callDelayRun = new Runnable() {
        @Override
        public void run() {
            HttpTask task = null;
            while (true){
                try {
                    task = DelayQueue.take();
                    if (task != null){
                        //判断Task是否超过了重试的次数
                        if (task.getReTryCount() < 3){
                            task.setReTryCount(task.getReTryCount() + 1);
                            mThreadPool.execute(task);
                            Log.e("---重试---","第"+ task.getReTryCount() + "次");
                        } else {
                            //多次尝试失败,放弃
                            Log.e("---重试---","重试多次失败,放弃。");
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
}

ThreadPoolManager 是核心的代码,主要制定网络请求线程的执行规则。通过线程池,以及创建等待队列,重试队列等来实现请求任务按照相关规则执行。
有了上面的准备,一个简单版的OKHttp就差不多了。但是我们在使用OkHttp的时候,整个OkHttp是封装好了的,我们只需要调用它暴露的Api去实现我们的具体需求,那我们这里也封装一个MyOkHttp出来,毕竟Java面向对象才是精髓嘛。

MyOkHttp

public class MyOkHttp<E> {

        public MyOkHttp(String url,String type,E requestData,IHttpRequest httpRequest,CallbackListener callbackListener) {

            HttpTask task = new HttpTask(url,type ,requestData,httpRequest,callbackListener);
            ThreadPoolManager.getInstance().addTask(task);
        }

        static class Builder<T> {
            private  String url;
            private String type;
            public T requestData;
            private IHttpRequest httpRequest;
            private CallbackListener callbackListener;

            public Builder() {
            }

            public Builder setUrl(String url) {
                this.url = url;
                return this;
            }

            public Builder setType(String type) {
                this.type = type;
                return this;
            }

            public Builder setRequestData(T requestData) {
                this.requestData = requestData;
                return this;
            }

            public Builder setHttpRequest(IHttpRequest httpRequest) {
                this.httpRequest = httpRequest;
                return this;
            }

            public Builder setCallbackListener(CallbackListener callbackListener) {
                this.callbackListener = callbackListener;
                return this;
            }

            public MyOkHttp build(){
                return new MyOkHttp(url,type,requestData,httpRequest,callbackListener);
            }
        }
}

代码比较简单,主要通过构造函数为网络请求需要的参数赋值,客户端通过Builder来进行参数的设置,我们看下客户端:

HashMap<String,String> map = new HashMap();
        map.put("location","beijing");
        map.put("key","XXXXXXXX'");

        new MyOkHttp.Builder()
                .setUrl("https://free-api.heweather.net/s6/weather/now")
                .setType("POST")
                .setHttpRequest(httpRequest)
                .setRequestData(map)
                .setCallbackListener(callbackListener)
                .build();

这里采用的是访问和风天气接口为例,通过Map封装了请求数据,并将请求数据,URL等通过MyOkHttp的Builder传递给MyOkHttp里的HttpTask,并将改Task放入线程池开启线程。
到这里,关于简单版OkHttp就介绍完了。实现的功能不多,逻辑也不复杂,就当练手了吧。比起完整的开源OkHttp来说,很简单了,但是也对我们理解OkHttp起到了帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值