带你玩转OkHttp网络框架

Android 高级工程师进阶 ~ 网易云课堂 学习笔记

【课程主题】使用线程池快速打造简易支持队列

【课程大纲】

1、网络访问框架需求基本分析

2、队列与线程池的使用

3、请求重试机制

okhttp 核心原理解析与实现

线程池,队列的使用

网络访问框架关心的问题
1,能够并发接受多个请求,并返回用户需要的数据
2,重试机制

网络访问框架实现步骤
1,创建线程池管理类(队列,线程池)
2,封装请求
3,封装响应
4,封装请求任务
5,封装使用工具

封装网络访问任务

1,网络访问请求
2,服务器返回的响应
3,具体访问实现

重试机制:
当网络访问失败后,应该给予一定的时间进行“试错”,只有错误的次数,以及时间够“合理”时再撤销。

通过延迟队列来确定延迟时间,并通过响应状态来判断是否需要延迟。

直接撸代码

1, 创建线程池管理类(队列,线程池)

public class ThreadPoolManger {
    private static ThreadPoolManger threadPoolManger = new ThreadPoolManger();

    public static ThreadPoolManger getInstance() {
        return threadPoolManger;
    }

    private ThreadPoolManger() {
        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 runn = null;

        @Override
        public void run() {
            while (true) {
                try {
                    runn = mQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mThreadPoolExecutor.execute(runn);
            }
        }
    };


    //创建队列,将网络请求任务添加到队列中 (队列 特性  先进先出)
    private LinkedBlockingDeque<Runnable> mQueue = new LinkedBlockingDeque<>();

    public void addTask(Runnable runnable) {
        if (runnable != null) {
            try {
                mQueue.put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //创建线程池
    public ThreadPoolExecutor mThreadPoolExecutor;

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

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

    //创建延迟线程
    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("===重试机制===", "重试超过 3次,直接放弃");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
}

2,封装请求

定义请求接口

public interface IHttpRequest {


    void setUrl(String url);

    void setData(byte[] data);

    void setListener(CallBackListener  callBackListener);

    void execute();
}

实现 请求接口

public class JsonHttpRequest implements IHttpRequest {

    private String url;
    private byte[] data;
    private CallBackListener listener;

    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 callBackListener) {
        this.listener = callBackListener;
    }

    @Override
    public void execute() {
        //访问网络的具体操作

        URL url = null;

        try {
            url = new URL(this.url);
            urlConnection = (HttpURLConnection) url.openConnection();//打开http连接
            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();//连接,从上述至此的配置必须要在connect 之前完成,实际上它只是建立了一个与服务器的TCP连接。

            //--------------------------- 使用字节流发生数据---------------------------------------
            OutputStream outputStream = urlConnection.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);// 缓冲字节流包装字节流
            bos.write(data);//把这个字节数组的数据写入缓冲区中
            bos.flush();// 刷新缓冲区,发送数据
            outputStream.close();
            bos.close();

            //----------------------字符流写入数据 -----------------------------
            if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {//得到服务器的返回码是否连接成功
                InputStream in = urlConnection.getInputStream();
                listener.onSucess(in);
            } else {
                throw new RuntimeException("请求失败");
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败");
        } finally {
            urlConnection.disconnect();
        }
    }
}

3,封装响应

回调

public interface IJsonDataListener<T> {
    void onSuccess(T t);

    void onFailure();
}
public interface CallBackListener {
    void onSucess(InputStream inputStream);

    void onFailure();
}

实现 回调

public class JsonCallBackListener<T> implements CallBackListener {

    private Class<T> responseClass;

    private Handler mHandler = new Handler(Looper.getMainLooper());
    private IJsonDataListener jsonDataListener;

    public JsonCallBackListener(Class<T> responseClass, IJsonDataListener listener) {
        this.responseClass = responseClass;
        this.jsonDataListener = listener;
    }

    @Override
    public void onSucess(InputStream inputStream) {
        // 将流转换成我们对应的 T 类型
        String response = getContent(inputStream);
        final T clazz = JSON.parseObject(response, responseClass);
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                jsonDataListener.onSuccess(clazz);
            }
        });
    }

    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;
    }

    @Override
    public void onFailure() {

    }
}

