关于android okhttp网络请求和重试机制

OkHttp网络请求框架搭建
本文详细介绍了如何使用OkHttp构建网络请求框架,包括线程池管理、请求队列、延迟队列、重试机制等核心组件的设计与实现。

最近在网易云公开课上学习了关于okhttp的网络请求底层,做一下记录。

 

这是关于网络请求的逻辑图片。

网络访问框架是通过请求队列和线程池来进行网络访问框架,其中线程池分为任务线程和核心线程,核心线程的作用是不间断的去请求队列里获取请求,将请求和线程关联起来。而任务线程则是用来处理请求。

(一)首先我们来线程池管理类来处理请求和线程池的关系。

也就是图中需要处理的模块

 

/**
 * 线程和请求管理者
 */
public class ThreadPoolManager {

    private static ThreadPoolManager threadPoolManager=new ThreadPoolManager();



    public static ThreadPoolManager getInstance(){
        return threadPoolManager;
    }

    //请求队列
    private LinkedBlockingDeque<Runnable> mQueue=new LinkedBlockingDeque<>();


    //将请求任务添加到请求队列里
    public void addTask(Runnable runnable){
        if (runnable!=null){
            try {
                mQueue.put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //创建延迟队列
    private DelayQueue<HttpTask> mDelayQueue=new DelayQueue();

    //将失败的请求添加到延迟队列

    public void addDelayTask(HttpTask httpTask){
        if (httpTask!=null){
            httpTask.setDelayTime(3000);
            mDelayQueue.offer(httpTask);
        }
    }



    //创建线程池
    private ThreadPoolExecutor mThreadPoolExecutor;

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

        mThreadPoolExecutor.execute(coreThread);
        mThreadPoolExecutor.execute(delayThread);

    }

    //核心线程
    public Runnable coreThread=new Runnable() {
        Runnable runnable=null;
        @Override
        public void run() {
            while(true){
                try {
                    //去队列里拿线程
                    runnable=mQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mThreadPoolExecutor.execute(runnable);
            }
        }
    };


    //延迟线程
    public Runnable delayThread=new Runnable() {
        HttpTask httpTask=null;
        @Override
        public void run() {
            while (true){
                try {
                    httpTask=mDelayQueue.take();
                    if (httpTask.getRetryCount()<3){
                        mThreadPoolExecutor.execute(httpTask);
                        httpTask.setRetryCount(httpTask.getRetryCount()+1);
                        Log.e("=========>",httpTask.getRetryCount()+" "+System.currentTimeMillis());
                    }else {
                        Log.e("=========>","重试超过三次");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

}

其中比较难理解的点是关于LinkedBlockingQueue,这个地方对于这个不太常用,感觉以后可以去认真学习一下,现在对于他的理解就是一个阻塞队列。

另外一点就是关于ThreadPoolExecutor线程池,线程池的构造方法

RejectedExecution因为某些原因被拒绝,我们就重新将线程添加到请求队列里。

还有一个关键点就是核心线程中通过while(true)的死循环来不停的监听队列里的请求。

另外关于重试的后面再讲。

现在已经完成了请求的核心。

 

(二)然后我们继续来写框架,基于这几天的学习,如果想架构框架之类的东西,我们就要学会面向接口的架构思想

所以下面我们基于接口来进行架构

 

/**
 * 请求接口约束规范
 */
public interface IHTTPRequest {

    //设置url
    void setUrl(String url);

    //设置参数
    void setData(byte[] data);

    //返回监听
    void setListener(CallbackListener listener);

    //访问网络
    void execute();

}
/**
 * 返回监听
 */
public interface CallbackListener {

    void onSuccess(InputStream inputStream);

    void onFail();

}

在写完两个Base类之后,我们根据base来进行二次约束


/**
 * 在IHttpRequest的基础上封装
 */
public class JsonHttpRequest implements IHTTPRequest {


    private String url;
    private byte[] data;
    private CallbackListener listener;
    private HttpURLConnection urlConnection;



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

    @Override
    public void setData(byte[] data) {
        this.data=data;
    }

    @Override
    public void setListener(CallbackListener listener) {
        this.listener=listener;
    }

    @Override
    public void execute() {

        URL url=null;

        try {
            url=new URL(this.url);
            urlConnection= (HttpURLConnection) url.openConnection();
            urlConnection.setConnectTimeout(6000);
            urlConnection.setUseCaches(false);
            urlConnection.setInstanceFollowRedirects(true);
            urlConnection.setReadTimeout(3000);
            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("Content-Type","application/json;charset=UTF-8");
            urlConnection.connect();


            OutputStream outputStream=urlConnection.getOutputStream();
            BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
            bufferedOutputStream.write(data);
            bufferedOutputStream.flush();
            outputStream.close();

            if (urlConnection.getResponseCode()==HttpURLConnection.HTTP_OK){
                InputStream inputStream=urlConnection.getInputStream();
                listener.onSuccess(inputStream);
            }else {
                throw new RuntimeException("请求失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败");
        }finally {
            urlConnection.disconnect();
        }


    }
}
/**
 * 约束Callback
 */
public class JsonCallbackListener<T> implements CallbackListener {


    private Class<T> responseClass;//结果bean
    private Handler handler=new Handler(Looper.getMainLooper());
    private JsonDataTrasformListener listener;

    public JsonCallbackListener(Class<T> responseClass,JsonDataTrasformListener listener) {
        this.responseClass = responseClass;
        this.listener=listener;
    }


    @Override
    public void onSuccess(InputStream inputStream) {
        //将InputString装换成对象
        String respose = getContent(inputStream);
        final T clazz = new Gson().fromJson(respose, responseClass);
        handler.post(new Runnable() {//主线程
            @Override
            public void run() {
                listener.onSuccess(clazz);//传递对象
            }
        });

    }

    @Override
    public void onFail() {

    }

/**
 * 将inputsteanm转化为String
 * @param inputStream
 * @return
 */
 private String getContent(InputStream inputStream) {
        String content = null;
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder stringBuilder = new StringBuilder();

            String line = null;

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


        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;

    }


}
/**
 * 传递参数
 */
public interface JsonDataTrasformListener<T> {

    void onTrasformSuccess(T z);

}

(三)创建HttpTask

 

/**
 * 封装请求,也就是放入ThreadPoolManager里的请求队列的runable
 */
public class HttpTask<T> implements Runnable, Delayed {

    private IHTTPRequest ihttpRequest;

    public HttpTask(String url, T requestData, IHTTPRequest ihttpRequest, CallbackListener callbackListener) {
        this.ihttpRequest = ihttpRequest;
        ihttpRequest.setUrl(url);
        ihttpRequest.setListener(callbackListener);
        //将request类转化为string 后面再转为byte
        String content = new Gson().toJson(requestData);
        try {
            ihttpRequest.setData(content.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }


    @Override
    public void run() {
        try {
            ihttpRequest.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(this.delayTime-System.currentTimeMillis(),TimeUnit.MILLISECONDS);
    }

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

关于delayed后面再讲

(四)创建工具类

/**
 * 网络访问工具类
 */
public final class TXHttp {

    public static <T, M> void sendJsonRequest(String url, T requestData, Class<M> response, JsonDataTrasformListener listener) {

        //请求
        IHTTPRequest ihttpRequest = new JsonHttpRequest();
        //回调监听
        CallbackListener callbackListener = new JsonCallbackListener<>(response, listener);
        //HttpTask的请求 runnable
        HttpTask httpTask = new HttpTask(url, requestData, ihttpRequest, callbackListener);
        //线程池添加请求
        ThreadPoolManager.getInstance().addTask(httpTask);
    }

}

(五)调用工具类

TXHttp.sendJsonRequest(url, null, BaseResponse.class, new JsonDataTrasformListener() {
    @Override
    public void onTrasformSuccess(Object z) {

    }
});

以上就完成了网络请求框架的搭建。

(六)重试机制

关于重试机制,其实跟正常的请求是一样的,创建一个重试的请求队列,然后将重试的队列同样添加到线程池中,设置一个重试的次数和重试时间。

需要注意的点是HttpTask需要实现delay

在ThreadPoolManager里创建延迟队列和延迟线程

 

当网络请求出错或者抛出异常时,我们就可以进行重试,所以我们在HTTPTask的run方法里try catch一下。

 

学过之后目前有问题的其实还是请求队列里的

LinkedBlockingDeque
DelayQueue

这两个队列还不是很清晰,以后可以研究一下,现在还处于模仿阶段。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值