12.网络框架volley

来源:互联网 发布:安徽文华软件 编辑:程序博客网 时间:2024/06/07 23:42
转载请标明出处: 

http://blog.csdn.net/yujun411522/article/details/46226087
本文出自:【yujun411522的博客】


     在android开发中使用网络的场景很多,绝大部分都是http方式。就算是采用上面的httpurlconnection和httpclient有时仍然是会稍显麻烦,所以一个新的网络通信框架应运而生:volley。它的使用场景是数据量不大但是访问通信很频繁的网络操作,我们绝大部分的操作也是如此,比如用app浏览新闻等。它对大数据量的操作比如下载文件支持的不好。

     12.1常见用法
     简要介绍一个常见的用法。     
     1 获得一个RequestQueue:RequestQueue mQueue = Volley.newRequestQueue(context);这里是一个请求队列,缓存所有的http请求。使用时只需要将http请求加入这个队列即可。内部实现很高效,后面分析。
     2 创建http请求,以StringRequest为例子:
    StringRequest stringRequest = new StringRequest("http://www.baidu.com",new Response.Listener<String>(){
     //override
     public void onResponse(String response){
          //成功获取http数据
          Log.d("tag",response);
     }

     },new Response.ErrorListener(){
     //override
       public void onErrorResponse(VolleyError error){
          //获取http数据失败
          Log.d("tag",error.getMessage());
     }
});
     如果使用post请求,可以重写父类request的getParam方法,不再赘述。
     请求方式还可以是Json数据,框架已经封装好了,我们来看:JsonRequest是一个抽象类,使用的话使用两个子类:JsonObjectRequest和JsonArrayRequest。以JsonObjectRequest为例子
     JsonObjectRequest request = new JsonObjectRequest(url,new Response.Listener<JSONObject>(){
               public void onResonse(JSONObject response){

               }
     },new Response.ErrorListener(){
               public void onErrorResponse(VolleyError error){
                         //
               }         
});    
     volley 还可以请求网络图片,有ImageRequest,ImageLoader,和NetworkImageView(控件形式),不再赘述。

     3 将http请求加入到requestqueue中:mQueue.add(stringRequest);之后就会自动的进行网络请求。
     总结起来就是:1.构造一个RequestQueue,2.构造一个http请求,3.然后加入到requestqueue中即可。

     12.2自定义request
    Volley框架里面提供请求方式有限,我们也可以定义自己的请求
     先看StringRequest类:继承Request
     重要成员变量:private final Listener<String> mListner;
     重要方法:
     1 protected void deliverResponse(String response){
          //就是调用了定义时的Listener.onResponse()方法。
          //此方法在ExecutorDelivery中会执行
          mListener.onResponse(response);
     }
     2 protected Response<String> parseNetworkResponse(NetworkResponse response){
           //将返回数据解析成以Response<String>类型.
          String parsed;
               try{
                         parsed = new String(response.data,HttpHeaderParser.parseCharset(response.header));
               }
          catch(UnsuppoertedEncodingException e){
                    parsed = new String(response.data);
          }     
               //返回Response<String>类型
                    return Response.success(parsed,HttpHeaderParser.parseCacheHeaders(response));
     }
     这里我们实现一个解析xml文件的自定义request,解析器选择XmlPullParser
     重点实现Response<XmlPullParser> parserNetworkResponse()方法
     protected Response<XmlPullParser> parserNetworkResponse(NetworkResponse response){
          String xmlString;
          try{
               xmlString = new String(response.data,HttpHeaderParser.parseCharset(response.header));//先将原始Response转化成string类型,便于解析
               XmlPullParser parser = XmlParserFactory.newInstance().newPullParser();//创建XmlPullParser解析xml文件
               parser.setInput(new StringReader(xmlString));//设置XmlPullParser的解析内容
               return Response.success(parser,parsed,HttpHeaderParser.parseCacheHeaders(response));//返回一个Response<XmlPullParser> 对象
          }
          catch(){
               }
     }
     然后在Listener中重写onResponse方法通过xmlPullparser解析xml文件:
