volley网络框架2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jakezhang1990/article/details/83412857

volley优势:

1,使用了线程池,支持多个并发网络请求链接,可以高强度密集小数据量网络请求;
2,支持请求优先级,可以进行定制,设置队列最大请求数等;
3,支持取消网络;可以取消单个请求,也可以设置要取消的请求块或范围。
4,自动调度网络请求,已经做了封装,可以在主线程发起请求会自动切换到子线程,子线程拿到响应会自动切换到主线程,回调到填充UI;

volley劣势:

Volley不适合大型下载或流媒体操作,因为Volley在解析期间将所有响应保存在内存中。对于大型下载操作,请考虑使用类似的替代方法DownloadManager
可以通过创建RequestQueue并传递 Request对象来使用Volley
RequestQueue所做的工作:1管理子线程的网络请求的执行操作,2写入原始响应到缓存并从写入的缓存中读取响应内容,3解析响应(其实这第三步是异步回调来完成的,严格来说不能算是RequestQueue的工作)。

Requests do the parsing of raw responses and 
Volley takes care of dispatching the parsed response back to the main thread for delivery.
Requests做原始响应,Volley讲解析后的响应分发给主线程进行传递。

Volley总是在主线程上提供已解析的响应。

最简单的用法-发起网络请求

java:

final TextView mTextView = (TextView) findViewById(R.id.text);
// ...

// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});

// Add the request to the RequestQueue.
queue.add(stringRequest);

kotlin:

val textView = findViewById<TextView>(R.id.text)
// ...

// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "http://www.google.com"

// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
        Response.Listener<String> { response ->
            // Display the first 500 characters of the response string.
            textView.text = "Response is: ${response.substring(0, 500)}"
        },
        Response.ErrorListener { textView.text = "That didn't work!" })

// Add the request to the RequestQueue.
queue.add(stringRequest)

原理分析

RequestQueue调用add()方法即queue.add(request),就可以将这个请求加入到线程池的中,在线程池的管道中排序进行移动、获取服务以及解析原声响应并传递到主线程。
当调用add()方法时,Volley运行一个缓存处理线程和一个网络分发线程池。
当您向队列添加请求时,该请求会被缓存线程拾取并进行分类:如果请求可以从缓存中获得服务,则缓存响应将在缓存线程上进行解析,并且解析后的响应将传递到主线程上。如果无法从缓存中为请求提供服务,则将其置于网络请求队列中。第一个可用的网络线程从队列中取得请求,执行HTTP事务,解析工作线程上的响应,将响应写入缓存,并将解析的响应发送回主线程以进行传递。
在这里插入图片描述

请注意,阻塞I / O、解析/解码等昂贵耗时的操作是在工作线程上完成的。可以从任何线程添加请求,但响应最终都会传递到主线程上。

取消请求

要取消请求,使用RequestQueue对象调用cancel()方法,来取消Request请求。一旦取消,Volley就永远不会调用当前请求的响应处理产需。在实际开发中,可以在onStop()方法中取消所有待处理的请求。
所以,必须要知道现在正在进行的请求是哪个,也就是说必须跟踪所有正在进行的请求,以便能够在适当时间取消他们。
volley提供了一种简便的办法来进行跟踪:将标记对象与每个请求相关联,也就是说给每个请求设置一个标记tag,然后,可以使用此标记取消请求,可以取消一个请求,也可以取消一大块请求,比如取消当前页面所有的待处理的请求。
例如,可以将所有的请求的tag设置为Activity,在onStop()方法中调用requestQueue.cancelAll(this)取消当前页面所有的待处理的网络请求

两步走

1,设置标记

kotlin:

val TAG = "MyTag"
val stringRequest: StringRequest // Assume this exists.
val requestQueue: RequestQueue? // Assume this exists.

// Set the tag on the request.
stringRequest.tag = TAG

// Add the request to the RequestQueue.
requestQueue?.add(stringRequest)

java

public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue;  // Assume this exists.

// Set the tag on the request.
stringRequest.setTag(TAG);

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);

2,根据标记取消请求

kotlin

protected fun onStop() {
    super.onStop()
    requestQueue?.cancelAll(TAG)
}

java:

@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

设置RequestQueue

