【网络编程】Volley使用与源码分析

这篇具有很好参考价值的文章主要介绍了【网络编程】Volley使用与源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


在2013年Google I/O大会上推出了一个新的网络通信框架Volley。Volley既可以访问网络取得数据,也可以加载图片,并且在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

Volley网络请求队列

Volley请求网络都是基于请求队列的,开发者只要把请求放在请求队列中就可以了。
请求队列会依次进行请求,一般情况下,一个应用程序如果网络请求没有特别频繁则完全可以只有一个请求队列(对应Application),如果非常多或其他情况,则可以是一个Activity对应一个网络请求队列,这就要看具体情况了,首先创建队列:

  RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());

StringRequest的用法

StringRequest返回的数据是String类型的,我们查看下StringRequest的源码:

public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mListener = listener;
    }

    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(0, url, listener, errorListener);
    }
    
    ...
    
}

【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java
试着用GET方法来请求百度:

	//创建请求队列
	RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
	StringRequest mStringRequest = new StringRequest(Request.Method.GET, "http://www.baidu.com",
	        new Response.Listener<String>() {
	            @Override
	            public void onResponse(String response) {
	                Log.i("TAG", response);
	            }
	        }, 
	        new Response.ErrorListener() {
	            @Override
	            public void onErrorResponse(VolleyError error) {
	                Log.e("TAG", error.getMessage(), error);
	            }
	        });
	//将请求添加在请求队列中
	mQueue.add(mStringRequest);
	//要添加网络权限

JsonRequest的用法

  RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
        JsonObjectRequest mJsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
        "http://api.1-blog.com/biz/bizserver/article/list.do",
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d("TAG", response.toString());
                    }
                }, 
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("TAG", error.getMessage(), error);
                    }
                }
                );
        //将请求添加在请求队列中
        mQueue.add(mJsonObjectRequest);

收到的是json,可用Gson去获取对应的对象



源码分析

结构图

【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java
【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java

RequestQueue 请求队列

创建 RequestQueue
 RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());

深入:

 public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }
    
↓↓↓↓↓↓↓↓↓↓↓↓↓

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        return newRequestQueue(context, stack, -1);
    }
    
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
    // 第一步:创建缓存目录
    File cacheDir = new File(context.getCacheDir(), "volley");

    // 第二步:设置默认用户代理
    String userAgent = "volley/0";

    try {
        // 第三步:获取应用程序包名和版本号
        String packageName = context.getPackageName();
        PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
        userAgent = packageName + "/" + packageInfo.versionCode;
    } catch (NameNotFoundException var7) {
        // 忽略异常
    }

    // 第四步:如果未提供 HttpStack,则根据 Android 版本选择使用 HurlStack 或 HttpClientStack
    if (stack == null) {
        if (VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }

    // 第五步:使用 HttpStack 创建 BasicNetwork
    BasicNetwork network = new BasicNetwork((HttpStack) stack);

    // 第六步:使用 DiskBasedCache 创建 RequestQueue
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1) {
        queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    } else {
        queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }

    // 第七步:启动 RequestQueue
    queue.start();

    // 第八步:返回创建的 RequestQueue
    return queue;
}

主要看第四步和第七步:

  • 如果android版本大于等于2.3则调用基于HttpURLConnection的HurlStack(),否则就调用基于HttpClient的HttpClientStack()
  • 创建了RequestQueue,调用了start()方法:
public void start() {
    this.stop();
    this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
    
    // 启动缓存调度器线程
    this.mCacheDispatcher.start();

    // 创建多个网络调度器线程,并将网络队列、网络对象、缓存对象和传递对象作为参数
    for(int i = 0; i < this.mDispatchers.length; ++i) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
        
        // 将网络调度器添加到调度器数组中
        this.mDispatchers[i] = networkDispatcher;
        
        // 启动网络调度器线程
        networkDispatcher.start();
    }
}

启动CacheDispatcher(缓存调度器线程),然后在循环中调用了**NetworkDispatcher(网络调度线程)**的start()方法
默认情况下mDispatchers.length为4,默认开启了4个网络调度线程,也就是说有5个线程在后台运行并等待请求的到来。

然后再使用过程中我们创建各种的Request,并调用RequestQueue的add()方法:

