一、问: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("https://img-my.youkuaiyun.com/uploads/201404/13/1397393290_5765.jpeg", listener);
第二种:imageLoader.get("https://img-my.youkuaiyun.com/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("https://img-my.youkuaiyun.com/uploads/201404/13/1397393290_5765.jpeg",
imageLoader);