Mako Shan

Mako 是一名密码朋克爱好者
这里是我记录生活和成长的地方

联系我的微信号
👏欢迎一起交流学习

使用Volley

Volley由Google在2013年开发者大会发布,旨在解决Android开发中复杂的联网操作。
Volley擅长执行用来显示UI的RPC操作, 例如获取搜索结果的数据。它轻松的整合了任何协议,并输出操作结果的数据,可以是raw strings,也可以是images,或者是JSON。通过提供内置你可能使用到得功能,Volley可以使得你免去重复编写样板代码,使你可以把关注点放在你的app的功能逻辑上。

Volley不适合用来下载大的数据文件。因为Volley会在解析的过程中保留持有所有的响应数据在内存中。对于下载大量的数据操作,请考虑使用DownloadManager。
 Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。所以不建议用它去进行下载文件、加载大图的操作。有人可能会问,如果我服务器中的图片都挺大的,activity中listview要加载这些图片,是不是不能用这个框架呢?其实这个问题本身就是错误的,你想如果你服务器的图片都是大图,你要在手机上用照片墙进行展示,下载图片都会慢的要死,这个本身就是不可行的。所以在项目初期就应该建立好服务器端的小图,照片墙用加载小图,点击后再从网络上下载大图,这才是正确思路。这时你就可以用volley加载小图了,如果是要加载大图,可以用别的算法,强烈建议手动完成大图清除的工作,否则很可能会出现OOM。Volley本身没有做什么回收算法,还是用最基本的GC,实际使用中可以根据需要自定义一下。

Vollay原理

Image Title
从图中大概可以看出,框架中包含三种线程,主线程,内存线程,网路线程。网络线程可以是多个,主线程负责将请求按优先权顺序添加进任务队列,内存线程查看缓存中是否有此请求,有的话从缓存中获取返回数据回馈给主线程,否则将请求交给网络线程,网络线程多个任务线程(NetworkDispatcher)组成,这些任务线程同时启动,不停的从任务队列中获取待执行的任务,执行完毕后把返回结果回馈给主线程。

Volley框架HTTP请求流程图
Image Title

从图中可以简单了解到,各类请求添加进请求队列,然后分发给网络线程(RequestDispatcher),通过HurlStack或者HttpClientStack执行网络请求,得到NetworkResponse返回,如果是错误,可以retry一次,否则直接返回给主线程错误信息,如果返回数据正确,就会解析成Response 通过ResponseDelivery返回给主线程。

相关阅读 https://www.zybuluo.com/flyouting/note/20334

http://blog.csdn.net/guolin_blog/article/details/17656437

Image Title

Google I/O 2013

Volley是在2013的Google开发者大会发布的,下面是FicusKirkpatrick演讲视频。

视频相关pdf

官方实例app

The Google I/O 2014 Android App

使用

  • 可以从官方AOSP获取源码
git clone https://android.googlesource.com/platform/frameworks/volley
  • Gradle配置,但不是官方提供的
compile 'com.mcxiaoke.volley:library:1.0.+'
// or 
compile 'com.mcxiaoke.volley:library:1.0.+@aar'

创建全局的单例模式

初始化请求对象——RequestQueue

不必为每一次HTTP请求都创建一个RequestQueue对象,推荐在application中初始化。既然是Http操作,自然有请求和响应,RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的。所以在这里我建立了一个application,然后用单例模式定义了这个对象。当然,你可以选择在一个activity中定义一个RequestQueue对象,但这样可能会比较麻烦,而且还可能出现请求队列包含activity强引用的问题,因此我还是推荐在application中定义。

AppController.java
 
public class AppController extends Application {
 
    public static final String TAG = AppController.class
            .getSimpleName();
 
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
 
    private static AppController mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized AppController getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }
 
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

创建LruCache图片缓存处理

LruBitmapCache.java
package info.androidhive.volleyexamples.volley.utils;
 
import com.android.volley.toolbox.ImageLoader.ImageCache;
 
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
 
public class LruBitmapCache extends LruCache<String, Bitmap> implements
        ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;
 
        return cacheSize;
    }
 
    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }
 
    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }
 
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }
 
    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }
 
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
}

AndroidManifest.xml配置

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.volleyexamples"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application
        android:name="info.androidhive.volleyexamples.app.AppController"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <!-- all activities and other stuff -->
    </application>
 
</manifest>

使用StringRequest接收String类型的响应

前面定义了请求对象,那么自然就有接收响应的对象了,这个框架中有多个响应对象,像StringRequest接受到的响应就是string类型的;JsonRequest接收的响应就是Json类型对象。其实它们都是继承自Request,然后根据不同的响应数据来进行特殊的处理。

// Tag used to cancel the request
String tag_json_obj = "json_obj_req";
 
String url = "http://api.androidhive.info/volley/person_object.json";
         
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setMessage("Loading...");
pDialog.show();     
         
        JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,
                url, null,
                new Response.Listener<JSONObject>() {
 
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, response.toString());
                        pDialog.hide();
                    }
                }, new Response.ErrorListener() {
 
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d(TAG, "Error: " + error.getMessage());
                        // hide the progress dialog
                        pDialog.hide();
                    }
                });
 
// Adding request to request queue
AppController.getInstance().addToRequestQueue(jsonObjReq, tag_json_obj);

这就是StringRequest的两个构造函数,不同之处是一个传入了一个method的参数,一个没有。其实看上面的源码就知道,如果你不传入method,默认会调用GET方式进行请求。当你传入了Method.POST就会用post来请求。

【参数解释】

url:请求的地址

listener:响应成功的监听器

errorListener:出错时的监听器

10
AppCompat22新特性
目录