public <T> Request<T> add(Request<T> request) {
    // 设置请求队列
    request.setRequestQueue(this);
    // 将请求添加到当前请求集合中
    Set var2 = this.mCurrentRequests;
    synchronized(this.mCurrentRequests) {
        this.mCurrentRequests.add(request);
    }
    // 设置请求的序列号
    request.setSequence(this.getSequenceNumber());
    // 添加标记到请求中,用于跟踪请求的生命周期
    request.addMarker("add-to-queue");
    
    // 如果不需要缓存,则直接将请求添加到网络请求队列中
    if (!request.shouldCache()) {
        this.mNetworkQueue.add(request);
        return request;
    } else {
        // 获取等待请求集合,并进行同步操作
        Map var8 = this.mWaitingRequests;
        synchronized(this.mWaitingRequests) {
            String cacheKey = request.getCacheKey();

            // 判断是否已经有相同CacheKey的请求正在发送中,若有则将当前请求加入等待队列中,不再重复请求
            if (this.mWaitingRequests.containsKey(cacheKey)) {
                Object stagedRequests = (Queue) this.mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList();
                }

                ((Queue) stagedRequests).add(request);
                this.mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
                }
            } else {
                // 若没有相同CacheKey的请求正在发送中,则将请求加入缓存队列中,并将其添加到等待集合中
                this.mWaitingRequests.put(cacheKey, (Object) null);
                this.mCacheQueue.add(request);
            }

            return request;
        }
    }
}

通过判断request.shouldCache(),来先判断是否可以缓存,默认是可以缓存的
如果不能缓存,则将请求添加到网络请求队列中,如果能缓存就判断之前是否有执行相同的请求且还没有返回结果的,如果有的话将此请求加入mWaitingRequests队列,不再重复请求;没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据。
当将请求添加到网络请求队列或者缓存队列时,这时在后台的网络调度线程和缓存调度线程轮询各自的请求队列发现有请求任务则开始执行


CacheDispatcher缓存调度线程

缓存队列mCacheQueue
CacheDispatcher的

run()

public void run() {
    if(DEBUG) {
        VolleyLog.v("start new dispatcher", new Object[0]);
    }
    //线程优先级设置为最高级别
    Process.setThreadPriority(10);
    this.mCache.initialize();
    while(true) {
        while(true) {
            while(true) {
                while(true) {
                    try {
                    //获取缓存队列中的一个请求
                        final Request e = (Request)this.mCacheQueue.take();
                        e.addMarker("cache-queue-take");
                        //如果请求取消了则将请求停止掉
                        if(e.isCanceled()) {
                            e.finish("cache-discard-canceled");
                        } else {
                        //查看是否有缓存的响应
                            Entry entry = this.mCache.get(e.getCacheKey());
                            //如果缓存响应为空,则将请求加入网络请求队列
                            if(entry == null) {
                                e.addMarker("cache-miss");
                                this.mNetworkQueue.put(e);
                            //判断缓存响应是否过期    
                            } else if(!entry.isExpired()) {
                                e.addMarker("cache-hit");
                                //对数据进行解析并回调给主线程
                                Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                                e.addMarker("cache-hit-parsed");
                                if(!entry.refreshNeeded()) {
                                    this.mDelivery.postResponse(e, response);
                                } else {
                                    e.addMarker("cache-hit-refresh-needed");
                                    e.setCacheEntry(entry);
                                    response.intermediate = true;
                                    this.mDelivery.postResponse(e, response, new Runnable() {
                                        public void run() {
                                            try {
                                                CacheDispatcher.this.mNetworkQueue.put(e);
                                            } catch (InterruptedException var2) {
                                                ;
                                            }

                                        }
                                    });
                                }
                            } else {
                                e.addMarker("cache-hit-expired");
                                e.setCacheEntry(entry);
                                this.mNetworkQueue.put(e);
                            }
                        }
                    } catch (InterruptedException var4) {
                        if(this.mQuit) {
                            return;
                        }
                    }
                }
            }
        }
    }
}

static {
    DEBUG = VolleyLog.DEBUG;
}

【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java


NetworkDispatcher网络调度线程

