面试记录第十一节——(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
创建一个RequestQueue对象。
创建一个Request对象。
将Request对象添加到RequestQueue里面。
注意: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);
- 面试记录第十一节——(volley框架)
- 面试记录第二十一节——(进程保活)
- 学习Ajax框架之dojo:第十一节——Dojo 的General Widget(上)(附源代码)
- 面试记录第十三节——(什么是anr)
- 面试记录第十八节——(冷启动)
- 第十一节 html5学习——应用程序缓存
- Scala入门到精通——第十一节 Trait进阶
- 【Android测试】【第十一节】Uiautomator——简介
- 考研复试系列——第十一节 map的使用
- Scala入门到精通——第十一节 Trait进阶
- HTML 第十一节(CSS选择器)
- 面试记录第十二节——(Butterknife 注入框架)
- 面试记录第十五节——(bitmap释放、lru、三级缓存、图片压缩)
- Spark修炼之道(基础篇)——Linux大数据开发基础:第十一节:Shell编程入门(三)
- Volley(一 )—— 框架简介
- php学习 第十一节
- 第十一节 串口通信
- 第十一节 Trait进阶
- 怎么将导出的sql文件再次导入到数据库?
- 搜狐狐友通过增加知名度增加用户量
- caffe在ubuntu16.04安装遇到的问题及解决方法(CPU ONLY)
- Linux系统(二)
- CTP 客户端 技术相关 简介 一
- 面试记录第十一节——(volley框架)
- 在linux下查看so或可执行程序的依赖库
- 10/11
- cocos js 重启虚拟机 restartVM crash 修复
- PDF如何转换成EPUB格式?PDF转EPUB常用方法分享
- 用java实现快排
- eclipse不支持tomcat8.0版本解决
- yarn中的cgroup调度
- Revit明细表读取、将明细表数据存储到sql server数据库、python读取数据库