mXmlRequest = new XmlRequest(
"http://flash.weather.com.cn/wmaps/xml/china.xml",
new Response.Listener<XmlPullParser>() {
public void onResponse(XmlPullParser response) {//解析xml文件
try {
int eventType = response.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
StringBuilder builder = new StringBuilder();
if (eventType == XmlPullParser.START_TAG) {
String tagName = response.getName();
if (tagName.equals("city")) {
builder.append("city<");
int count = response.getAttributeCount();
for (int i = 0; i < count; i++) {
builder.append(" "+response.getAttributeName(i)+ "="+ response.getAttributeValue(i));
}
}
builder.append("\n");
}
Log.d("XmlRequest", builder.toString());
eventType = response.next();
}
} catch (Exception e) {
Log.d("XmlRequest", e.getMessage());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("XmlRequest", error.getMessage());
}
});



        
   12.3 源码分析:
   这是官网给出的操作流程:
   

     volley总体设计图:
     
    简单介绍其中的一些类
     Volley:对外暴露的API,通过newRequestQueue来创建一个requestQueue。
     Request:一个http请求抽象类,可以使用子类StringRequest,JsonRequest等等,还可以自定义request。
     RequestQueue:http请求队列,里面有一个cacheDispatcher用于处理可以缓存的请求,一个networkdispatcher数组,用户处理网络访问的请求。一个responseDelivery用户返回response结果。
     CacheDispatcher:线程,处理可以缓存的请求。启动之后不断从mCacheQueue中取出request。如果没有canceled,没有过期,且命中的话直接解析该网络结果,否则将该request加入到mNetworkQueue中处理。
     NetworkDispatcher:线程,处理进行网络请求。其中之后从mNetworkQueue中取出request,请求http结束之后将结果responsedelivery进行处理。有必要时对其进行缓存。
     ResponseDelivery:一个接口、用于处理请求响应的。
     ExecutorDelivery:ResponseDelivery的实现类。
     HttpStack:处理http请求,使用的基于HttpURLConnection的HurlStack和基于HttpClient的HttpClientStack
     NetWork:一个处理请求的接口,有实现类BasicNetwork
     Cache:缓存请求结果的。使用的DiskBasedCache。如果请求的http在cache中有,直接取出结果使用。

     下面看具体的代码
     Volley.java
    重要的就两个重载方法
     public static RequestQueue newRequestQueue(Context){}      
     public static RequestQueue newRequestQueue(Context,HttpStack){}      
     在构造时如果不提供HttpStack,系统默认按照自己的方式生成,还需要使用netWork将stack包装起来:
      public static RequestQueuenewRequestQueue(Context context, HttpStack stack) {
          //....设置缓存文件目录
        //设置HttpStack API>=9,HulStack,API,9 HttpClientStack
       //在 Froyo(2.2) 之前,HttpURLConnection 有个重大 Bug,调用 close() 函数会影响连接池,导致连接复用失效,所以在 Froyo 之前使用HttpURLConnection 需要关闭        //keepAlive。另外在 Gingerbread(2.3) HttpURLConnection 默认开启了 gzip 压缩,提高了 HTTPS 的性能,Ice Cream Sandwich(4.0) HttpURLConnection 支持了
请求结果缓存。再加上 HttpURLConnection 本身 API 相对简单,所以对 Android 来说,在 2.3 之后建议使用 HttpURLConnection,之前建议使用 AndroidHttpClient
        if (stack == null) {
            if (Build.VERSION. SDK_INT >= 9) {
                stack = new HurlStack();
            } else {       
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);
          //构造函数两个参数,一个是缓存方式,一个是网络请求
        RequestQueue queue = new RequestQueue( newDiskBasedCache(cacheDir),network);
        queue.start();
       return queue;
    }
     
     再看Request.java以及其子类。
     重要的方法:
     //子类实现此方法,解析原始的response返回一个合适的Respose type(类型可以是String,JSONObject,或者自定义的XMLPullParser)
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response):
/**
     * Subclasses must implement this toparse the raw network response andreturn an appropriate response type. This method will be
     * called from a worker thread.  The response will not be delivered if you return null.
     * @param response Response from the network
     * @return The parsed response, or null in the case of an error
     */
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);//子类要实现这个方法,主要工作就是将原始network Response转换成一个合适类型(String,JSON,XML)的Response。

     //子类中实现此方法,将已经解析好的response发送给他们的监听器。
    abstract protected void deliverResponse(T response);  
