Java基础知识强化之网络编程笔记23:Android网络通信之 Volley(Google开源网络通信库)
联合网上资料学习:http://www.open-open.com/lib/view/open1451223702339.html
一、Volley的介绍
1. Volley简介
在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,今年的Google I/O 2013上,Volley发布了。Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
这是Volley名称的由来: a burst or emission of many things or a large amount at once
在Google IO的演讲上,其配图是一幅发射火弓箭的图,有点类似流星。见下图:
其实,从这幅图,我们也可以看出来,Volley特别适合数据量不大但是通信频繁的场景。
2. Volley的引入背景:
在以前,我们可能面临如下很多麻烦的问题。
(1)比如以前从网上下载图片的步骤可能是这样的流程:
- 在ListAdapter#getView()里开始图像的读取。
- 通过AsyncTask等机制使用HttpURLConnection从服务器去的图片资源
- 在AsyncTask#onPostExecute()里设置相应ImageView的属性。
而在Volley下,只需要一个函数即可,详细见后面的例子。
(2)再比如,屏幕旋转的时候,有时候会导致再次从网络取得数据。为了避免这种不必要的网络访问,我们可能需要自己写很多针对各种情况的处理,比如cache什么的。
(3)再有,比如ListView的时候,我们滚动过快,可能导致有些网络请求返回的时候,早已经滚过了当时的位置,根本没必要显示在list里了,虽然我们可以通过ViewHolder来保持url等来实现防止两次取得,但是那些已经没有必须要的数据,还是会浪费系统的各种资源。
等等……
3. Volley提供的功能:
简单来说,它提供了如下的便利功能(优势):
• JSON,图像等的异步下载;
• 网络请求的排序(scheduling)
• 网络请求的优先级处理
• 缓存
• 多级别取消请求
• 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)
二、Volley的使用:
1. 引入Volley非常简单,首先,从git库先克隆一个下来:
1 git clone https://android.googlesource.com/platform/frameworks/volley
然后编译为jar包,再在自己的工程里import进来。
注意,这个库要求最低SDK版本为Froyo,即至少要设置android:minSdkVersion为 8 以上。
也可以从Github代码库中获取:https://github.com/mcxiaoke/android-volley
我自己也存放了Volley的jar包:http://download.csdn.net/detail/hebao5201314/9581002
2. Volley实现JSON字符串GET请求:
(1)构建一个RequestQueue
1 RequestQueue requestQueue=Volley.newRequestQueue(this);//这里的this指的是Context
(2)创建一个Request
(3)将Request 添加到RequestQueue
注:
在构建JsonObjectRequest
对象时,需要四个参数:
第一个参数代表http方法;
第二个参数代表访问Url;
第三个和第四个参数分别是 响应监听 和 响应错误监听,分别需要覆写onResponse()和onErrorResponse()方法;RequestQueue
将会执行请求,并将响应回调onResponse()
方法,所以需要在onResponse()方法中实现自己的业务逻辑。
代码如下:
1 //获取Json字符串 2 public void getJSONVolley() { 3 RequestQueue requestQueue = Volley.newRequestQueue(this); 4 String JSONDateUrl = "http://www.wwtliu.com/jsondata.html"; 5 /** 6 * JsonObjectRequest(int method, String url, JSONObject jsonRequest, 7 * Listener<JSONObject> listener, ErrorListener errorListener) 8 */ 9 JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, JSONDateUrl, null, 10 new Response.Listener<JSONObject>() { 11 12 public void onResponse(JSONObject response) { 13 System.out.println("response="+response); 14 15 } 16 17 }, new Response.ErrorListener() { 18 public void onErrorResponse(com.android.volley.VolleyError arg0) { 19 System.out.println("对不起,有问题"); 20 }; 21 } 22 ); 23 24 requestQueue.add(jsonObjectRequest); 25 }
效果如下:
3. Volley实现异步加载图片:
(1)首先开启Apache服务器,在Apache服务器中存放一个图片text.jpg,如下:
使用360浏览器访问这个text.jpg文件,结果如下:
说明这里的Apache服务器是开启成功,可以正常访问。
(2)新建一个Android工程,命名为"MyVolley",如下:
(3)我们先看看布局文件activity_main.xml,如下:
这个布局是自适应显示图片的。
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.himi.myvolley.MainActivity" > 10 11 <ImageView 12 android:id="@+id/iv" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_alignParentLeft="true" 16 android:layout_alignParentTop="true" 17 android:layout_marginLeft="30dp" 18 android:layout_marginTop="16dp" /> 19 20 </RelativeLayout>
(4)MainActivity.java:
1 package com.himi.myvolley; 2 3 import org.json.JSONObject; 4 5 import android.app.Activity; 6 import android.graphics.Bitmap; 7 import android.os.Bundle; 8 import android.util.LruCache; 9 import android.widget.ImageView; 10 11 import com.android.volley.Request; 12 import com.android.volley.RequestQueue; 13 import com.android.volley.Response; 14 import com.android.volley.toolbox.ImageLoader; 15 import com.android.volley.toolbox.ImageLoader.ImageCache; 16 import com.android.volley.toolbox.ImageLoader.ImageListener; 17 import com.android.volley.toolbox.JsonObjectRequest; 18 import com.android.volley.toolbox.Volley; 19 20 /** 21 * Volley是Android平台网络通信库:更快,更简单,更健壮。 22 * volley提供的功能: 23 * 1. JSON、图片(异步) 24 * 2. 网络请求的排序 25 * 3. 网络请求的优先级处理 26 * 4. 缓存 27 * 5. 多级别的取消请求 28 * 6. 与Activity生命周期联动(Activity结束时同时取消所有网络请求) 29 * 30 * 31 * 获取Volley() 32 * git clone https://android.googlesource.com/platform/frameworks/volley 33 */ 34 public class MainActivity extends Activity { 35 private ImageView iv1; 36 37 @Override 38 protected void onCreate(Bundle savedInstanceState) { 39 super.onCreate(savedInstanceState); 40 setContentView(R.layout.activity_main); 41 init(); 42 //加载网络图片 43 loadImageVolley(); 44 } 45 46 47 public void init() { 48 iv1 = (ImageView) findViewById(R.id.iv); 49 } 50 51 52 //异步加载图片 53 54 public void loadImageVolley() { 55 String imageurl = "http://49.123.72.145/text.jpg"; 56 RequestQueue requestQueue = Volley.newRequestQueue(this); 57 final LruCache<String, Bitmap> lurcache = new LruCache<String, Bitmap>(20); 58 ImageCache imageCache = new ImageCache() { 59 60 //返回当前图片 61 public Bitmap getBitmap(String key) { 62 return lurcache.get(key); 63 } 64 65 //缓存图片资源 66 public void putBitmap(String key, Bitmap value) { 67 lurcache.put(key, value); 68 } 69 70 }; 71 72 ImageLoader imageLoader = new ImageLoader(requestQueue, imageCache); 73 /** 74 * getImageListener(ImageView view, int defaultImageResId, int errorImageResId) 75 * 76 */ 77 ImageListener listener = imageLoader.getImageListener(iv1, R.drawable.ic_launcher, 78 R.drawable.ic_launcher); 79 imageLoader.get(imageurl, listener); 80 } 81 82 }
这里要使用到网络,所以我们这里在AndroidMainfest.xml添加网络权限,如下:
<uses-permission android:name="android.permission.INTERNET"/>
(5)布署程序到模拟器上,如下:
4. Volley使用NetWorkImageView:
Volley提供了一个新的控件NetworkImageView来代替传统的ImageView,这个控件的图片属性可以通过:
mImageView.setImageUrl(url, imageLoader)
来设定。而且,这个控件在被从父控件detach的时候,会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题。
(1)这里我们3的项目中展示NetWorkImageView的使用,如下我们首先来到布局文件activity_main.xml,如下:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.himi.myvolley.MainActivity" > 10 11 <ImageView 12 android:id="@+id/iv" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_alignParentLeft="true" 16 android:layout_alignParentTop="true" 17 android:layout_marginLeft="30dp" 18 android:layout_marginTop="16dp" /> 19 20 <com.android.volley.toolbox.NetworkImageView 21 android:id="@+id/imageView1" 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:layout_centerHorizontal="true" 25 android:layout_centerVertical="true" 26 /> 27 28 </RelativeLayout>
(2)然后是MainActivity.java,如下:
1 package com.himi.myvolley; 2 3 import org.json.JSONObject; 4 5 import android.app.Activity; 6 import android.graphics.Bitmap; 7 import android.os.Bundle; 8 import android.util.LruCache; 9 import android.widget.ImageView; 10 11 import com.android.volley.Request; 12 import com.android.volley.RequestQueue; 13 import com.android.volley.Response; 14 import com.android.volley.toolbox.ImageLoader; 15 import com.android.volley.toolbox.ImageLoader.ImageCache; 16 import com.android.volley.toolbox.ImageLoader.ImageListener; 17 import com.android.volley.toolbox.JsonObjectRequest; 18 import com.android.volley.toolbox.NetworkImageView; 19 import com.android.volley.toolbox.Volley; 20 21 /** 22 * Volley是Android平台网络通信库:更快,更简单,更健壮。 23 * volley提供的功能: 24 * 1. JSON、图片(异步) 25 * 2. 网络请求的排序 26 * 3. 网络请求的优先级处理 27 * 4. 缓存 28 * 5. 多级别的取消请求 29 * 6. 与Activity生命周期联动(Activity结束时同时取消所有网络请求) 30 * 31 * 32 * 获取Volley() 33 * git clone https://android.googlesource.com/platform/frameworks/volley 34 */ 35 public class MainActivity extends Activity { 36 private ImageView iv1; 37 private NetworkImageView iv2; 38 39 @Override 40 protected void onCreate(Bundle savedInstanceState) { 41 super.onCreate(savedInstanceState); 42 setContentView(R.layout.activity_main); 43 init(); 44 //getJSONVolley(); 45 NetWorkImageViewVolley(); 46 47 } 48 49 50 public void init() { 51 iv1 = (ImageView) findViewById(R.id.iv); 52 iv2 = (NetworkImageView) findViewById(R.id.imageView1); 53 loadImageVolley(); 54 } 55 56 //获取Json字符串 57 public void getJSONVolley() { 58 RequestQueue requestQueue = Volley.newRequestQueue(this); 59 String JSONDateUrl = "http://www.wwtliu.com/jsondata.html"; 60 /** 61 * JsonObjectRequest(int method, String url, JSONObject jsonRequest, 62 * Listener<JSONObject> listener, ErrorListener errorListener) 63 */ 64 JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, JSONDateUrl, null, 65 new Response.Listener<JSONObject>() { 66 67 public void onResponse(JSONObject response) { 68 System.out.println("response="+response); 69 70 } 71 72 }, new Response.ErrorListener() { 73 public void onErrorResponse(com.android.volley.VolleyError arg0) { 74 System.out.println("对不起,有问题"); 75 }; 76 } 77 ); 78 79 requestQueue.add(jsonObjectRequest); 80 } 81 82 //异步加载图片 83 //http://localhost/lesson-img.png 84 public void loadImageVolley() { 85 String imageurl = "http://49.123.72.145/text.jpg"; 86 RequestQueue requestQueue = Volley.newRequestQueue(this); 87 final LruCache<String, Bitmap> lurcache = new LruCache<String, Bitmap>(20); 88 ImageCache imageCache = new ImageCache() { 89 90 //返回当前图片 91 public Bitmap getBitmap(String key) { 92 return lurcache.get(key); 93 } 94 95 //缓存图片资源 96 public void putBitmap(String key, Bitmap value) { 97 lurcache.put(key, value); 98 } 99 100 }; 101 102 ImageLoader imageLoader = new ImageLoader(requestQueue, imageCache); 103 /** 104 * getImageListener(ImageView view, int defaultImageResId, int errorImageResId) 105 * 106 */ 107 ImageListener listener = imageLoader.getImageListener(iv1, R.drawable.ic_launcher, 108 R.drawable.ic_launcher); 109 imageLoader.get(imageurl, listener); 110 } 111 // Volley使用NetWorkImageView 112 public void NetWorkImageViewVolley() { 113 String imageUrl = "http://49.123.72.145/text.jpg"; 114 RequestQueue requestQueue = Volley.newRequestQueue(this); 115 final LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(20); 116 ImageCache imageCache = new ImageCache() { 117 118 public Bitmap getBitmap(String key) { 119 // TODO 自动生成的方法存根 120 return lruCache.get(key); 121 } 122 123 public void putBitmap(String key, Bitmap value) { 124 lruCache.put(key, value); 125 126 } 127 128 }; 129 130 ImageLoader imageLoader = new ImageLoader(requestQueue, imageCache); 131 iv2.setTag("url"); 132 iv2.setImageUrl(imageUrl, imageLoader); 133 } 134 135 }
其他代码不变,布署程序到模拟器上,效果如下:
5. Volley的框架原理:
Volley使用了线程池来作为基础结构,主要分为主线程,cache调度线程和 network调度线程。
主线程 和 cache调度线程 都只有一个,而network调度线程可以有多个,这样能解决比并行问题。如下图:
• 优点:Volley简化了网络通信这块的开发,特别是针对数据量不大但网络通信频繁,对JSON对象,图片加载这两块进行了很好的封装和支持,
• 缺点:对大数据(large payloads ),流媒体,这些case不能很好的支持,还需要使用原始的方法,比如Download Manager等。
其中蓝色的是主线程,绿色的是缓存线程,黄色的是网络线程
(1)当一个Request请求添加到RequestQueue请求队列中,Volley就开始工作了。RequestQueue请求队列中持有一个CacheDispatcher缓存调度器和一组NetworkDispatcher网络调度器。
(2)RequestQueue会先叫来CacheDispatcher缓存调度器,让他去看看,当前请求的数据在不在cache中。
2.1.当前的数据在cache中,那就把数据从cache中取出来,然后经过一番加工,将加工好的数据交付给主线程
2.2.当前数据没在cache中,进行第3步
(3)进行到了这一步,那肯定是数据没有在缓存中,那只能去网络中获取了,这时候RequestQueue会叫来NetworkDispatcher网络调度器,NetworkDispatcher可是有一大帮子呢,其实这是一个线程池,默认情况下会启动4个线程去网络下载数据。所以RequestQueue把当前闲着的NetworkDispatcher叫来,给他们分配任务。
(4)拿到任务的NetworkDispatcher就会去网络上下载数据了,与此同时,他会判断下载到的数据能否写入到cache缓存中,如果可以的话就写入cache,以便于下一次直接从cache中获取到数据。最后,将数据加工,交付给主线程。
如果在一个Activity里面启动了网络请求,而在这个网络请求还没返回结果的时候,如果Activity被结束了,则我们需要写如下代码作为防守:
1 @Override 2 public void onPostExecute(Result r) { 3 if (getActivity() == null) { 4 return; 5 } 6 // ... 7 }
Activity被终止之后,如果继续使用其中的Context等,除了无辜的浪费CPU,电池,网络等资源,有可能还会导致程序crash,所以,我们需要处理这种一场情况。
使用Volley的话,我们可以在Activity停止的时候,同时取消所有或部分未完成的网络请求。
Volley里所有的请求结果会返回给主进程,如果在主进程里取消了某些请求,则这些请求将不会被返回给主线程。
比如,可以针对某些个request做取消操作:
1 @Override 2 public void onStop() { 3 for (Request <?> req : mInFlightRequests) { 4 req.cancel(); 5 } 6 ... 7 }
或者,取消这个队列里的所有请求:
1 @Override 2 pubic void onStop() { 3 mRequestQueue.cancelAll(this); 4 ... 5 }
也可以根据RequestFilter或者Tag来终止某些请求:
1 @Override public void onStop() { 2 mRequestQueue.cancelAll( new RequestFilter() {}) 3 ... 4 // or 5 mRequestQueue.cancelAll(new Object()); 6 ...