zoukankan      html  css  js  c++  java
  • Volley 解析

    Volley

    Volley 原理
    Request流程

    Request处理流程

    RequestQueue类中有三个基本的队列。调用RequestQueue.add(request)增加的请求会先增加mCacheQueue(优先级堵塞队列)由CacheDispatcher( 循环读取队列中的请求,当没有请求处理时线程堵塞)线程处理。假设该请求之前已经被缓存。读取缓存返回给主线程结果。否则将请求增加mNetworkQueue由NetworkDispatcher线程处理。

    因为处理网络请求比較耗时。NetworkDispatcher线程默认开启四个。

    每一个线程都循环从mNetworkQueue读取请求。运行HTTP请求并解析server返回结果,将结果存入缓存,同一时候向通过handler回调到主线程,向用户返回结果。


    假设该请求不是第一次处理。在进入mCacheQueue之前,可能回被增加mWaitingRequests(假设有同样url的请求正在处理)。作用是避免反复的请求多次运行,提高请求速度。当请求处理完之后,会检查mWaitingRequests是否有等待的请求。并所有增加缓存队列。

    mCacheQueue :PriorityBlockingQueue<Request<?

    >>
    mNetworkQueue:PriorityBlockingQueue<Request<?>>
    mWaitingRequests: Map<String,Queue<Request<?>>

    怎样推断缓存是否过期

    Expires首部和Cache-Control:max-age首部都是来告诉缓存文档有没有过期。Volley提供的解析类HttpHeaderParser在解析HTTP返回结果时。会从返回头中获取Expires和Cache-Control:max-age相关信息并存储在缓存数据中。假设没有过期还要推断是否须要刷新。一般假设数据没有过期是不须要刷新的。可是假设返回头中包括stale-while-revalidate会将数据提交给用户后请求刷新数据。在实现Request类的抽象方法parseNetworkResponse时,用户必须调用HttpHeaderParser.parseCacheHeaders解析返回头,否则无法推断缓存是否过期。

    运行网络请求

    假设sdk版本号大于9使用HttpURLConnection运行网络请求,否则使用HttpClient。由BasicNetwork的
    performRequest方法运行网络请求。在performRequest会调用Request类的getHeaders获取请求头。


    假设server返回的响应头中包括Last-Modified或ETag,在运行下次请求时会在请求头中增加If-None-Match或If-Modified-Since。
    假设server上的内容没有改变,会返回304状态。不会返回内容。内容从缓存获取。

    自己定义Request

    这里写图片描写叙述

    Request类是一个泛型类,当中T用户期望带到的数据类型。Volley已经提供了StringRequest、ImageRequest、JosnObjectRequest等能够将server返回的数据解析为String、Image JSONObject。可是这些Request有时候不能满足用户的需求。

    比方假设server返回
    Xml格式的数据,server返回的Josn用户不想解析为JosnObject。

    能够很方便的对Volley的Request进行扩展。主要是继承Request。重写parseNetworkResponse和deliverResponse方法在parseNetworkResponse中将返回二进制数据转化为须要的数据格式。同一时候调用HttpHeaderParser.parseCacheHeaders得到缓存数据。

    在deliverResponse中调用Listenner返回结果。
    假设要更改Http的请求头或者在post方法中须要提供数据须要重写getHeaders和getParams方法。

    Request实现了Comparable,存储Request使用的是优先级队列。能够对Reqest设定优先级。有LOW,NORMAL,HIGH,IMMEDIATE四中优先级。

    默认使用NORMAL。ImageRequest使用的是LOW。

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

    NetworkImageView 载入图片

    NetworkImageView必须配合ImageLoader使用。ImageLoader能够依据url异步载入图片。ImageLoader内部使用ImageRequest载入图片,同一时候对会把同样的请求进行合并降低请求次数。

    多个NetworkImageView相应一个ImageLoader。
    假设有多个NetworkImageView同一时候请求同一个图片。ImageLoader仅仅会运行一次网络请求。


    ImageLoader内部定义了一个接口,该接口的实现由用户提供。该接口用来缓存图片,一般的实现都使用了LRU算法。

    public class BitmapLruCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
    public static final int ONE_MB = 1048576;
    public BitmapLruCache(Context context) {
        super(1048576 * ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 10);
    }
    protected int sizeOf(String key, Bitmap value) {
        return Build.VERSION.SDK_INT >= 12?value.getByteCount():value.getRowBytes() * value.getHeight();
    }
    public Bitmap getBitmap(String url) {
        return (Bitmap)this.get(url);
    }
    public void putBitmap(String url, Bitmap bitmap) {
        this.put(url, bitmap);
    }
    }
    

    加上Volley提供的磁盘缓存。NetworkImageView载入图片使用了两级缓存。

    1. 创建ImageLoader
      mLoader = new ImageLoader(Volley.newRequestQueue(context), new BitmapLruCache(context));

    2. 载入图片
      imageView.setImageUrl(girl.getImg(), mLoader);

    还能够通过imageView.setDefaultImageResId()来设置图片未载入时显示的默认图片。通过imageView.setErrorImageResId()来设置图片载入失败显示的图片。NetworkImageView会在url地址改变之后会及时取消之前的载入。

    Volley 缓存

    Volley默认会缓存请求结果。缓存存放在应用的volley文件夹。每一个请求结果相应一个文件。文件名称由url的hash码得到。默认缓存大小为5M。

    没有提供更改缓存大小的接口。假设更改大小必须更改代码。缓存採用LUR算法。

    内部主要由LinkedHashMap实现。LinkedHashMap内部有一个双向链表和一个HashMap,链表用来保存元素的存储顺序,MAP用来高速存取元素。
    private final Map<String, CacheHeader> mEntries =new LinkedHashMap<String, CacheHeader>(16, .75f, true)
    必须将LinkedHashMap的第三个參数设为true。LinkedHashMap才会按訪问顺序排序(最少被訪问的entry靠前,近期訪问的entry靠后)。
    每次在增加元素时。都会推断缓存大小是否超过5M,假设超过调用pruneIfNeeded将最老的元素移除。缓存文件时,会缓存server返回的http头和body。

    Retrofit

    Retrofit注解

    Retrofit的Annotation包括请求方法相关的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH。和參数相关的@Path、@Field、@Multipart等。

    • 通过注解指定请求url,方法和參数
      @GET("group/{id}/users")
      Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
    • 指定请求头
      @Headers("Cache-Control: max-age=640000")
      @GET("widget/list")
      Call<List<Widget>> widgetList();
    • 指定field
      @FormUrlEncoded
      @POST("user/edit")
      Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last)
    • 指定Http body
      @POST("users/new")
      Call<User> createUser(@Body User user);//必须有相应的Converter

      通用的header能够在okhttp的Interceptors中设定。

    使用步骤

    1.定义接口
    public interface Api {
    @GET("tnfs/api/list")
    Call<Gallery> getList(@Query(ID) int id, @Query(PAGE) int page, @Query(ROWS) int rows);
    }

    2.生成对象

    public static  Api getApi(){    
    Retrofit retrofit=new Retrofit.Builder().baseUrl(BASEURL)   //设置域名 
                .addConverterFactory(GsonConverterFactory.create())//增加Converter
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//与RxJava整合时须要增加
                .build();    
        return  retrofit.create(Api.class);    
    }
    

    Converter用于将请求结果转化成须要的数据。如GsonConverter将JSON请求结果用Gson解析成Java对象。Retrofit提供的Converter

    • Gson: com.squareup.retrofit2:converter-gson
    • Jackson: com.squareup.retrofit2:converter-jackson
    • Moshi: com.squareup.retrofit2:converter-moshi
    • Protobuf: com.squareup.retrofit2:converter-protobuf
    • Wire: com.squareup.retrofit2:converter-wire
    • Simple XML: com.squareup.retrofit2:converter-simplexml
    • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

    3.调用接口
    异步调用

    NetworkApi.getApi().getList(mId,1,8).enqueue(new Callback<Gallery>() {
    @Override
    public void onResponse(Response<Gallery> response, Retrofit retrofit) {
            mAdapter.setItemList(response.body().getList());
    }
     @Override
     public void onFailure(Throwable t) {
         }
    });
    

    同步调用
    Callery gallery= NetworkApi.getApi().getList(mId,1,8).excute();

    Retrofit + RxJava

    方法的返回类型定义为Observable
    Observable<Gallery> getList(@Query(ID) int id, @Query(PAGE) int page, @Query(ROWS) int rows);
    须要增加addCallAdapterFactory(RxJavaCallAdapterFactory.create()才干够识别Observable接口。

    NetworkApi.getApi().getList(mId,1,8).observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Subscriber<Gallery>() {
        @Override
        public void onCompleted() {}
        @Override
        public void onError(Throwable e) {}
        @Override
        public void onNext(Gallery gallery) {}
    });
    

    实现原理

    使用OKHTTP运行网络请求
    使用动态代理为接口生成实现类。

    代理模式介绍

    proxy.png

    主要作用
    1. 方法增强
    你能够在不改动源代码的情况下,增强一些方法,在方法运行前后做不论什么你想做的事情。比方。比方能够增加调用日志。做事务控制等。


    2. 远程调用
    Android中的跨进程通信。


    服务端为实现类。client为代理类。实现同样的接口可是代理类并不须要实现接口定义的功能。

    而是使用binder机制向服务端发起请求,服务端返回结果给client。

    服务端和client还能够跨网络。

    动态代理

    为某个类自己主动产生代理类。该类必须实现一个接口。
    原理
    在运行时自己主动产生代码并进行编译,然后用classloader载入字节码生成Class类,用反射调用Class类的构造函数生成对象。

    public class CachedProviderHandler implements InvocationHandler {
    private Map<String, Object> cached = new HashMap<>();
    private Object target;
    
    public CachedProviderHandler(Object target) {
        this.target = target;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        Type[] types = method.getParameterTypes();
        if (method.getName().matches("get.+") && (types.length == 1) &&
                (types[0] == String.class)) {
            String key = (String) args[0];
            Object value = cached.get(key);
            if (value == null) {
                value = method.invoke(target, args);
                cached.put(key, value);
            }
            return value;
        }
        return method.invoke(target, args);
    }
    }
    
    public abstract class ProviderFactory {
    public static FontProvider getFontProvider() {
        Class<FontProvider> targetClass = FontProvider.class;
        return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
            new Class[] { targetClass },
            new CachedProviderHandler(new FontProviderFromDisk()));
    }
    }
    

    怎样自己主动产生代码

    // 假设需代理接口 Simulator 
    public interface Simulator { 
    short simulate(int arg1, long arg2, String arg3) throws ExceptionA, ExceptionB;
    } 
    
    // 假设代理类为 SimulatorProxy, 其类声明将例如以下
    final public class SimulatorProxy implements Simulator { 
    
    // 调用处理器对象的引用
    protected InvocationHandler handler; 
    
    // 以调用处理器为參数的构造函数
    public SimulatorProxy(InvocationHandler handler){ 
        this.handler = handler; 
    } 
    
    // 实现接口方法 simulate 
    public short simulate(int arg1, long arg2, String arg3) 
        throws ExceptionA, ExceptionB {
    
        // 第一步是获取 simulate 方法的 Method 对象
        java.lang.reflect.Method method = null; 
        try{ 
            method = Simulator.class.getMethod( 
                "simulate", 
                new Class[] {int.class, long.class, String.class} );
        } catch(Exception e) { 
            // 异常处理 1(略)
        } 
    
        // 第二步是调用 handler 的 invoke 方法分派转发方法调用
        Object r = null; 
        try { 
            r = handler.invoke(this, 
                method, 
                // 对于原始类型參数须要进行装箱操作
                new Object[] {new Integer(arg1), new Long(arg2), arg3});
        }catch(Throwable e) { 
            // 异常处理 2(略)
        } 
        // 第三步是返回结果(返回类型是原始类型则须要进行拆箱操作)
        return ((Short)r).shortValue();
    } 
    }
    

    Volley vs Retrofit

    • Retrofit更好用
    • Volley提供了载入图片的支持
    • Volley有缓存,Retrofit能够借助Okhttp提供缓存
    • Volley因为仅仅有四个线程,缓存默认仅仅有5M不适合大文件处理

    參考

    http://gank.io/post/560e15be2dca930e00da1083#toc_26
    给 Android 开发人员的 RxJava 具体解释
    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html
    Java 动态代理机制分析及扩展
    http://square.github.io/retrofit/
    Retrofit官网

  • 相关阅读:
    虚拟机镜像下载
    The method getTextContent() is undefined for the type Node
    sun.misc.BASE64Encoder找不到jar包的解决方法
    eclipce项目中的js报错解决
    eclipce导入项目导入不进去
    myeclipse新安装好后需要的设置
    mysql软件下载
    Git Bash 命令行方式 提交源码到GitHub
    使用plsql developer 创建用户
    鼠标滑过TAB选项卡切换demo 可拓展
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7345405.html
Copyright © 2011-2022 走看看