虽然市面上有很多优秀的开源网络框架,例如volley,Okhttp。为啥还要自己来手写一个呢?主要是为了加深一下自己的记忆,还有把自己的技术点串联起来。代码并不难,都挺简单的,有详细的注释。可以优化的地方很多,但是写文的时候状态不是特别好,所以就懒得去改了。再者,大部分情况下我也是会去用OKHttp的!写下来是对自己花费时间的认可。
我相信大部分朋友手写一个简单的网络请求框架都没什么问题。不过温故而知新嘛,我再写一遍的时候,倒是发现了一些以前未曾注意的地方。
这个框架稍微注重了一下多并发的问题。但是没有集合命令模式,所以没有撤销请求这个功能。
第一个类是展示给应用层调用的静态方法。写成静态方法是因为我懒,喜欢的可以自己改成链式调用。这个方法需要传入的参数也写的很清楚啦!
public class SimpleHttp {
/**
* 访问地址URL
* 请求参数DATA
* 回调函数 Listener
* 需要的类型Cls
*/
public static<T,K> void sendRequest(String url, T data, Class<K> cls, IHttpRequestListener listener){
//构建一个请求对象
IHttpRequest iHttpRequest=new HttpRequest();
//构建一个返回回调
IHttpResponseListener iHttpResponseListener=new HttpResponseListener<>(cls,listener);
//构建一个请求线程
HttpTask httpTask=new HttpTask(iHttpRequest,url,data,iHttpResponseListener);
//构建一个线程管理
ThreadManager.getInstance().addTask(httpTask);
}
}
这个是请求的对象接口,至少得满足可扩展性嘛!
public interface IHttpRequest {
//访问的链接URL
void setUrl(String Url);
//请求的参数
void setBytes(byte[] data);
//请求返回的回调
void setIDnHttpResponseListener(IHttpResponseListener iDnHttpResponseListener);
//请求方法
void execute();
}
这个是请求的实现类啦!选的是HttpURLConnection 因为足够轻量,基本能满足需求。请求方法我没有封装到外面,想要使用的可以自行封装一下。
public class HttpRequest implements IHttpRequest {
private String Url;
private byte[] data;
private IHttpResponseListener iDnHttpResponseListener;
private HttpURLConnection httpURLConnection;
@Override
public void setUrl(String Url) {
this.Url = Url;
}
@Override
public void setBytes(byte[] data) {
this.data = data;
}
@Override
public void setIDnHttpResponseListener(IHttpResponseListener iDnHttpResponseListener) {
this.iDnHttpResponseListener=iDnHttpResponseListener;
}
@Override
public void execute() {
URL url = null;
try {
url = new URL(this.Url);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(6000); //连接超时时间
httpURLConnection.setUseCaches(false); //不使用缓存
httpURLConnection.setInstanceFollowRedirects(true); //是成员变量 仅作用域当前函数,设置当前这个对象
httpURLConnection.setReadTimeout(3000); //响应超时的时间
httpURLConnection.setDoInput(true); //设置这个连接对否可以写入数据;
httpURLConnection.setDoOutput(true); //设置这个连接对否可以输出数据;
httpURLConnection.setRequestMethod("POST"); //设置这个请求的方法
httpURLConnection.setRequestProperty("Content-Type","application/json;charset=UTF-8");
httpURLConnection.connect(); //建立连接
//---------------使用字节流发送数据---------------------------
OutputStream out = httpURLConnection.getOutputStream();
//缓冲字节流 包装字节流
BufferedOutputStream bos = new BufferedOutputStream(out);
//把字节流数组写入缓冲区中
bos.write(data);
//刷新缓冲区 发送数据
bos.flush();
out.close();
bos.close();
//如果响应码为200代表请求访问成功
if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
InputStream in = httpURLConnection.getInputStream();
//回调内部的回调接口
iDnHttpResponseListener.onSuccess(in);
}else{
throw new RuntimeException("请求失败");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("请求失败");
}finally {
//关闭httpURLConnection对象
httpURLConnection.disconnect();
}
}
}
这是返回时用的回调接口,实现2个方法即可。
public interface IHttpResponseListener {
//返回成功
void onSuccess(InputStream inputStream);
//反回失败
void onFailed();
}
返回回调接口的实现类,把返回回来的参数转换成自己想要的数据类型。
public class HttpResponseListener<T> implements IHttpResponseListener {
private IHttpRequestListener listener;
private Class<T> cls ;
//切换线程
Handler handler=new Handler(Looper.getMainLooper());
public HttpResponseListener(Class<T>cls , IHttpRequestListener listener){
this.cls=cls;
this.listener=listener;
}
@Override
public void onSuccess(InputStream inputStream) {
String responseStr=getContent(inputStream);
final T t= JSON.parseObject(responseStr,cls);
handler.post(new Runnable() {
@Override
public void run() {
listener.onSuccess(t);
}
});
}
@Override
public void onFailed() {
handler.post(new Runnable() {
@Override
public void run() {
listener.onFailed();
}
});
}
/**
* inputStream转为String类型
* @param inputStream
* @return
*/
private String getContent(InputStream inputStream) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = bufferedReader.readLine()) != null) {
sb.append(line + "/n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString().replace("/n","");
}
}
线程池的管理类,这里为什么要用LinkedBlockingQueue不用ArrayBlockingQueue的原因在于
链表持有2把锁,可以同时进出,但是Array只有一把锁。
在只进或者只出的时候,用数组会有明显优势,但是要满足同时进出,链表会更快。
这里还用了DelayQueue,这是一个延时列表。如果不设置延时时间,会出问题。
虽然拒绝策略大部分时候都没啥用,但是你不写肯定是不行的!
public class ThreadManager {
private static ThreadManager threadManager=new ThreadManager();
public static ThreadManager getInstance(){
return threadManager;
}
//请求队列
private LinkedBlockingQueue<Runnable> requestQueue=new LinkedBlockingQueue<>();
//失败队列
private DelayQueue<Delayed> failedQueue=new DelayQueue<>();
//线程池
private ThreadPoolExecutor threadPoolExecutor;
private ThreadManager(){
threadPoolExecutor = new ThreadPoolExecutor(3, 10, 15,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
addTask(r);
}
});
threadPoolExecutor.execute(runnable);
threadPoolExecutor.execute(failRunnable);
}
//添加线程
public void addTask(Runnable runnable){
if(runnable==null){
return;
}
try {
requestQueue.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加失败线程
public void addFailedTask(HttpTask httpTask){
if(httpTask==null){
return;
}
httpTask.setDelayTIme(3000);
failedQueue.offer(httpTask);
}
//核心线程
private Runnable runnable=new Runnable() {
@Override
public void run() {
while (true){
try {
Runnable runnable=requestQueue.take();
threadPoolExecutor.execute(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//失败核心线程
private Runnable failRunnable=new Runnable() {
@Override
public void run() {
while (true){
try {
HttpTask httpTask= (HttpTask) failedQueue.take();
if(httpTask.getRetryTimes()<3){
httpTask.setRetryTimes(httpTask.getRetryTimes()+1);
threadPoolExecutor.execute(httpTask);
}else {
IHttpResponseListener httpResponseListener=httpTask.getListener();
httpResponseListener.onFailed();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
}
这个就是请求的线程啦!之后需要把请求线程交给线程池去管理。
public class HttpTask<T> implements Runnable, Delayed {
private IHttpRequest iHttpRequest;
private IHttpResponseListener listener;
//延迟时间
private long delayTIme;
//重试次数
private int retryTimes;
public HttpTask(IHttpRequest iHttpRequest,String url,T Data,IHttpResponseListener listener){
this.iHttpRequest=iHttpRequest;
this.listener=listener;
this.iHttpRequest.setUrl(url);
this.iHttpRequest.setIDnHttpResponseListener(listener);
if(Data!=null){
String jsonStr= JSON.toJSONString(Data);
try {
this.iHttpRequest.setBytes(jsonStr.getBytes("UtF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
try {
this.iHttpRequest.execute();
}catch (Exception e){
ThreadManager.getInstance().addFailedTask(this);
}
}
public IHttpResponseListener getListener() {
return listener;
}
public long getDelayTIme() {
return delayTIme;
}
public void setDelayTIme(long delayTIme) {
this.delayTIme = delayTIme+System.currentTimeMillis();
}
public int getRetryTimes() {
return retryTimes;
}
public void setRetryTimes(int retryTimes) {
this.retryTimes = retryTimes;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(getDelayTIme()-System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return 0;
}
}
大部分地方都加了注释,加上代码本身难度也不高。如果还有什么问题也可以直接问我。