上面是最简单的使用volley发起网络请求,Volley.newRequestQueue来实例化RequestQueue,线程池支持的最大并发请求数量、缓存大小、线程池管道请求排队、网络请求超时时间等使用的都是volley默认的设置,如果需要更改设置,可以对RequestQueue进行设置。
如果只进行一次请求,那么也不用进行这些设置,直接通过默认设置进行使用就足够了。

RequestQueue一般推介使用単例模式创建。

设置网络和缓存

重复讲一下,RequestQueue做两件事情:1,执行网络请求的传输;2,操作处理缓存;Volley toolbox包中提供了标准实现可用:DiskBasedCache提供带有内存索引的单文件响应缓存,BasicNetwork根据首选的HTTP客户端提供网络传输。BasicNetwork是Volley的默认网络实现。BasicNetwork 必须通过HTTP客户端初始化,来保证APP可以连接到网络. 通常这个客户端是一个HttpURLConnection。

设置RequestQueue的步骤:

java:

RequestQueue mRequestQueue;

// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap

// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());

// Instantiate the RequestQueue with the cache and network.
mRequestQueue = new RequestQueue(cache, network);

// Start the queue
mRequestQueue.start();

String url ="http://www.example.com";

// Formulate the request and handle the response.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
        new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Do something with the response
    }
},
    new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // Handle error
    }
});

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);

// ...

kotlin:

// Instantiate the cache
val cache = DiskBasedCache(cacheDir, 1024 * 1024) // 1MB cap

// Set up the network to use HttpURLConnection as the HTTP client.
val network = BasicNetwork(HurlStack())

// Instantiate the RequestQueue with the cache and network. Start the queue.
val requestQueue = RequestQueue(cache, network).apply {
    start()
}

val url = "http://www.example.com"

// Formulate the request and handle the response.
val stringRequest = StringRequest(Request.Method.GET, url,
         Response.Listener<String> { response ->
            // Do something with the response
        },
        Response.ErrorListener { error ->
            // Handle error
            textView.text = "ERROR: %s".format(error.toString())
        })

// Add the request to the RequestQueue.
requestQueue.add(stringRequest)

// ...

使用単例模式

如果应用程序需要不断的使用网络,RequestQueue这个对象的获取可以使员工単例模式,来保证整个APP存活期间,只有一个对象,来提高效率。当然也可以子类化Application,然后在Application.onCreate()中初始化RequestQueue
一个关键概念是RequestQueue必须使用Application上下文实例化 ,而不是Activity上下文。这可确保在RequestQueue应用程序的生命周期内持续使用,而不是每次重新创建活动时重新创建(例如,当用户旋转设备时)。
例如
kotlin:

class MySingleton constructor(context: Context) {
    companion object {
        @Volatile
        private var INSTANCE: MySingleton? = null
        fun getInstance(context: Context) =
            INSTANCE ?: synchronized(this) {
                INSTANCE ?: MySingleton(context).also {
                    INSTANCE = it
                }
            }
    }
    val imageLoader: ImageLoader by lazy {
        ImageLoader(requestQueue,
                object : ImageLoader.ImageCache {
                    private val cache = LruCache<String, Bitmap>(20)
                    override fun getBitmap(url: String): Bitmap {
                        return cache.get(url)
                    }
                    override fun putBitmap(url: String, bitmap: Bitmap) {
                        cache.put(url, bitmap)
                    }
                })
    }
    val requestQueue: RequestQueue by lazy {
        // applicationContext is key, it keeps you from leaking the
        // Activity or BroadcastReceiver if someone passes one in.
        Volley.newRequestQueue(context.applicationContext)
    }
    fun <T> addToRequestQueue(req: Request<T>) {
        requestQueue.add(req)
    }
}

java:

public class MySingleton {
    private static MySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;

