Android网络优化6--写一个网络请求模板2--基于Volley

下面介绍一个基于Volley来写的一个网络请求模块,对于Volley的介绍,可以看这篇文章:
Android 网络通信框架Volley简介(Google IO 2013)

好啦,直接进入主题咯。
那么我们的网络模块到底该怎么写比较好看一点呢?
下面是一个可以参考的模板:

Activity

在我们的Activity里面的请求是这样的:

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  
           loadData();
    }

    private void loadData() {       
        StringRequest stringRequest = new StringRequest(
                Constant.YOUR_URL, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {                        
                            Log.i("onResponse","data= "+response);                                               
                    } 
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("str","onErrorResponse "+error);
                    }
                });

        stringRequest.setTag(getClass().getSimpleName());
        MyApplication.getRequestQueue().add(stringRequest);
   }
}

上面的代码看起来还不是很多,还可以看懂。建立一个请求,然后请求的连接就是Constant.YOUR_URL
,这个是你想请求的地址。然后把我们的请求传给我们的队列,由他去执行。

Application

很简单的就这样

public class MyApplication extends Application {

    private static MyApplication myApplication;
    private static RequestQueue requestQueue;

    public static MyApplication getInstance() {
        return myApplication;
    }

    public static RequestQueue getRequestQueue() {
        return requestQueue;
    }

    @Override
    public void onCreate() {
        super.onCreate(); 
        myApplication = this;
        requestQueue = Volley.newRequestQueue(this);            
    }
}

到现在应该都很好理解,那么,该进入主题了!
看我们怎么写StringRequest咯?

StringRequest

不好意思哦,这个是Volley自带的一个类,不用我们写啦,
是不是觉得很容易好用!用Volley框架让你省去很多功夫?是不是觉有点没过瘾?

那我们看下源码:

/**
 * A canned request for retrieving the response body at a given URL as a String.
 */