run():

 public void run() {
        Process.setThreadPriority(10);

        while(true) {
            long startTimeMs;
            Request request;
            while(true) {
                startTimeMs = SystemClock.elapsedRealtime();

                try {
                //从队列中取出请求
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var6) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                if(request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                } else {
                    this.addTrafficStatsTag(request);
                    //请求网络
                    NetworkResponse e = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response volleyError1 = request.parseNetworkResponse(e);
                        request.addMarker("network-parse-complete");
                        if(request.shouldCache() && volleyError1.cacheEntry != null) {                         
                            //将响应结果存入缓存
                            this.mCache.put(request.getCacheKey(), volleyError1.cacheEntry);
                            request.addMarker("network-cache-written");
                        }

                        request.markDelivered();
                        this.mDelivery.postResponse(request, volleyError1);
                    }
                }
            } catch (VolleyError var7) {
                var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.parseAndDeliverNetworkError(request, var7);
            } catch (Exception var8) {
                VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
                VolleyError volleyError = new VolleyError(var8);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.mDelivery.postError(request, volleyError);
            }
        }
    }

网络调度线程也是从队列中取出请求并且判断是否被取消了,如果没取消就去请求网络得到响应并回调给主线程

获取网络响应

请求网络时调用this.mNetwork.performRequest(request)用来返回网络响应,这个mNetwork是一个接口,实现它的类是BasicNetwork,我们来看看BasicNetwork的performRequest()方法:

public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime(); 
    while (true) {
        HttpResponse httpResponse = null;
        Object responseContents = null;
        Map responseHeaders = Collections.emptyMap();
        try {
            HashMap e = new HashMap();
            this.addCacheHeaders(e, request.getCacheEntry()); 
            httpResponse = this.mHttpStack.performRequest(request, e); // 开始发起网络请求
            StatusLine statusCode1 = httpResponse.getStatusLine();
            int networkResponse1 = statusCode1.getStatusCode();
            responseHeaders = convertHeaders(httpResponse.getAllHeaders()); // 将响应头信息转换为Map形式

            if (networkResponse1 == 304) { // 如果服务器返回304,表示请求的资源没有变化,可以使用缓存的响应
                Entry requestLifetime2 = request.getCacheEntry();
                if (requestLifetime2 == null) { // 如果没有缓存条目,则直接返回304响应
                    return new NetworkResponse(304, null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                }
                requestLifetime2.responseHeaders.putAll(responseHeaders); 
                return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
            }
        } catch (IOException var12) {
            // 处理网络请求异常
        }
    }
}

【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java

回调给主线程

再回到网络调度线程,请求网络后,会将响应结果存在缓存中,如果响应结果成功则调用this.mDelivery.postResponse(request, volleyError1)来回调给主线程。
Delivery的postResponse()方法:

 public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
    }
    
    ↓↓↓↓↓↓↓ResponseDeliveryRunnable
    
    private class ResponseDeliveryRunnable implements Runnable {
    private final Request mRequest;
    private final Response mResponse;
    private final Runnable mRunnable;

    public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
        this.mRequest = request; // 存储请求对象
        this.mResponse = response; // 存储响应对象
        this.mRunnable = runnable; // 存储可运行对象
    }

    public void run() {
        if (this.mRequest.isCanceled()) { // 如果请求已被取消
            this.mRequest.finish("canceled-at-delivery"); // 标记请求为取消状态
        } else {
            if (this.mResponse.isSuccess()) { 
                this.mRequest.deliverResponse(this.mResponse.result); //***如果响应成功,传递响应结果给请求对象进行处理
            } else {
                this.mRequest.deliverError(this.mResponse.error); //不成功 传递错误信息给请求对象进行处理
            }

            if (this.mResponse.intermediate) { // 如果响应为中间响应
                this.mRequest.addMarker("intermediate-response"); // 添加标记表示为中间响应
            } else {
                this.mRequest.finish("done"); // 标记请求为完成状态
            }

            if (this.mRunnable != null) { // 如果存在可运行对象
                this.mRunnable.run(); // 执行可运行对象的操作
            }
        }
    }
}

看第25行的this.mRequest.deliverResponse(this.mResponse.result)
这个就是实现Request抽象类必须要实现的方法,例如StringRequest,用来传递响应结果

public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mListener = listener;
    }

    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(0, url, listener, errorListener);
    }

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

     ...
     
}

在deliverResponse()方法中调用了this.mListener.onResponse(response),最终将response回调给了Response.Listener的onResponse()方法。
即我们一般在app里面写的写法:

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest mStringRequest = new StringRequest(Request.Method.GET, "http://www.baidu.com",
        new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                //.....
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
       ...
    }
});
//将请求添加在请求队列中
mQueue.add(mStringRequest);

