Android之对Volley网络框架的一些理解

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50888340
本文出自:【顾林海的博客】

##前言
Volley这个网络框架大家并不陌生,它的优点网上一大堆, 适合网络通信频繁操作,并能同时实现多个网络通信、扩展性强、通过接口配置之类的优点。在写这篇文章之前,特意去了解并使用Volley这个网络框架,文章是对Volley的一点点理解,如有写得不到位的地方,欢迎大家指出。

##用法
使用Volley的通用步骤就是通过Volley暴露的newRequestQueue方法,创建的我们的RequestQueue,接着往我们的RequestQueue中添加Request( StringRequest、JsonRequest、ImageRequest,以及你自己定义的Request)。

private void initNetWork() {
        RequestQueue queue = Volley.newRequestQueue(this);
        String url = "";
        StringRequest stringRequest = new StringRequest(Request.Method.GET,
                url, new Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {

                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });
        queue.add(stringRequest);
        queue.start();
 }

步骤很简单,总结就三步:

  1. 创建一个RequestQueue对象。
  2. 创建一个StringRequest对象。
  3. 将StringRequest对象添加到RequestQueue里面。

上面的实例通过get方式请求数据的,post的方式也是很简单,在创建Request的时候,第一个参数改为Request.Methode.POST:

private void initNetWork_1() {
        RequestQueue queue = Volley.newRequestQueue(this);
        String url = "";
        StringRequest stringRequest = new StringRequest(Request.Method.POST,
                url, new Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {

                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                // TODO Auto-generated method stub
                return super.getParams();
            }
        };
        queue.add(stringRequest);
        queue.start();
}

仔细查看上面POST和GET的不同方式的请求代码,可以看出POST的时候多了一个getParams方法,返回的Map类型,getParams获取的数据是用于向服务器提交的参数。
在这里我们看到,如果每次都去定义Map,然后往里面put键值对,这是一件很沮丧的事情,这里给出一个工具类,可以通过解析对象转换成我们需要的Map,代码如下:

package com.example.volleyproject;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

public class MapUtil {

    public static <T> Map<String, String> changeTtoMap(T m) {
        HashMap<String, String> map = new HashMap<String, String>();
        // 获取实体类的所有属性
        Field[] field = m.getClass().getDeclaredFields();
        // 遍历所有属性
        for (int j = 0; j < field.length; j++) {
            // 获取属性的名字
            String name = field[j].getName();

            String value = (String) getFieldValueObj(m, name);
            if (value != null && !value.equals("")) {
                map.put(name, value);
            }
        }
        return map;
    }

    /**
     * 获取对应的属性值
     *
     * @param target
     *            对象
     * @param fname
     *            Filed
     * @return
     */
    public static Object getFieldValueObj(Object target, String fname) { // 获取字段值
        // 如:username 字段,getUsername()
        if (target == null || fname == null || "".equals(fname)) {// 如果类型不匹配,直接退出
            return "";
        }
        Class clazz = target.getClass();
        try { // 先通过getXxx()方法设置类属性值
            String methodname = "get" + Character.toUpperCase(fname.charAt(0))
                    + fname.substring(1);
            Method method = clazz.getDeclaredMethod(methodname); // 获取定义的方法
            if (!Modifier.isPublic(method.getModifiers())) { // 设置非共有方法权限
                method.setAccessible(true);
            }
            return (Object) method.invoke(target); // 执行方法回调
        } catch (Exception me) {// 如果get方法不存在,则直接设置类属性值
            try {
                Field field = clazz.getDeclaredField(fname); // 获取定义的类属性
                if (!Modifier.isPublic(field.getModifiers())) { // 设置非共有类属性权限
                    field.setAccessible(true);
                }
                return (Object) field.get(target); // 获取类属性值
            } catch (Exception fe) {
            }
        }
        return "";
    }
}

最后在getParams 调用这个工具类中的changeTtoMap方法即可:

private void initNetWork_1() {
        RequestQueue queue = Volley.newRequestQueue(this);
        String url = "";
        final RequestParams params = new RequestParams();
        params.id = "1";
        params.name = "bill";
        StringRequest stringRequest = new StringRequest(Request.Method.POST,
                url, new Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {

                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return MapUtil.changeTtoMap(params);
            }
        };
        queue.add(stringRequest);
        queue.start();
}

##Volley的浅谈