/**
     * Subclasses must implement this to perform delivery of the parsed response to theirlisteners.  The given response is guaranteed to
     * be non-null; responses that fail to parse are not delivered.
     * @param response The parsed response returned by
     * {@link #parseNetworkResponse(NetworkResponse)}
     */
    abstract protected void deliverResponse(T response);
   
    再看RequestQueue.java
     重要成员变量:
     1 两个基于优先级的阻塞队列:mCacheQueue,mNetworkQueue。mCacheQueue可以cache的队列,mNetworkQueue进行网络请求的队列
      /** The cache triage queue. */
    private final PriorityBlockingQueue<Request> mCacheQueue =  new PriorityBlockingQueue<Request>();
    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request> mNetworkQueue =  new PriorityBlockingQueue<Request>();
     2 mWaitingRequests:正在等待的请求集合,如果有相同url的请求,将生成一个该url的队列,加入到此map中。
     private final Map<String, Queue<Request>> mWaitingRequests =   new HashMap<String, Queue<Request>>();
      3 mCurrentRequests:正在进行处理的request集合,如果在任何一个queue中等待或者被任意一个Dispatcher处理的请求都在此set中。
     private final Set<Request> mCurrentRequests = new HashSet<Request>();
     4 mDispatchers:用于处理mNetworkQueue的线程数组
      private NetworkDispatcher[] mDispatchers;
     5 mCacheDispatcher:用户处理mCacheQueue的线程
     private CacheDispatcher mCacheDispatcher;

     重要方法
     1 start ,之前Volley.newRequestQueue方法中调用了此方法
      /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        stop();  //停止mCacheDispatcher和mDispatchers线程
        // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue , mNetworkQueue , mCache , mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers. length; i++) {
            //mDispatchers. length默认值是4
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue , mNetwork ,mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }
     所以这里一共开始了五个线程

     2 add方法,就是我们在创建请求之后添加到RequestQueue中。
     总体流程如下
     
     上代码:
      public Request add(Request request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue( this);
        synchronized (mCurrentRequests ) {
            //加入当前队列   
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker( "add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            //不允许cache,直接请求http网络,返回
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests .containsKey(cacheKey)) {//当前等待请求中是否有相同的请求
                // There is already a request in flight. Queue up.
                Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog. v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in flight.
                //加入mWaitingRequests 和mCacheQueue中。
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

 3 finish方法
     void finish(Request request) {
        // Remove from the set of requests currently being processed.
        synchronized (mCurrentRequests ) {
            mCurrentRequests.remove(request);
        }

        if (request.shouldCache()) {
            synchronized (mWaitingRequests ) {
                String cacheKey = request.getCacheKey();
                Queue<Request> waitingRequests =mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) {
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }

      再看CacheDispatcher.java
      线程类,重要的就看run方法的流程:
           
      public void run() {
        if (DEBUG ) VolleyLog.v( "start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // Make a blocking call to initialize the cache.
        mCache.initialize();
        while (true ) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request request =mCacheQueue.take();//mCacheQueue取出来一个request
                request.addMarker( "cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) {//请求已经取消过了
                    request.finish("cache-discard-canceled" );
                    continue;//request结束,继续执行while
                }

                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {//cache 没有命中
                    request.addMarker( "cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);//没有命中,加入到mNetworkQueue
                    continue;//request结束,继续执行while
                }

                // If it is completely expired, just send it to the network.
                if (entry.isExpired()) {过期
                    request.addMarker("cache-hit-expired" );
                    request.setCacheEntry( entry);
                    mNetworkQueue.put(request);//命中但是结果过期,加入到mNetworkQueue
                    continue;//request结束,继续执行while
                }
                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker( "cache-hit");
               //如果cache命中,则构造一个NetworkResponse并解析成Response<?>类型
                Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry. data, entry.responseHeaders));//cache命中,
                request.addMarker( "cache-hit-parsed");
        
//直接传输响应结果       
if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    mDelivery.postResponse(request, response);
                } else {
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }
 

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit ) {
                    return;
                }
                continue;
            }
        }
    }

     还有一个线程类NetWorkDispatcher.java
     run方法流程:
     
     
     上代码:
      @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true ) {
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit ) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker( "network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled" );
                    continue;
                }

                // Tag the request (if API >= 14)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH ) {
                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                }

                // Perform the network request.
                NetworkResponse networkResponse = mNetwork.performRequest(request);//进行http请求,并返回一个NetworkResponse 对象
                request.addMarker("network-http-complete" );

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish( "not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete" );

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                if (request.shouldCache() && response.cacheEntry != null ) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);//加入到cache中,下次请求相同http直接可以从cache中取出来
                    request.addMarker("network-cache-written" );
                }

                // Post the response back.
                request.markDelivered();
                mDelivery.postResponse(request, response);//传输Response结果
            } catch (VolleyError volleyError) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog. e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }
    
     再看如何分派request的接口ResponseDelivery.java以及它的实现类ExecutorDelivery.java
   public interface ResponseDelivery {
    /**
     * Parses a response from the network or cache and delivers it.
     */
    public void postResponse(Request<?> request, Response<?> response);

    /**
     * Parses a response from the network or cache and delivers it. The provided
     * Runnable will be executed after delivery.
     */
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /**
     * Posts an error for the given request.
     */
    public void postError(Request<?> request, VolleyError error);
}
     
     实现类ExecutorDelivery.java
      里面定义了一个内部私有类ResponseDeliveryRunnable
       private class ResponseDeliveryRunnable implements Runnable {
            @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest .isCanceled()) {
                mRequest.finish( "canceled-at-delivery" );
                return ;
            }

            // Deliver a normal response or error, depending.
            if (mResponse .isSuccess()) {
               //如果request请求成功,执行该request的deliveryResponse方法,也就是我们在实例化request时的Response.Listener<T>
                mRequest .deliverResponse(mResponse .result );
            } else {
                //如果request请求失败,执行该request的deliveryError方法,也就是我们在实例化request时的new Response.ErrorListener()
                mRequest.deliverError( mResponse .error );
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
        ....
       }
    }
     参考:http://codekk.com/open-source-project-analysis/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90
0 0