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方法中的处理逻辑就一目了然了,先是经过缓存的请求,在未缓存过或是缓存失效时,再进入网络的请求,并判断结果是否要进行缓存。

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页