借鉴Volley框架思想,自己手写网络请求框架

2018-03-11  本文已影响0人  吃了药去旅行
   来来来,小板凳给大佬们准备好了,先坐下来看完这篇文章再去刺激战场吧,最后一个敌人留给你,最后一颗子弹留给你!!!!!!

看到这篇的文章的大佬,我想都是用过第三方开源框架了的,这是一种共享文化氛围,当然就也免不得是一种伤害,这样呢?并不是说有什么太大的问题,毕竟大多数时候可以提高我们开发效率,减轻我们的负担。但是,但是,但是,喜欢捣腾的大佬们就会陷入深深的思考中,他们不甘,他们会在酱油瓶打满的时候想,我要开始脱胎换骨了。于是啊,他们开始小心翼翼的写自己的轮子,试着摆脱掉停留在用别人轮子的水平阶段。好了,不跟你们皮了,到这里,开始写轮子前,我们要先弄明白几个事情:1.别人能写框架,我就一定也能写,至少要试着写在自己本工作中使用的框架,2.用第三方框架,出了Bug,md,很难得查。3.当框架大量使用时,会造成我们自己程序中大量的代码冗余,程序更加臃肿!

现在开始进入干货阶段,之前熟悉过Volley框架源码,其本身也有缺陷,比如不支持post大数据,不适合上传文件。不过Volley设计的初衷就是为频繁的、数据量小的网络请求而生,借鉴里面的思想,自己写了个解决工作上需求的小型网络请求框架,实现了多任务依次请求,并且完成线程切换操作。我相信这种需求很多公司中都会有,所以重写了Demo,分享出来,望各位大佬给出你们的宝贵建议。Volley的使用与原理解释,建议各位移步:https://www.jianshu.com/p/15e6209d2e6f

1.创建线程池管理类
面对调用层发出的网络请求,都是需要在子线程中执行,我们可以将其分为多个子线程任务(HttpTask),每个任务一个线程全部添加到我们的任务请求队列中,然后再使用线程池做为管理中心将队列中的请求依次执行,执行不成功的,使用jdk提供的RejectedExecutionHandlerrejectedExecutionHandler将超时未完成的任务线程重新添加到请求队列并再次进入线程池执行请求操作,源码看的多大佬们,可能还会想到,我线程池干嘛不自己写呢?对,确实可以自己写,说不定更能满足需求,也更稳定。这里就先用jdk自带就够用了,那种及其骚又让人窒息的操作,就留给大佬去秀吧!
不是有句话说的好么?“源码看的多,能写又会说”,对,没错,这话是我讲的,但是我不会!!!


package com.example.httpvolley.httpvolley;

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.LinkedBlockingDeque;

import java.util.concurrent.RejectedExecutionHandler;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

/**

* Created by lvsimple23@163.com on 2018/3/9.

*/

public class ThreadPoolManager {

    //添加任务到请求队列

    private LinkedBlockingDequequeue =new LinkedBlockingDeque<>();

    //添加任务就是添加线程

    public void excute(Runnable runnable){

if (runnable !=null) {

try {

queue.put(runnable);

            }catch (InterruptedException e) {

e.printStackTrace();

            }

}

}

    //把任务放到线程池;

    private ThreadPoolExecutorthreadPoolExecutor;

    private ThreadPoolManager(){

threadPoolExecutor =new ThreadPoolExecutor(4,20,15, TimeUnit.SECONDS,new ArrayBlockingQueue(4));

        //开启传动带

        threadPoolExecutor.execute(runnable);

    }

    //拒绝策略,将超时的线程重新放回线程池;

    private RejectedExecutionHandlerrejectedExecutionHandler =new RejectedExecutionHandler() {

@Override

        public void rejectedExecution(Runnable runnable/*runnable 超时的线程*/, ThreadPoolExecutor threadPoolExecutor) {

try {

queue.put(runnable);

            }catch (InterruptedException e) {

e.printStackTrace();

            }

}

};

    private Runnablerunnable =new Runnable() {

@Override

        public void run() {

while (true){

Runnable runnable =null;

                try {

                    //从队列里取线程;

                    runnable =queue.take();

                }catch (InterruptedException e) {

                    e.printStackTrace();

                }

                    if (runnable !=null){

                   threadPoolExecutor.execute(runnable);

                }

}

}

};