所有操作都是基于上面讲的三个步骤,第一步创建一个RequestQueue对象。

RequestQueue queue = Volley.newRequestQueue(this);

请求队列(RequestQueue)不需要出现多个,因此我们可以在Application中进行初始化。

	 /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
 }

方法很简单,创建的我们的请求队列,这里通过传入Context来确定我们缓存目录。

	/**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @param stack An {@link HttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

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

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

在Volley中,http的处理请求,默认 Android2.3 及以上基于 HttpURLConnection的 HurlStack,2.3 以下基于 HttpClient 的 HttpClientStack,通过上面的这段代码可知:

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

这里面的HurlStack和HttpClientStack都实现了HttpStack这个接口,HttpStack是用于http请求,返回请求结果的。

public interface HttpStack {
    /**
     * Performs an HTTP request with the given parameters.
     *
     * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
     * and the Content-Type header is set to request.getPostBodyContentType().</p>
     *
     * @param request the request to perform
     * @param additionalHeaders additional headers to be sent together with
     *         {@link Request#getHeaders()}
     * @return the HTTP response
     */
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError;

}


public class HttpClientStack implements HttpStack {
}

public class HurlStack implements HttpStack{
}

在根据SDK版本确定使用哪个Http请求方式后创建BasicNetwork对象,BasicNetwork实现了Network接口:

/**
 * An interface for performing requests.
 */
public interface Network {
    /**
     * Performs the specified request.
     * @param request Request to process
     * @return A {@link NetworkResponse} with data and caching metadata; will never be null
     * @throws VolleyError on errors
     */
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}

NetWork用于 调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。 这里面的BasicNetwork就是一个具体是实现。

接着执行:

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();

DiskBasedCache是我们缓存的目录:

 public DiskBasedCache(File rootDirectory) {
        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
 }

 public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
        mRootDirectory = rootDirectory;
        mMaxCacheSizeInBytes = maxCacheSizeInBytes;
 }

这里面定义了缓存的地址,默认最大是5MB。继续查看RequestQueue的构造器:

public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
  }

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

在创建RequestQueue实例时进行了一系列的初始化(缓存、HttpStack的处理请求、线程池大小、返回结果的分发)。
NetworkDispatcher是一个线程, 用于调度处理网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。

public class NetworkDispatcher extends Thread {
}

这里面的ResponseDelivery用于返回结果分发接口,目前只有基于ExecutorDelivery的 handler 对应线程内进行分发。

ExecutorDelivery通过内部的handler对应的线程进行返回结果的分发:

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

Executor框架是java 5引入的并发库,把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。

这里面传递进来的是new Handler(Looper.getMainLooper()),传入UI线程的Handler的目的是用于UI更新,比如通过通过Volley进行图片下载时的ImageView更新。