private String url = “http://v.juhe.cn/historyWeather/citys?province_id=2&key=bb52107206585ab074f5e59a8c73875b”;

定义 url 的bean 文件

public class ResponseClass {

    /**
     * resultcode : 112
     * reason : 当前可请求的次数不足
     * result : null
     * error_code : 10012
     */

    public String resultcode;
    public String reason;
    public Object result;
    public int error_code;

    public String getResultcode() {
        return resultcode;
    }

    public void setResultcode(String resultcode) {
        this.resultcode = resultcode;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    public int getError_code() {
        return error_code;
    }

    public void setError_code(int error_code) {
        this.error_code = error_code;
    }

    @Override
    public String toString() {
        return "ResponseClass{" +
                "resultcode='" + resultcode + '\'' +
                ", reason='" + reason + '\'' +
                ", result=" + result +
                ", error_code=" + error_code +
                '}';
    }
}

4,封装请求任务

public class HttpTask<T> implements Runnable, Delayed {

    private IHttpRequest mIhttpRequest;

    public HttpTask(String url, T requestData, IHttpRequest httpRequest, CallBackListener listener) {
        mIhttpRequest = httpRequest;
        httpRequest.setUrl(url);
        httpRequest.setListener(listener);
        String content = JSON.toJSONString(requestData);
        try {
            httpRequest.setData(content.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            mIhttpRequest.execute();
        } catch (Exception e) {
            e.printStackTrace();
            ThreadPoolManger.getInstance().addDelayTask(this);
        }
    }

    private long delayTime;
    private int retryCount;

    public long getDelayTime() {
        return delayTime;
    }

    public void setDelayTime(long delayTime) {
        //设置 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;
    }
}

5,封装使用工具

public class NeNetUtil {

    public static <T, M> void sendJsonRequest(String url, T requestData, Class<M> respone, IJsonDataListener listener) {
        IHttpRequest httpRequest = new JsonHttpRequest();
        CallBackListener callBackListener = new JsonCallBackListener<>(respone, listener);
        HttpTask httpTask = new HttpTask(url, requestData, httpRequest, callBackListener);
        ThreadPoolManger.getInstance().addTask(httpTask);
    }
}

界面类

public class MyMainActivity extends AppCompatActivity {

    private String url = "http://v.juhe.cn/historyWeather/citys?province_id=2&key=bb52107206585ab074f5e59a8c73875b";
//    private String url = "http://xxxx";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity_main);

        sendRequest();
    }

    private void sendRequest() {
        NeNetUtil.sendJsonRequest(url, null, ResponseClass.class, new IJsonDataListener<ResponseClass>() {
            @Override
            public void onSuccess(ResponseClass o) {
                Log.e("===>", "onSuccess: " + o.toString());
            }

            @Override
            public void onFailure() {

            }
        });
    }


}

运行结果

private String url = “http://v.juhe.cn/historyWeather/citys?province_id=2&key=bb52107206585ab074f5e59a8c73875b”;

05-29 17:46:51.888 9267-9267/com.github.yoyozhangh.studydemo E/===>: onSuccess: ResponseClass{resultcode='112', reason='当前可请求的次数不足', result=null, error_code=10012}

当网络访问失败后,应该给予一定的时间进行“试错”,只有错误的次数,以及时间够“合理”时再撤销。
private String url = “http://xxxx”;

05-29 15:51:37.539 8509-8586/com.github.yoyozhangh.studydemo E/===重试机制===: 1  1559116297537
05-29 15:51:40.543 8509-8586/com.github.yoyozhangh.studydemo E/===重试机制===: 2  1559116300542
05-29 15:51:43.558 8509-8586/com.github.yoyozhangh.studydemo E/===重试机制===: 3  1559116303558
05-29 15:51:46.569 8509-8586/com.github.yoyozhangh.studydemo E/===重试机制===: 重试超过 3次,直接放弃

源码 地址: https://github.com/yoyo0316/StudyDemo/commit/280a713e1dff782248660cf0ae6a7ed0588bdb83

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值