    //写成单列模式

    private static ThreadPoolManagerourInstace =new ThreadPoolManager();

    public static ThreadPoolManagergetourInstace(){

    return  ourInstace;

    }

}

2.将请求与响应分别抽为接口;
请求接口中设置了请求Url,请求参数,请求执行,并回调了响应的结果,说明白点就是HttpService接口持有了HttpListener接口的引用

**
 * 封装响应
 */

public interface HttpListener {
    //接收请求接口中的流数据结果
    void onSuccess(InputStream inputStream);
    void onfalure();
}

/**
 * 封装请求
 */

public interface HttpService {
    void setUrl(String url);
    void setRequestData(byte[] requestData);
    void excute();

    //两个接口之间的关系
    void setHttpCallBack(HttpListener httpListener);
}

3.请求任务建立
请求任务实现Runnable接口,HttpTask为一个线程任务,持有两个接口的引用,因为当我们任务以对象方式封装时,我们并不能确定调用者的请求参数是以什么对象形式传进来的,使用泛型处理,能很好的避免这个问题。run()方法中只需要执行发送请求就ok了!到这里,我们的请求任务是形成了,可是,大家知道吗?任务有是有了,可并没有具体的实现类来操作啊?对不对?对不对?看到这里,明白的请点击下方的喜欢按钮,欢迎大家一起交流!那么我们现在就去写实现类!

/**
 * Created by lvsimple23@163.com on 2018/3/9.
 */

public class HttpTask<T> implements Runnable {
    //接口引用
    private HttpListener httpListener;
    private HttpService httpService;

    //请求参数以对象的形式发送,使用泛型
    public<T> HttpTask(T requestInfo,String url,HttpService httpService,HttpListener httpListener){
        this.httpService = httpService;
        this.httpListener = httpListener;
        httpService.setUrl(url);
        //是两个接口相关联
        httpService.setHttpCallBack(httpListener);
        if(requestInfo != null){
            String requestContent = JSON.toJSONString(requestInfo);
            //设置请求参数
            try {
                httpService.setRequestData(requestContent.getBytes("utf-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void run() {
        httpService.excute();
    }
}

4.上面封装接口的步骤中是不是说的很清楚了,在这里,我们设置好参数,持有HttpListenter后 ,在哪里进行网络操作?大家都清楚了吧,我相信大家的基本功比我的扎实,这对大家来说并不是问题。怎么操作的?当然的是Android 原生HttpURLConnection,这个我想大家都用过吧,没用过,赶紧放下手里吃鸡的游戏,去补基本API的使用。这里,当我们把requestData以流的方式从网络上写出去后,得到的返回码为200时,拿回来的流数据通过监听返回到onSuccess中,返回后,我们监听接口这时候要做什么?是不是要在这个时候实现我们的监听接口中的方法,不然返回的流数据起什么效果呢?它并不是一个美丽的摆设,供我们欣赏的,我们需要的是使用它。

/**
 * Created by lvsimple23@163.com on 2018/3/9.
 */

public class JsonHttpService implements HttpService {

    private String url;
    private byte[] requestData;
    //持有另外一个接口的引用
    HttpListener httpListener;

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

    @Override
    public void setRequestData(byte[] requestData) {
        this.requestData = requestData;
    }

    @Override
    public void setHttpCallBack(HttpListener httpListener) {
        this.httpListener = httpListener;
    }

    //真的网络操作实现方法
    @Override
    public void excute() {
        HttpconnectPost();
    }
    HttpURLConnection httpURLConnection = null;
    public void HttpconnectPost(){
        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.setRequestMethod("POST");
            httpURLConnection.setDoInput(true);
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setRequestProperty("Content-Type","application/json; charset=UTF-8");
            httpURLConnection.connect();
            //使用字节流发送数据
            OutputStream outputStream = httpURLConnection.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);
            //判断请求数据是否为空,并写入输出流
            if(requestData != null){
                bos.write(requestData);
            }
            bos.flush();
            outputStream.close();
            bos.close();
            //字符流写入数据
            if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStream inputStream = httpURLConnection.getInputStream();
                httpListener.onSuccess(inputStream);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5.实现监听接口,返回来的流数据,我是不是要将它转换为对象,拿到流数据我们首先把它转为String类型(流转String这种不够骚的操作我就不解释了),对不对?没毛病,那么接下来是不是就是要再继续转换为对象(我这里用的Json,也可以是XML.......),然后我们是不是在这个时候要通过写一个中间接口DataListner来堵住传送出来的对象,通过接口再传送会方便很多,对不对,对不对?写到这里问题就来了,我们在HttpTask中excute方法执行并在JsonHttpService类中实现这个方法后,返回来的数据,一直到最后传递给调用,这是不是都是在子线成操作的?不要担心,Handler,Handler ,Handler,Handler handler = new Handler(Looper.getMainLooper()); 这是无缝实现线程切换啊,简直就是万精油般的存在啊!

/**
 * Created by lvsimple23@163.com on 2018/3/10.
 */

public class JsonHttpListener<M> implements HttpListener {
    //响应结果转为对象
    Class<M> responseClass;
    //定义接口
    DataListener<M> dataListener;
    //线程切换
    Handler handler = new Handler(Looper.getMainLooper());

    public JsonHttpListener(Class<M> responseClass , DataListener dataListener){
        this.responseClass = responseClass;
        this.dataListener = dataListener;
    }
    @Override
    public void onSuccess(InputStream inputStream/*拿到的是网络响应结果*/) {
        //把响应的结果byte数据转为String数据
        String content = getContent(inputStream);
        //把转换好的结果(例如:json字符串)再转换为对象
        final M response = JSON.parseObject(content,responseClass);
        //把结果传送到调用层
        handler.post(new Runnable() {
            @Override
            public void run() {
                if(dataListener != null){
                    dataListener.onSuccess(response);
                }
            }
        });
    }

    //流转字符串
    private String getContent(InputStream inputStream) {
        String content = null;
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder stringBuilder = new StringBuilder();
            String line = null;
            try {
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
            } catch (IOException e) {
                System.out.println("Error" + e.toString());
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.out.println("Error" + e.toString());
                }
            }
            return stringBuilder.toString();
        }catch(Exception exception){
            exception.printStackTrace();
        }
        return content;
    }

    @Override
    public void onfalure() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (dataListener != null){
                    dataListener.onFalure();
                }
            }
        });
    }
}

