Volley的中文翻译为“齐射、并发”,是在2013年的Google大会上发布的一款Android平台网络通信库,具有网络请求的处理、小图片的异步加载和缓存等功能,能够帮助 Android APP 更方便地执行网络操作,而且更快速高效。
在Google IO的演讲上,其配图是一幅发射火弓箭的图,有点类似流星。这表示,Volley特别适合数据量不大但是通信频繁的场景。见下图:
Volley 不适合用来下载大的数据文件。因为 Volley 会保持在解析的过程中所有的响应。对于下载大量的数据操作,请考虑使用 DownloadManager。
在volley推出之前我们一般会选择比较成熟的第三方网络通信库,如:android-async-http、retrofit、okhttp等。他们各有优劣,可有所斟酌地选择选择更适合项目的类库。
附录:
Volley的github地址: https://github.com/mcxiaoke/android-volley ;
Google I/O 2013 – Volley: Easy, Fast Networking for Android: https://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded
Volley 框架的核心代码是托管在 AOSP 仓库 的 frameworks/volley 中,相关的工具放在 toolbox 下。
把 Volley 添加到项目中最简便的方法是 Clone 仓库,然后把它设置为一个 library project。
git clone https://android.googlesource.com/platform/frameworks/volley
android update project -p . ant jar
如无意外,将获得volley.jar包。
可参考: http://jingyan.baidu.com/article/e6c8503c7190b7e54f1a1893.html
附上我的volley.jar包的地址: http://pan.baidu.com/s/1sjSwCrV ,方便大家直接下载使用。当然,Volley更新较快,还是希望大家能直接通过clone代码后进行编译。
Volley工作原理图如下:
使用Volley框架实现网络数据请求主要有以下三个步骤:
一般而言,网络请求队列都是整个APP内使用的全局性对象,因此最好写入Application类中:
public class MyApplication extends Application{ // 建立请求队列 public static RequestQueue queue; @Override public void onCreate() { super.onCreate(); queue = Volley.newRequestQueue(getApplicationContext()); } public static RequestQueue getHttpQueue() { return queue; } }
这是,我们还需要修改AndroidManifest.xml文件,使APP的Application对象为我们刚定义的MyApplication,并添加INTERNET权限:
<uses-permission android:name="android.permission.INTERNET" /> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > </application>
Volley提供了JsonObjectRequest、JsonArrayRequest、StringRequest等Request形式:
另外可以继承Request
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // GET请求 VolleyGet(); // POST请求 VolleyPost(); } // 定义POST请求的方法 private void VolleyPost() { // 请求地址 String url = "http://ce.sysu.edu.cn/hope/"; // 创建StringRequest,定义字符串请求的请求方式为POST, StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() { // 请求成功后执行的函数 @Override public void onResponse(String s) { // 打印出POST请求返回的字符串 Toast.makeText(MainActivity.this, "POST: " + s, Toast.LENGTH_LONG).show(); } }, new Response.ErrorListener() { // 请求失败时执行的函数 @Override public void onErrorResponse(VolleyError volleyError) { } }){ // 定义请求数据 @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> hashMap = new HashMap<String, String>(); hashMap.put("phone", "11111"); return hashMap; } }; // 设置该请求的标签 request.setTag("abcPost"); // 将请求添加到队列中 MyApplication.getHttpQueue().add(request); } // 定义GET请求的方法 private void VolleyGet() { // 定义请求地址 String url = "http://ce.sysu.edu.cn/hope/"; StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String s) { // 打印出GET请求返回的字符串 Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { } }); // 设置该请求的标签 request.setTag("abcGet"); // 将请求添加到队列中 MyApplication.getHttpQueue().add(request); } }
输出:
// POST请求 POST: <厚朴网站首页源代码。。。> // GET请求 GET: <厚朴网站首页源代码。。。>
// 网络请求标签为"abcGet" public void onStop() { super.onStop(); MyApplication.getHttpQueues.cancelAll("abcGet"); }
在activity的onStop()方法里面,取消所有的包含这个tag的请求任务。
@Override protected void onStop() { super.onStop(); mRequestQueue.cancelAll(this); }
因为网络请求队列相对于APP应用老说是全局对象,因此可以定义在全局中。为此,我们新建一个LIMSApplication,并让其继承自Application。
LIMSApplication.java文件:
public class LIMSApplication extends Application { public static RequestQueue volleyQueue; @Override public void onCreate() { super.onCreate(); /* Volley配置 */ // 建立Volley的Http请求队列 volleyQueue = Volley.newRequestQueue(getApplicationContext()); } // 开放Volley的HTTP请求队列接口 public static RequestQueue getRequestQueue() { return volleyQueue; } }
不要忘记在AndroidManifest.xml文件中修改Application的name和相应的网络请求权限:
<uses-permission android:name="android.permission.INTERNET" /> <application android:name=".LIMSApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > </application>
目前,VolleyRequestUtil工具库只包含了两个函数,分别获取GET和POST请求。
VolleyRequestUtil.java:
public class VolleyRequestUtil { public static StringRequest stringRequest; public static Context context; /* * 获取GET请求内容 * 参数: * context:当前上下文; * url:请求的url地址; * tag:当前请求的标签; * volleyListenerInterface:VolleyListenerInterface接口; * */ public static void RequestGet(Context context, String url, String tag, VolleyListenerInterface volleyListenerInterface) { // 清除请求队列中的tag标记请求 LIMSApplication.getRequestQueue().cancelAll(tag); // 创建当前的请求,获取字符串内容 stringRequest = new StringRequest(Request.Method.GET, url, volleyListenerInterface.responseListener(), volleyListenerInterface.errorListener()); // 为当前请求添加标记 stringRequest.setTag(tag); // 将当前请求添加到请求队列中 LIMSApplication.getRequestQueue().add(stringRequest); // 重启当前请求队列 LIMSApplication.getRequestQueue().start(); } /* * 获取POST请求内容(请求的代码为Map) * 参数: * context:当前上下文; * url:请求的url地址; * tag:当前请求的标签; * params:POST请求内容; * volleyListenerInterface:VolleyListenerInterface接口; * */ public static void RequestPost(Context context, String url, String tag, final Map<String, String> params, VolleyListenerInterface volleyListenerInterface) { // 清除请求队列中的tag标记请求 LIMSApplication.getRequestQueue().cancelAll(tag); // 创建当前的POST请求,并将请求内容写入Map中 stringRequest = new StringRequest(Request.Method.POST, url, volleyListenerInterface.responseListener(), volleyListenerInterface.errorListener()){ @Override protected Map<String, String> getParams() throws AuthFailureError { return params; } }; // 为当前请求添加标记 stringRequest.setTag(tag); // 将当前请求添加到请求队列中 LIMSApplication.getRequestQueue().add(stringRequest); // 重启当前请求队列 LIMSApplication.getRequestQueue().start(); } }
封装Volley请求(成功或失败)的监听事件,见VolleyListenerInterface.java:
public abstract class VolleyListenerInterface { public Context mContext; public static Response.Listener<String> mListener; public static Response.ErrorListener mErrorListener; public VolleyListenerInterface(Context context, Response.Listener<String> listener, Response.ErrorListener errorListener) { this.mContext = context; this.mErrorListener = errorListener; this.mListener = listener; } // 请求成功时的回调函数 public abstract void onMySuccess(String result); // 请求失败时的回调函数 public abstract void onMyError(VolleyError error); // 创建请求的事件监听 public Response.Listener<String> responseListener() { mListener = new Response.Listener<String>() { @Override public void onResponse(String s) { onMySuccess(s); } }; return mListener; } // 创建请求失败的事件监听 public Response.ErrorListener errorListener() { mErrorListener = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { onMyError(volleyError); } }; return mErrorListener; } }
Volley库还具有图片加载的功能。但适合小图片的异步加载,不适合于比较大的图片资源的请求。
Volley提供了多种Request方法,譬如ImageRequest、ImageLoader、NetWorkImageView。
网络图片资源的请求封装如下:
ImageLoaderUtil.java:
public class ImageLoaderUtil { /* * 通过ImageRequest来显示网络图片 * */ public static void setImageRequest(String url, final ImageView imageView) { ImageRequest imageRequest = new ImageRequest(url, new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap bitmap) { imageView.setImageBitmap(bitmap); } }, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { imageView.setBackgroundResource(R.mipmap.ic_launcher); } }); LIMSApplication.getRequestQueue().add(imageRequest); } /* * 通过ImageLoader来显示网络图片 * */ public static void setImageLoader(String url, ImageView imageView, int defaultImageResId, int errorImageResId) { ImageLoader loader = new ImageLoader(LIMSApplication.getRequestQueue(), new BitmapCache()); ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(imageView, defaultImageResId, errorImageResId); loader.get(url, imageListener); } /* * 通过Volley的NetWorkImageView来显示网络图片 * */ public static void setNetWorkImageView(String url, NetworkImageView netWorkImageView, int defaultImageResId, int errorImageResId) { ImageLoader loader = new ImageLoader(LIMSApplication.getRequestQueue(), new BitmapCache()); netWorkImageView.setDefaultImageResId(defaultImageResId); netWorkImageView.setErrorImageResId(errorImageResId); netWorkImageView.setImageUrl(url, loader); } }
new VolleyRequestUtil().RequestGet(this, "http://ce.sysu.edu.cn/hope/", "hopePage", new VolleyListenerInterface(this, VolleyListenerInterface.mListener, VolleyListenerInterface.mErrorListener) { // Volley请求成功时调用的函数 @Override public void onMySuccess(String result) { Toast.makeText(this, s, Toast.LENGTH_LONG).show(); } // Volley请求失败时调用的函数 @Override public void onMyError(VolleyError error) { // ... } });
输出:厚朴网站首页的源代码。
new VolleyRequestUtil().RequestPOST(this, "http://ce.sysu.edu.cn/hope/", "hopePage", new VolleyListenerInterface(this, VolleyListenerInterface.mListener, VolleyListenerInterface.mErrorListener) { // Volley请求成功时调用的函数 @Override public void onMySuccess(String result) { Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show(); } // Volley请求失败时调用的函数 @Override public void onMyError(VolleyError error) { // ... } });
输出:厚朴网站首页的源代码。
// 参数分别为:请求图片的地址、图片的容器ImageView ImageView imgView = (ImageView) findViewById(R.id.imgView); new ImageLoaderUtil().setImageRequest("http://7xinb0.com1.z0.glb.clouddn.com/skin/HopeRebuild/dist/images/logo/logo_40.png", imgView);
在布局文件中,定义ImageView:
<ImageView android:id="@+id/imgView" android:layout_width="wrap_content" android:layout_height="wrap_content" />
// 参数分别为:请求图片的地址、图片的容器ImageView、默认显示的图片ResourceID、请求失败时显示的图片的ResourceID new ImageLoaderUtil().setImageLoader("http://7xinb0.com1.z0.glb.clouddn.com/skin/HopeRebuild/dist/images/logo/logo_40.png", imgView, R.mipmap.default, R.mipmap.error);
在布局文件中,定义ImageView:
<ImageView android:id="@+id/imgView" android:layout_width="wrap_content" android:layout_height="wrap_content" />
// 参数分别为:请求图片的地址、图片的容器NetworkImageView、默认显示的图片ResourceID、请求失败时显示的图片的ResourceID NetworkImageView netWorkImageView = (NetworkImageView) findViewById(R.id.imgNetworkView); new ImageLoaderUtil().setNetWorkImageView("http://7xinb0.com1.z0.glb.clouddn.com/skin/HopeRebuild/dist/images/logo/logo_40.png", netWorkImageView, R.mipmap.default, R.mipmap.error);
在布局文件中,定义NetworkImageView:
<com.android.volley.toolbox.NetworkImageView android:id="@+id/imgNetworkView" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerHorizontal="true"/>
该Volley的封装中,暂未考虑到图片和数据缓存。
有一些地方封装得仍不够抽象,有待完善。
非常欢迎读者能提出修改建议,一起进步。