Android应用层(网络编程)三(Volley使用与源码解析)

Volley的使用

Volley网络请求队列

  • Volley请求网络都是基于请求队列的,开发者只要把请求放在请求队列中就可以了
  • 请求队列会依次进行请求,一般情况下,一个应用程序如果网络请求没有特别频繁则完全可以只有一个请求队列(对应Application),如果非常多或其他情况,则可以是一个Activity对应一个网络请求队列
Step 1 : 创建队列
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
Step 2 : StringRequest的用法

StringRequest返回的数据是String类型

试着用Get方法请求百度(当然别忘了添加网络访问权限)

<uses-permission android:name="android.permission.INTERNET"/>
//创建请求队列
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("zsj", response);
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        Log.e("zsj", error.getMessage(), error);
    }
});
//将请求添加在请求队列中
mQueue.add(mStringRequest);
Step 3 : JsonRequest的用法
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
        JsonObjectRequest mJsonObjectRequest = new JsonObjectRequest(Request.Method.POST, "http://39.108.135.114:8001/ZRobot/function/getAppResponce",
                null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d("zsj", response.toString());
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("zsj", error.getMessage(), error);
                    }
                }
                );
        mQueue.add(mJsonObjectRequest);

注意,获得了JSON还需要进行解析,可以使用Gson进行解析

Step 4 : ImageRequest加载图片

ImageRequest已经是过时的方法了,和前面两种的用法类似

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
     ImageRequest imageRequest = new ImageRequest(
             "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg",
             new Response.Listener<Bitmap>() {
                 @Override
                 public void onResponse(Bitmap response) {
                     iv_image.setImageBitmap(response);
                 }
             }, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
         @Override
         public void onErrorResponse(VolleyError error) {
             iv_image.setImageResource(R.drawable.ico_default);
         }
     });
     mQueue.add(imageRequest);
Step 5 : 使用ImageLoader加载图片

ImageLoader的内部使用ImageRequest来实现,它的构造器可以传入一个ImageCache缓存形参,实现了图片缓存的功能,同时还可以过滤重复链接,避免重复发送请求。

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
        ImageLoader imageLoader = new ImageLoader(mQueue, new ImageLoader.ImageCache() {
            @Override
            public Bitmap getBitmap(String s) {
                return null;
            }

            @Override
            public void putBitmap(String s, Bitmap bitmap) {

            }
        });
        ImageLoader.ImageListener listener = ImageLoader.getImageListener(iv_image,R.drawable.ico_default, R.drawable.ico_default);
        imageLoader.get("https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg", listener);

要想改变其图片大小,只需要调整相应的View即可

Step 6 : 使用NetWorkImageView加载图片

NetworkImageView是一个自定义控件,继承自ImageView,封装了请求网络加载图片的功能

先在布局中引用:

<com.android.volley.toolbox.NetworkImageView
        android:id="@+id/nv_image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/iv_image"
        android:layout_marginTop="20dp"
     ></com.android.volley.toolbox.NetworkImageView>

再在代码中调用

iv_image = (ImageView) this.findViewById(R.id.iv_image);
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache());
nv_image.setDefaultImageResId(R.drawable.ico_default);
nv_image.setErrorImageResId(R.drawable.ico_default);
nv_image.setImageUrl("https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg",
        imageLoader);

从源码解析Volley

1. Volley结构图

volley结构图

2. 切入RequestQueue

我们都知道,使用Volley之前,首先要创建RequestQueue:

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

这也是volley运作的入口,看看newRequestQueue:

 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 network = context.getPackageName();
            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
            userAgent = network + "/" + queue.versionCode;
        } catch (NameNotFoundException var7) {
            ;
        }
        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
        RequestQueue queue1;
        if(maxDiskCacheBytes <= -1) {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        } else {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
        }
        queue1.start();
        return queue1;
    }

可以看到如果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是缓存调度线程,并调用了start()方法,在循环中调用了NetworkDispatcher的start()方法,NetworkDispatcher是网络调度线程,默认情况下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();

      //之前是否有执行相同的请求且还没有返回结果的,如果有的话将此请求加入mWaitingRequests队列,不再重复请求
               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 {
  //没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据
                   this.mWaitingRequests.put(cacheKey, (Object)null);
                   this.mCacheQueue.add(request);
               }
               return request;
           }
       }
   }

通过判断request.shouldCache(),来判断是否可以缓存,默认是可以缓存的,如果不能缓存,则将请求添加到网络请求队列中,如果能缓存就判断之前是否有执行相同的请求且还没有返回结果的,如果有的话将此请求加入mWaitingRequests队列,不再重复请求;没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据。

从上面可以看出RequestQueue的add()方法并没有做什么请求网络或者对缓存进行操作。当将请求添加到网络请求队列或者缓存队列时,这时在后台的网络调度线程和缓存调度线程轮询各自的请求队列发现有请求任务则开始执行,我们先看看缓存调度线程。

3.CacheDispatcher缓存调度线程

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

看到四个while循环有些晕吧,让我们挑重点的说,首先从缓存队列取出请求,判断是否请求是否被取消了,如果没有则判断该请求是否有缓存的响应,如果有并且没有过期则对缓存响应进行解析并回调给主线程。接下来看看网络调度线程。

4.NetworkDispatcher网络调度线程

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());
                if(networkResponse1 == 304) {
                    Entry requestLifetime2 = request.getCacheEntry();
                    if(requestLifetime2 == null) {
                        return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                    }
                    requestLifetime2.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                }
...省略

从上面可以看到在12行调用的是HttpStack的performRequest()方法请求网络,接下来根据不同的响应状态码来返回不同的NetworkResponse。另外HttpStack也是一个接口,实现它的两个类我们在前面已经提到了就是HurlStack和HttpClientStack。让我们再回到NetworkDispatcher,请求网络后,会将响应结果存在缓存中,如果响应结果成功则调用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();
               }
           }
       }
   }

第17行调用了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()方法。我们用StringRequest请求网络的写法是这样的:

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("wangshu", response);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("wangshu", error.getMessage(), error);
            }
        });
        //将请求添加在请求队列中
        mQueue.add(mStringRequest);

看到第5行整个Volley的大致流程都通了吧,好了关于Volley的源码就讲到这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值