面试记录第十一节——(volley框架)

来源:互联网 发布:烟台南山学院教务网络 编辑:程序博客网 时间:2024/06/08 17:31

一、问:volley框架的历史

答:volley是在2013年Google I/O大会上推出了一个新的网络通信框架Volley。Volley既可以访问网络取得数据,也可以加载图片,并且在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。


二、问:五种volley请求方式分别为?

答:StringRequest、 JsonRequest、 ImageRequest、 ImageLoader、 NetworkImageView


三、问:volley如何使用?

答:分三步

  • 第一步:创建一个RequestQueue对象,他用的是Volley中的静态方法Volley.newRequestQueue(),这里拿到的对象是一个请求对象,它可以缓存所有的http请求,按照一定的算法,并发的发送这些请求。RequestQueue的设计,非常适合并发,因此我们不用每一次请求都创建RequestQueue,也就是说,在一个Activity中不管多少请求,只需要创建一个RequestQueue即可。如下:

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);

  • 第二步:创建一个StringQuest对象。首先查看下StringRequest的源码,有连个构造方法。
  StringRequest sr = new StringRequest("", new Response.Listener<String>() {       @Override       public void onResponse(String s) {       }   }, new Response.ErrorListener() {       @Override       public void onErrorResponse(VolleyError volleyError) {       }   });
  • 第三部:就是把我们创建的sr对象,添加到我们第一步创建的mQueue队列中,然后就可以高并发的发送请求了。

mQueue .add(sr);


四、问:volley源码解析(可以忽略不看,重点看下面总结)?

答:

  • 我们来看创建RequestQueue对象是怎么创建对象的?

1、RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);这里用到的是newRequestQueue,我们点击源码进去查看,如下:

//源码:    public static RequestQueue newRequestQueue(Context context) {        return newRequestQueue(context, (HttpStack)null);    }

不难看出,上面源码:他返回的是newRequestQueue(context, (HttpStack)null),里面有两个参数,我们继续往下看。

//源码:public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        File cacheDir = new File(context.getCacheDir(), "volley");        String userAgent = "volley/0";        try {            String network = context.getPackageName();            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);            userAgent = network + "/" + queue.versionCode;        } catch (NameNotFoundException var6) {            ;        }       //HttpStack 是对我们网络框架HurlStack和HttpClient的封装。        if(stack == null) {            if(VERSION.SDK_INT >= 9) {               //这里是做一个手机版本的判断,如果手机版本大于9,他就会创建一个HurlStack                stack = new HurlStack();            } else {                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }         //根据传入的stack来处理网络请求,        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);         //创建一个RequestQueue 对象,并且调用.start()启动。        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);        queue1.start();        //返回此queue1;        return queue1;    }