上面也提到过ResponseDelivery用于返回结果分发,目前只基于ExecutorDelivery的handler对应线程进行分发,由此我们找到数据分发的方法:

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

    @Override
    public void postError(Request<?> request, VolleyError error) {
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

在postResponse、postError都调用了Executor.execute(Runnalbe)方法进行并发执行,ResponseDeliveryRunnable实现了Runnable接口并重写run方法:

	public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = 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()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

run方法中的代码比较简单,进行数据的分发,这里面的mResponse就是我们返回的结果,通过mRequest.deliverResponse(mResponse.result);进行数据分发,这里面的Request是一个请求的抽象类,进入StringRequest类中,我们发现StringRequest实现了这个抽象类:

public class StringRequest extends Request<String>{
}

在StringRequest 中我们找到这么一段代码:

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

对比之前我们定义的StringRequest的代码:

StringRequest stringRequest = new StringRequest(Request.Method.GET,
                url, new Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {

                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
	  });

是不是很熟悉,最后结果就是通过ExecutorDelivery中handler对应线程进行分发处理的。
到这里请求队列(RequestQueue)就初始化完毕了。
接着就是定义我们的Request,Volley定义了现成的Request,像 StringRequest、JsonRequest、ImageReques,当然我们可以根据StringRequest一样定义我们自己的Request,只需实现Request这个抽象类:

package com.android.volley.toolbox;

import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;

import java.io.UnsupportedEncodingException;

/**
 * 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));
    }
}

查看StringRequest源码,重写了以下两个方法:

abstract protected void deliverResponse(T response);
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

NetworkResponse存放的是从服务器返回并以字节形式的数据。按照StringRequest我们可以定义GsonRequest:

public class GsonRequest<T> extends Request<T> {

    private final Listener<T> mListener;

    private Gson mGson;

    private Class<T> mClass;

    public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mGson = new Gson();
        mClass = clazz;
        mListener = listener;
    }

    public GsonRequest(String url, Class<T> clazz, Listener<T> listener,
            ErrorListener errorListener) {
        this(Method.GET, url, clazz, listener, errorListener);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(mGson.fromJson(jsonString, mClass),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

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

}

到这里为止我们的队列以及请求响应都已定义好,最后通过请求队列的start方法进行请求:

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


 public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }

CacheDispatcher和NetworkDispatcher中的quit方法:

public void quit() {
        mQuit = true;
        interrupt();
 }

通过start方法对队列进行调度,方法中的先通过stop进行缓存和网络请求的调度终止,通过它们的quit方法中的interrupt方法进行线程中断,CacheDispatcher是 一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
由此start方法中的处理逻辑就一目了然了,先是经过缓存的请求,在未缓存过或是缓存失效时,再进入网络的请求,并判断结果是否要进行缓存。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Volley是一个优秀的安卓开源网络访问工具,这里包含了volley jar包和源码,版本是2015.07.28的1.0.18版本。更多资料可以参见volley的github地址: https://github.com/mcxiaoke/android-volley 1.0.18 2015.07.28 merge upstream, process response bodies only when present tweak getCacheKey(), using method and original url, fix #67 #78 add RedirectError, wrong exception for http 301 and 302, fix #51 make NetworkResponse Serializable, fix #53 create NetworkError using IOException, not using null response 1.0.17 2015.07.13 merge upstream, stop logging slow requests outside of debug logs merge pr #64, fix leaking the last request object 1.0.16 2015.05.18 fix duplicate retry, change DEFAULT_MAX_RETRIES to 0 merge pr, fix NegativeArraySizeException merge upstream, Use a BufferedOutputStream to read and write cache 1.0.15 2015.03.18 add two missing constructors for JsonArrayRequest and JsonObjectRequest add unique identifier for Request 1.0.14 2015.03.17 add more constructors for JsonArrayRequest and JsonObjectRequest update and fix gradle build, using new build.gradle 1.0.13 2015.03.16 merge pr, added constructor to JSONArrayRequest 1.0.12 2015.03.12 merge upstream, fix ImageLoader.getCacheKey() merge upstream, Fix broken DiskBasedCache merge upstream, Modify header parser to handle must-revalidate. 1.0.11 2015.03.03 merge upstream, Add a RequestFinishedListener to RequestQueue. merge upstream, Change the default character encoding for JSON responses to UTF-8 1.0.10 2014.12.30 merge upstream, Use the view size and scale type to restrict NIV requests. merge pr, Add a getImageURL method to NetworkImageView merge pr, Add the options of setting DiskBasedCache sizes in Volley.java 1.0.9 2014.11.26 merge upstream, Fix deprecation warnings (now errors in 0.14.4) w/ gradle. 1.0.8 2014.11.07 merge upstream, Metric for network time and getBackoffMultiplier() 1.0.7 2014.10.13 merge upstream, Add locale to HttpHeaderParserTest.rfc1123Date(long millis) merge upstream, Copy cache header for 304 response 1.0.6 2014.09.10 merge pr, fixed bug where Disk cache misses for redirects merge upstream, Fix crash/OOM in DiskBasedCache merge upstream, allow use of custom ImageRequests in ImageLoader 1.0.5 2014.06.18 merge upstream, Fixes bug in PATCH method. Sets the method before setting the body. update gradle and build tools version, fix build 1.0.4 2014.05.04 merge upstream, Guard against NullPointerException currently occurring in Volley when a Request is given a url whose host is null. 1.0.3 2014.04.01 merge upstream, ImageLoader: setError after null check 1.0.2 2014.02.28 merge upstream, Use the view size to restrict NIV requests. merge upstream, Fix generic type parameter for RequestQueue.add(Request) merge pr, added support for handling 301 and 302 http redirects using standalone deploy gradle build file and script 1.0.1 2014.02.13 add gradle build support, add readme, release using gradle 1.0.0 2014.01.03 create volley mirror at github, release first version

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值