public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    /**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

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

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

这代码看起来也很简单,继承了Request,然后实现连接它的抽线接口做回调。
因为这个Request类太大,就不贴在这,我们先从头开始看起下吧。

源头

经过我们的查看,我们在Application申请的队列requestQueue = Volley.newRequestQueue(this);
最后在源代码是下面这么句,申请一个队列,然后就start(),开始干活咯。

    RequestQueue queue;
    if (maxDiskCacheBytes <= -1)
    {
        // No maximum size specified
        queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else
    {
        // Disk cache size specified
        queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }

    queue.start();

好了,那么问题来了。这个背后是个什么故事?

RequestQueue

RequestQueue,查看他的构造器,是下面这样,看起来很可能是一个什么线程池的样子,因为有一个threadPoolSize单词在这里,而且认识似乎都和networkDispatcher.start()这个紧密结合在一起,因为他背后是让这个开始去干活。

public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }


public void start() {
        stop();  // 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++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

嗯,是需要看下这个NetworkDispatcher是什么东西了。

NetworkDispatcher

我们看到源代码,他的声明是一个继承Thread的东西,看来似乎验证了我们的什么猜想。
public class NetworkDispatcher extends Thread
既然是继承与Thread,那我们得来看下他的Run里面写了什么东东:

现在,我们再来看下他里面具体的,为便于说明,有删减部分代码。

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {


            Request<?> request; 
                // Take a request from the queue.
                request = mQueue.take();

                // 这句重要,我们的Request最后就是跑到这里被执行了的。
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                //把请求的网络结果进行解析。
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

               //接着就是用  ResponseDelivery mDelivery这个类回调结果 
                request.markDelivered();
                mDelivery.postResponse(request, response);


            } catch (VolleyError volleyError) {             
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) { 
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                mDelivery.postError(request, volleyError);
            }
            //我们的出错回调来自这两个异常。
        }
    }

好啦,基本到这里快结束了,但我们需要看下这个回调的Delivery是怎么回事;
层层返回后,我们看到这Delivery是在构造RequestQueue里面生成的,
new ExecutorDelivery(new Handler(Looper.getMainLooper()))就是这句。
ResponseDelivery 本身只是一个接口,具体的实现是这个ExecutorDelivery

不知道你们注意到他的参数没有,
这个Delivery构造函数是一个Handler
而且这个Handler拿的是运行在UI线程的Looper哦,因此他的回调结果能运行在主线程的哦!下面是代码,真的就是用ExecutorDelivery.

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }
 public RequestQueue(Cache cache, Network network) {
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

OK。知道了具体干活的ExecutorDelivery,我们来看下
mDelivery.postResponse(request, response);这个函数里面到底干了什么

   @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    } 

层层转包,发现是ResponseDeliveryRunnable这么个线程被执行了,那我们来看下他的Run方法里面写了什么内容

public void run() {
            // 如果这请求被取消就finish掉,不传递回调
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

           //好啦,终于看到重要的一句啦,就是这个回调啦
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

             ......
       }

到现在为止,我们建立了StringRequest里面的回调和这个的联系,从而让我们收到结果。

== ,不对啊,好像落了什么

好像还有些没说完的,那么,好像没看到和我们前面说的MainLooper,Handler什么关系啊?
因为还没说mResponsePoster.execute()这个函数,我们看下构造函数。

private final Executor mResponsePoster;
public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }  

到此,一切都清楚了

看到这里,我想到了以前刚开始学安卓的时候,对于网络请求,就是在Activity里面直接开一个线程,
然后用Handler来显示数据。
这个Volley应该就算是高度封装版本吧,哈哈,所以万变不离其中,核心的还是一样的。
就像AnsyncTask底层其实也是封装Handler和Looper一样。
基础方法就那样,没别的。


====

好像还是漏了很重要的内容,

那个,他的网络求情底层到底用什么实现的?httpClient?HttpUrlConnection?还是什么鬼?
前面好像没提及到啊,为了先讲清楚流程,前面跳过了一句话,没对他进行深入,那就是
NetworkResponse networkResponse = mNetwork.performRequest(request);
就是这句啦,通过mNetWork去执行我们的请求。
NetWork本身也是一个接口,里面只有一个performRequest(),
他的接口实现是BasicNetWork类,在Volley.newRequestQueue() 里面初始化

代码如下:

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);

我们来看下这个BasicNetWork实现的接口里面写了什么

@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();
    while (true) {
        HttpResponse httpResponse = null;
        byte[] responseContents = null;
        Map<String, String> responseHeaders = Collections.emptyMap();
        try {
            // Gather headers.
            Map<String, String> headers = new HashMap<String, String>();
            addCacheHeaders(headers, request.getCacheEntry());
            //------------------看这里--------------------
            //------什么,背后居然还有一个HttpStack.performRequest()---
            httpResponse = mHttpStack.performRequest(request, headers);
            
            StatusLine statusLine = httpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();

            responseHeaders = convertHeaders(httpResponse.getAllHeaders());
            // Handle cache validation.
            if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                Entry entry = request.getCacheEntry();
                if (entry == null) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                            responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // A HTTP 304 response does not have all header fields. We
                // have to use the header fields from the cache entry plus
                // the new ones from the response.
                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                entry.responseHeaders.putAll(responseHeaders);
                return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                        entry.responseHeaders, true,
                        SystemClock.elapsedRealtime() - requestStart);
            }

            // Handle moved resources
            if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                String newUrl = responseHeaders.get("Location");
                request.setRedirectUrl(newUrl);
            }

            // Some responses such as 204s do not have content.  We must check.
            if (httpResponse.getEntity() != null) {
              responseContents = entityToBytes(httpResponse.getEntity());
            } else {
              // Add 0 byte response as a way of honestly representing a
              // no-content request.
              responseContents = new byte[0];
            }

            // if the request is slow, log it.
            long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
            logSlowRequests(requestLifetime, request, responseContents, statusLine);

            if (statusCode < 200 || statusCode > 299) {
                throw new IOException();
            }
            return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                    SystemClock.elapsedRealtime() - requestStart);

         }Catch(){
               .....
         }
    }
}

好吧,那么这个HttpStack到底是什么鬼呢?
其实刚才在初始化这个NetWork时候就有写了

         if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

对于版本为9以上的用HttpUrlConnection,9以下的版本用HttpClient来.

HttpClientStack里面的Perform如下

   protected final HttpClient mClient;

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        return mClient.execute(httpRequest);
    }

接着来看下UrlConenction的perform

 @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap<String, String> map = new HashMap<String, String>();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
        if (mUrlRewriter != null) {
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
        HttpURLConnection connection = openConnection(parsedUrl, request);
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));
        }
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        if (responseCode == -1) {
            // -1 is returned by getResponseCode() if the response code could not be retrieved.
            // Signal to the caller that something was wrong with the connection.
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
            response.setEntity(entityFromConnection(connection));
        }
        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        return response;
    }

写了很长了,不过关于这个库的还有一些没有说的,再说这文章就更长了,下次再补充吧
补充的就是他的CacheDispatcher,缓存请求命中的直接返回部分.

好了,到这里,是时候发一张图上来

Volley的架构设计

Volley
使用了线程池来作为基础结构,主要分为主线程,cache线程和network线程。
主线程和cache线程都只有一个,而NetworkDispatcher线程可以有多个,这样能解决比并行问题。
相信通过上面的层层挖掘,你对下面这张图就直接看得更明白了吧

这里写图片描述

看了这么,这里有一篇高逼格的,叫别人家的分析的文章推荐,相当不错Volley 源码解析

// 2015/12/16 更新下面内容

赏析

上面的图很好了解释整体,但并不是很好的能够欣赏到整个设计的良好的地方

这里写图片描述

上面这张图就可以,我们可以看到,很多关节的点是接口
这是关于依赖倒置的一个很好的解释案例图,
另外一点补充的是,图中的各种XxxRequest都是继承于Request<T>的,
这个设计很好,我看到不少项目,对于返回的数据,是没有设置一个通用的ResponeBody类来做的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值