    private MySingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(mRequestQueue,
                new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap>
                    cache = new LruCache<String, Bitmap>(20);

            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url, bitmap);
            }
        });
    }

    public static synchronized MySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new MySingleton(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

RequestQueue使用singleton类执行操作的示例
kotlin:

// Get a RequestQueue
val queue = MySingleton.getInstance(this.applicationContext).requestQueue

// ...

// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest)

java:

// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
    getRequestQueue();

// ...

// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);

使用volley标准的发起请求

使用Volley支持的常见请求类型:

  1. StringRequest。指定URL并接收原始字符串作为响应。
  2. JsonObjectRequestJsonArrayRequest(两个子类 JsonRequest)。指定URL并分别获取JSON对象或数组(作为响应)。
    第一个字符串是比较简单的,上面有例子,现在说说json数据作为相应的请求发起方式

请求JSON

  • JsonArrayRequest- 请求JSONArray 在给定URL处检索 响应正文。
  • JsonObjectRequest- JSONObject 在给定URL处检索响应主体的请求 ,允许将可选项 JSONObject 作为请求主体的一部分传入。
    这两个类都基于公共基类JsonRequest。
    例子:
    java:
String url = "http://my-json-feed";

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
        (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

    @Override
    public void onResponse(JSONObject response) {
        mTextView.setText("Response: " + response.toString());
    }
}, new Response.ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        // TODO: Handle error

    }
});

// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);

kotlin:

val url = "http://my-json-feed"

val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
        Response.Listener { response ->
            textView.text = "Response: %s".format(response.toString())
        },
        Response.ErrorListener { error ->
            // TODO: Handle error
        }
)

// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest)

自定义请求

基于Gson实现自定义JSON请求。
如果响应是字符串,图像或JSON,则可能不需要实现自定义Request
对于需要实现自定义请求的情况,您只需执行以下操作:

  1. 扩展Request<T>类,其中 <T>表示请求所期望的已解析响应的类型。因此,如果解析后的响应是字符串,则通过扩展创建自定义请求Request<String>
  2. 实现抽象方法,parseNetworkResponse()deliverResponse()

parseNetworkResponse

对于Response相应的封装的传递,使用给定类型(例如字符串,图像或JSON)解析的封装的传递。
例子:
kotlin:

override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
    return try {
        val json = String(
                response?.data ?: ByteArray(0),
                Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
        Response.success(
                gson.fromJson(json, clazz),
                HttpHeaderParser.parseCacheHeaders(response))
    }
    // handle errors
// ...
}

java:

@Override
protected Response<T> parseNetworkResponse(
        NetworkResponse response) {
    try {
        String json = new String(response.data,
        HttpHeaderParser.parseCharset(response.headers));
    return Response.success(gson.fromJson(json, clazz),
    HttpHeaderParser.parseCacheHeaders(response));
    }
    // handle errors
// ...
}

注意:parseNetworkResponse()从工作线程调用,以确保UI线程不会被阻塞。

deliverResponse

调用这个方法parseNetworkResponse()切换到主线程,
例子:
kotlin:

override fun deliverResponse(response: T) = listener.onResponse(response)

java:

protected void deliverResponse(T response) {
        listener.onResponse(response);

GsonRequest 示例

kotlin:

/**
 * Make a GET request and return a parsed object from JSON.
 *
 * @param url URL of the request to make
 * @param clazz Relevant class object, for Gson's reflection
 * @param headers Map of request headers
 */
class GsonRequest<T>(
        url: String,
        private val clazz: Class<T>,
        private val headers: MutableMap<String, String>?,
        private val listener: Response.Listener<T>,
        errorListener: Response.ErrorListener
) : Request<T>(Method.GET, url, errorListener) {
    private val gson = Gson()


    override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders()

    override fun deliverResponse(response: T) = listener.onResponse(response)

    override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
        return try {
            val json = String(
                    response?.data ?: ByteArray(0),
                    Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
            Response.success(
                    gson.fromJson(json, clazz),
                    HttpHeaderParser.parseCacheHeaders(response))
        } catch (e: UnsupportedEncodingException) {
            Response.error(ParseError(e))
        } catch (e: JsonSyntaxException) {
            Response.error(ParseError(e))
        }
    }
}

java:

public class GsonRequest<T> extends Request<T> {
    private final Gson gson = new Gson();
    private final Class<T> clazz;
    private final Map<String, String> headers;
    private final Listener<T> listener;

    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     */
    public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
            Listener<T> listener, ErrorListener errorListener) {
        super(Method.GET, url, errorListener);
        this.clazz = clazz;
        this.headers = headers;
        this.listener = listener;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(
                    response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(
                    gson.fromJson(json, clazz),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/jakezhang1990/article/details/83412857