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