【网络编程】Volley使用与源码分析,网络编程,php,开发语言,android,androidx,java文章来源地址https://www.toymoban.com/news/detail-830657.html

到了这里,关于【网络编程】Volley使用与源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 树莓派Pico W无线WiFi开发板使用方法及MicroPython网络编程实践

    树莓派Pico W开发板是树莓派基金会于2022年6月底推出的一款无线WiFi开发板,它支持C/C++和MicroPython编程。本文介绍树莓派Pico W无线WiFi开发板的使用方法及MicroPython编程示例,包括树莓派Pico W开发板板载LED使用及控制编程示例,Pico W开发板用作WiFi无线HTTP网络服务器的MicroPython编

    2023年04月12日
    浏览(45)
  • 网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

    网络编程是指网络上的主机,通过不同的进程,以编程的方式实现 网络通信(或称为网络数据传输) 只要满足不同的进程就可以进行通信,所以即便是在同一个主机,只要不同的进程,基于网络传输数据,也属于网络编程 在一次网络传输中: 发送端: 数据的 发送方进程

    2024年02月03日
    浏览(56)
  • 【网络编程】高性能并发服务器源码剖析

      hello !大家好呀! 欢迎大家来到我的网络编程系列之洪水网络攻击,在这篇文章中, 你将会学习到在网络编程中如何搭建一个高性能的并发服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!! 希望这篇文章能

    2024年04月15日
    浏览(51)
  • 【网络编程知识】什么是Socket?概念及原理分析

    先来看一下 百度百科 介绍 套接字(Socket) ,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程

    2024年02月09日
    浏览(37)
  • 【Python】Python 网络编程 ( Socket 套接字简介 | Socket 套接字使用步骤 | Socket 套接字服务端与客户端开发 )

    Socket 套接字 是一种 进程之间的 通信机制 , 通过套接字可以在 不同的进程之间 进行数据交换 ; 在 网络编程 中 , Socket 套接字 主要用于 客户端 与 服务器 之间的 通信 , 大部分 网络相关的应用程序 , 都使用到了 Socket 套接字技术 ; 套接字有两种类型 : 流套接字 : 提供了一个可

    2024年02月15日
    浏览(130)
  • Linux C程序开发,多线程编程、网络编程

    目录 多线程编程 网络编程 Linux C程序开发是指在Linux操作系统下使用C语言进行开发的过程。Linux是一种开源的操作系统,具有稳定性、安全性和灵活性等优点,因此在很多领域都得到了广泛的应用。 多线程编程是指在一个程序中同时运行多个线程,每个线程都有自己的执行路

    2024年02月13日
    浏览(43)
  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

    源IP地址(Source IP Address): 源IP地址是数据包发送方(或数据流出发点)的唯一标识符。它用于在互联网或本地网络中定位发送数据包的设备或主机。源IP地址是数据包的出发点,即数据从这个地址开始传送,向目的IP地址指示的设备发送。 在TCP/IP协议中,源IP地址通常由发

    2024年02月14日
    浏览(83)
  • 【网络编程】实现一个简单多线程版本TCP服务器(附源码)

    accept 函数是在服务器端用于接受客户端连接请求的函数,它在监听套接字上等待客户端的连接,并在有新的连接请求到来时创建一个新的套接字用于与该客户端通信。 下面是 accept 函数的详细介绍以及各个参数的意义: sockfd: 是服务器监听套接字的文件描述符,通常是使用

    2024年02月13日
    浏览(49)
  • QT网络编程TCP/UDP开发流程 制作网络调试助手

    1、QT的网络编程: TCP和UDP TCP编程需要用到俩个类: QTcpServer 和 QTcpSocket QTcpSocket类 提供了一个TCP套接字 QTcpSocket是QAbstractSocket的一个子类,它允许您建立TCP连接和传输数据流 注意:TCP套接字不能在QIODevice::Unbuffered模式下打开。 QTcpServer类 提供一个基于tcp的服务器 2. 这个类可以接

    2023年04月08日
    浏览(44)
  • 🔥🔥Java开发者的Python快速进修指南:网络编程及并发编程

    今天我们将对网络编程和多线程技术进行讲解,这两者的原理大家都已经了解了,因此我们主要关注的是它们的写法区别。虽然这些区别并不是非常明显,但我们之所以将网络编程和多线程一起讲解,是因为在学习Java的socket知识时,我们通常会将它们结合使用,以实现服务器

    2024年02月05日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包