/**
 * Created by lvsimple23@163.com on 2018/3/10.
 */

public interface DataListener<M> {
    //传回成功的泛型对象
    void onSuccess(M m);

    void onFalure();
}

6.调用类,我这里就只写一个json的请求,其余的大家可以参考这种思想去另写,或者去拓展。这里的请求与返回的结果不确定,最后也是使用了两个泛型。

/**
 * Created by lvsimple23@163.com on 2018/3/10.
 */

public class VolleyBrother {
    public static<T,M> void sendJsonRequest(T requestInfo,String url,Class<M> response,DataListener<M> /*回调响应数据接口*/dataListener){
        HttpService httpService = new JsonHttpService();
        HttpListener httpListener = new JsonHttpListener(response,dataListener);
        HttpTask<T> httpTask = new HttpTask<T>(requestInfo,url,httpService,httpListener);
        ThreadPoolManager.getourInstace().excute(httpTask);
    }
}

7.我们来看看,使用起来是否会方便一些!最后,大家可以自行去掉线程切换部分的代码测试一下,看看是否还能继续跑起来!Demo全部代码,已经提交到github,地址:https://github.com/guojie1992/HttpVolley

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private String url = "http://v.juhe.cn/weather/index?&cityname=长沙&key=自己的key";    /*相关的请求地址,自己去聚合网申请,种类繁多*/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
    }

    public void onclick(View view) {
        VolleyBrother.sendJsonRequest(null, url, ResponsData.class, new DataListener<ResponsData>() {
            @Override
            public void onSuccess(ResponsData responsData) {
                textView.setText("城市:" + responsData.result.today.city + "今天有" + responsData.result.today.temperature + "度");
            }

            @Override
            public void onFalure() {

            }
        });
    }
}
图片.png

欢迎大家阅读,并提出建议!我在刺激战场等你们,等你们成为我的最后一个敌人,等你们与我用最后一颗子弹见分晓!

上一篇下一篇

猜你喜欢

热点阅读