讲解:上面的静态newRequestQueue方法,才是真正做操作的的方法,我们创建的RequestQueue 对象,也是从此方法中返回。

  • 问: queue1.start();开启了具体什么东西呢(继续看源码)
    答:点击start()方法,会看到如下源码:
 public void start() {        this.stop();        //源码中会发现,这里会创建一个CacheDispatcher,并且调用CacheDispatcher的start()方法.,开启一个子线程的缓存请求队列。        //而CacheDispatcher它就是一个线程,继承的是Thread(可以点击查看源码)        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);        this.mCacheDispatcher.start();        //这里也创建了一个networkDispatcher 对象,并且调用了networkDispatcher的start()方法。而networkDispatcher 也是一个子线程。        //这里和CacheDispatcher不通的是,networkDispatcher处理的是网络请求。        for(int i = 0; i < this.mDispatchers.length; ++i) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);            this.mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }

讲解:

1、从上面中不难看出,这里面创面了两个子线程,一个是CacheDispatcher对象,NetworkDispatcher对象。一个是缓存、一个是网络。所以我们就很清晰的知道。

2、volley第一次请求网络的时候,,会同时开启两个请求队列,他会判断这个请求在我们本地是否有一份,如果有就直接在缓存中读取(也就是mCacheDispatcher.start();),这样就加快了我们读取的速度。

3、如果没有我们才会走NetworkDispatcher,从网络中获取数据,当从网络中获取数据的时候,我们会把数据保存在CacheDispatcher开启的子线程里面,这样我们第二次再次获取数据的时候,就直接从缓存中读取数据。大大提高获取数据的性能和速度。这就是Volley的缓存机制。

问:他又是怎么添加到缓存中的呢,也就是缓存CacheDispatcher又是怎么运行的。

答:

1、我们刚才说过,CacheDispatcher它内部是一个线程,继承的是Thread。既然是线程,那就来看看它的run()方法。

2、chcheDispatcher会从缓存中取出响应结果,如果entre也就是相应为空的话(entry == null),我们就把这个请求加入到网络请求中。

3、如果不为空的话,在判断这个缓存是否过期(entry.isExpired()),如果过期了,也会从网络请求中获取。

4、最后我们会调用e.parseNetworkResponse来进行数据的解析,然后吧解析后的数据进行回调。

 public void run() {        if(DEBUG) {            VolleyLog.v("start new dispatcher", new Object[0]);        }        Process.setThreadPriority(10);        this.mCache.initialize();        //开启了一个死循环,也就是说,这个缓存的线程始终是运行的。        while(true) {            while(true) {                while(true) {                    while(true) {                        try {                            while(true) {                                final Request e = (Request)this.mCacheQueue.take();                                e.addMarker("cache-queue-take");                                if(e.isCanceled()) {                                    e.finish("cache-discard-canceled");                                } else {                                    //它是一个内部类,同一个缓存队列中来获取缓存数据,来判断。                                    Entry entry = this.mCache.get(e.getCacheKey());                                    //如果这个缓存是一个空的话,请求就会加到mNetworkQueue中。                                    if(entry == null) {                                        e.addMarker("cache-miss");                                        this.mNetworkQueue.put(e);                                    } else if(entry.isExpired()) {//判断这个缓存是否过期                                        e.addMarker("cache-hit-expired");                                        e.setCacheEntry(entry);                                        //如果过期,也会把这个请求加入到网络请求中。                                        this.mNetworkQueue.put(e);                                    } else {//从                                        e.addMarker("cache-hit");                                        Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));                                        e.addMarker("cache-hit-parsed");                                        if(entry.refreshNeeded()) {                                            e.addMarker("cache-hit-refresh-needed");                                            e.setCacheEntry(entry);                                            response.intermediate = true;                                            this.mDelivery.postResponse(e, response, new Runnable() {                                                public void run() {                                                    try {                                                        CacheDispatcher.this.mNetworkQueue.put(e);                                                    } catch (InterruptedException var2) {                                                        ;                                                    }                                                }                                            });                                        } else {                                            this.mDelivery.postResponse(e, response);                                        }                                    }                                }                            }                        } catch (InterruptedException var4) {                            if(this.mQuit) {                                return;                            }                        }                    }                }            }        }    }

问:网络请求Networkdispatcher是如何实现的呢?

答:

1、networkdispatcher也是继承的thread,而它的run方法中也是开启了一个 while(true) 循环,说明这个网络请求也是不停的运行。

2、在 NetworkResponse e = this.mNetwork.performRequest(request);这句中,执行了performRequest方法,而这个方法就是用来发送网络请求的。而Network是一个接口。

3、当NetworkResponse对象收到返回值,通过parseNetworkResponse解析,而解析后的的resposne对象,就会保存在缓存Cache中

public void run() {        Process.setThreadPriority(10);        while(true) {            Request request;            while(true) {                try {                    request = (Request)this.mQueue.take();                    break;                } catch (InterruptedException var4) {                    if(this.mQuit) {                        return;                    }                }            }            try {                request.addMarker("network-queue-take");                if(request.isCanceled()) {                    request.finish("network-discard-cancelled");                } else {                    if(VERSION.SDK_INT >= 14) {                        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());                    }                   // 发送请求,获取返回值                    NetworkResponse e = this.mNetwork.performRequest(request);                    request.addMarker("network-http-complete");                    if(e.notModified && request.hasHadResponseDelivered()) {                        request.finish("not-modified");                    } else {                        //当NetworkResponse对象收到返回值,通过parseNetworkResponse解析                        Response response = request.parseNetworkResponse(e);                        request.addMarker("network-parse-complete");                        if(request.shouldCache() && response.cacheEntry != null) {                            //而解析后的的resposne对象,就会保存在缓存Cache中                            this.mCache.put(request.getCacheKey(), response.cacheEntry);                            request.addMarker("network-cache-written");                        }                        request.markDelivered();                        //当所有数据处理完后,会交给mDelivery.postResponse()方法,进行解析出来的数据做最后处理。                        this.mDelivery.postResponse(request, response);                    }                }            } catch (VolleyError var5) {                this.parseAndDeliverNetworkError(request, var5);            } catch (Exception var6) {                VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});                this.mDelivery.postError(request, new VolleyError(var6));            }        }    }

问:最后一步中我们执行的是mQueue .add(sr)方法作用

答:这个方法很简单,也就是判断请求request添加到那个队列中;

1、点击add()方法,进入源码 public Request add(Request request) {        request.setRequestQueue(this);        Set var2 = this.mCurrentRequests;        synchronized(this.mCurrentRequests) {            this.mCurrentRequests.add(request);        }        request.setSequence(this.getSequenceNumber());        request.addMarker("add-to-queue");        //判断当前的请求是否可以缓存        if(!request.shouldCache()) {            //如果不能缓存的话,就只能把request请求添加到加载网络请求的队列里面,上面我们已经讲解过volley的缓存机制。            this.mNetworkQueue.add(request);            return request;        } else {            Map var7 = this.mWaitingRequests;            synchronized(this.mWaitingRequests) {                String cacheKey = request.getCacheKey();                if(this.mWaitingRequests.containsKey(cacheKey)) {                    Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);                    if(stagedRequests == null) {                        stagedRequests = new LinkedList();                    }                    ((Queue)stagedRequests).add(request);                    this.mWaitingRequests.put(cacheKey, stagedRequests);                    if(VolleyLog.DEBUG) {                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});                    }                } else {                    this.mWaitingRequests.put(cacheKey, (Object)null);                    //如果可以缓存,就会把这个request请求,添加到CacheQueue队列里面。                    //注意,在默认情况下,任何请求都是可以缓存的。                    this.mCacheQueue.add(request);                }                return request;            }        }    }

最后总结:如图01

1、当volley开启网络请求的时候,它内部会开启两个请求队列。一个而是Cachedispatcher,一个是NetworkDispatcher。

2、每一次网络请求的时候,他都会去缓存中判断是否拥有一个这样的请求,如果缓存中存在这个请求,并且没有过期,就可以从缓存中读取这个请求,交给主线程进行网络相应。

3、如果缓存中没有这样一个请求或者请求已经过期,就会从网络请求中获取这个请求,交给networdispatcher来进行异步操作,最后还是会交给主线程进行相应。


五、问:volley图片加载?

答:

  • 1、ImageRequest
  1. 创建一个RequestQueue对象。

  2. 创建一个Request对象。

  3. 将Request对象添加到RequestQueue里面。

  4. 注意:ImageRequest的构造函数接收六个参数,第一个参数就是图片的URL地址,这个没什么需要解释的。第二个参数是图片请求成功的回调,这里我们把返回的Bitmap参数设置到ImageView中。第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。第六个参数是图片请求失败的回调,这里我们当请求失败时在ImageView中显示一张默认图片。

RequestQueue mQueue = Volley.newRequestQueue(context); ImageRequest imageRequest = new ImageRequest(          "http://developer.android.com/images/home/aw_dac.png",          new Response.Listener<Bitmap>() {              @Override              public void onResponse(Bitmap response) {                  imageView.setImageBitmap(response);              }          }, 0, 0, Config.RGB_565, new Response.ErrorListener() {              @Override              public void onErrorResponse(VolleyError error) {                  imageView.setImageResource(R.drawable.default_image);              }          });  
  • 2、ImageLoader用法
1. 创建一个RequestQueue对象。RequestQueue  mQueue = Volley.newRequestQueue(MainActivity.this);2. 创建一个ImageLoader对象。ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {      @Override      public void putBitmap(String url, Bitmap bitmap) {      }      @Override      public Bitmap getBitmap(String url) {          return null;      }  });  3. 获取一个ImageListener对象。mageListener listener = ImageLoader.getImageListener(imageView,          R.drawable.default_image, R.drawable.failed_image);  注意:通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数,第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示的图片,第三个参数指定加载图片失败的情况下显示的图片。4. 调用ImageLoader的get()方法加载网络上的图片第一种:imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener);  第二种:imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",                  listener, 200, 200);  
  • 3、NetworkImageView的用法
NetworkImageView是一个自定义控制,它是继承自ImageView的,具备ImageView控件的所有功能,并且在原生的基础之上加入了加载网络图片的功能。流程:1. 创建一个RequestQueue对象。2. 创建一个ImageLoader对象。3. 在布局文件中添加一个NetworkImageView控件。4. 在代码中获取该控件的实例。5. 设置要加载的图片地址。//布局<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="fill_parent"      android:layout_height="fill_parent"      android:orientation="vertical" >      <Button          android:id="@+id/button"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:text="Send Request" />      <com.android.volley.toolbox.NetworkImageView           android:id="@+id/network_image_view"          android:layout_width="200dp"          android:layout_height="200dp"          android:layout_gravity="center_horizontal"          />  </LinearLayout>//代码NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);  networkImageView.setDefaultImageResId(R.drawable.default_image);  networkImageView.setErrorImageResId(R.drawable.failed_image);  networkImageView.setImageUrl("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",                  imageLoader);  
阅读全文
0 0
原创粉丝点击