彻底搞懂Android Volley成功响应的解析处理(11)

100 阅读23分钟

彻底搞懂Android Volley成功响应的解析处理:从源码到实战的全面解析

一、引言

在Android开发中,网络请求是必不可少的一部分。而Volley作为Android官方推荐的网络请求库,凭借其简洁的API和高效的性能,受到了广大开发者的喜爱。当我们发送一个网络请求后,服务器会返回响应数据,而如何正确地解析这些响应数据就成为了关键。

本文将深入剖析Android Volley库中成功响应的解析处理机制,从源码级别详细分析响应解析的各个关键环节。通过本文的学习,你将全面掌握Volley响应解析的实现方法,理解其内部工作机制,从而在开发中更加灵活地处理各种复杂的网络响应场景。

二、Volley响应解析概述

2.1 响应解析的基本概念

响应解析是指将从服务器接收到的原始数据(如字节流、JSON字符串、XML等)转换为应用程序可以直接使用的数据结构(如Java对象、字符串等)的过程。在Android开发中,响应解析是网络请求处理的重要环节,它直接影响到应用程序对服务器数据的处理效率和准确性。

2.2 Volley响应解析的流程

在Volley中,响应解析的基本流程如下:

  1. 网络请求发送后,服务器返回响应数据
  2. Volley将响应数据封装到NetworkResponse对象中
  3. NetworkResponse对象被传递给Request的parseNetworkResponse方法进行解析
  4. parseNetworkResponse方法将原始数据解析为应用程序需要的类型,并封装到Response对象中
  5. Response对象通过ResponseDelivery机制传递到主线程
  6. 在主线程中,Response对象被传递给我们设置的响应监听器进行处理

下面我们将详细分析这个流程中的每一个环节。

三、NetworkResponse类解析

3.1 NetworkResponse类的作用

NetworkResponse类是Volley中用于封装网络响应数据的核心类。它包含了从服务器接收到的原始数据、HTTP状态码、响应头等信息。

3.2 NetworkResponse类的源码分析

让我们来看一下NetworkResponse类的源码:

/**
 * 封装从网络接收到的响应数据。
 */
public class NetworkResponse {
    /** 响应状态码 */
    public final int statusCode;
    
    /** 响应数据的字节数组 */
    public final byte[] data;
    
    /** 响应头 */
    public final Map<String, String> headers;
    
    /** 是否从缓存中获取的响应 */
    public final boolean notModified;
    
    /** 网络请求的耗时,单位为毫秒 */
    public final long networkTimeMs;

    /**
     * 创建一个新的NetworkResponse实例。
     *
     * @param statusCode HTTP状态码
     * @param data 响应数据的字节数组
     * @param headers 响应头
     * @param notModified 是否未修改(HTTP 304状态)
     * @param networkTimeMs 网络请求的耗时
     */
    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified, long networkTimeMs) {
        this.statusCode = statusCode;
        this.data = data;
        this.headers = headers;
        this.notModified = notModified;
        this.networkTimeMs = networkTimeMs;
    }

    /**
     * 创建一个新的NetworkResponse实例,不包含耗时信息。
     *
     * @param statusCode HTTP状态码
     * @param data 响应数据的字节数组
     * @param headers 响应头
     * @param notModified 是否未修改(HTTP 304状态)
     */
    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified) {
        this(statusCode, data, headers, notModified, 0);
    }

    /**
     * 创建一个新的NetworkResponse实例,用于成功响应。
     *
     * @param data 响应数据的字节数组
     * @param headers 响应头
     */
    public NetworkResponse(byte[] data, Map<String, String> headers) {
        this(HttpStatus.SC_OK, data, headers, false, 0);
    }

    /**
     * 创建一个新的NetworkResponse实例,用于成功响应,不包含响应头。
     *
     * @param data 响应数据的字节数组
     */
    public NetworkResponse(byte[] data) {
        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false, 0);
    }
}

从上面的源码可以看出,NetworkResponse类包含了以下几个重要的成员变量:

  • statusCode:HTTP状态码,如200表示成功,404表示未找到资源
  • data:响应数据的字节数组,包含了服务器返回的原始数据
  • headers:响应头,包含了服务器返回的各种元数据
  • notModified:一个布尔值,表示资源是否未被修改(对应HTTP 304状态)
  • networkTimeMs:网络请求的耗时,单位为毫秒

3.3 NetworkResponse的创建过程

在Volley中,NetworkResponse对象是在网络请求完成后创建的。具体来说,是在BasicNetwork类的performRequest方法中创建的:

/**
 * 执行网络请求并返回响应。
 */
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();
    
    // 循环处理请求,支持重试机制
    while (true) {
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        NetworkResponse networkResponse = null;
        
        try {
            // 准备请求头
            Map<String, String> headers = new HashMap<>();
            addCacheHeaders(headers, request.getCacheEntry());
            
            // 添加请求自定义的请求头
            headers.putAll(request.getHeaders());
            
            // 打开HTTP连接
            connection = openConnection(request, headers);
            
            // 设置连接和读取超时时间
            setConnectionParametersForRequest(connection, request);
            
            // 执行请求并获取响应码
            int responseCode = connection.getResponseCode();
            
            if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
                // 如果返回304状态码,表示资源未修改
                Cache.Entry entry = request.getCacheEntry();
                if (entry == null) {
                    // 如果没有缓存条目,返回一个空的响应
                    return new NetworkResponse(HttpURLConnection.HTTP_NOT_FOUND, null,
                            headers, true, SystemClock.elapsedRealtime() - requestStart);
                }
                
                // 返回缓存的响应
                return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED,
                        entry.data, headers, true,
                        SystemClock.elapsedRealtime() - requestStart);
            }
            
            // 读取响应数据
            inputStream = connection.getInputStream();
            byte[] responseContents = null;
            Map<String, String> responseHeaders = convertHeaders(connection.getHeaderFields());
            
            if (responseCode == HttpURLConnection.HTTP_OK) {
                // 如果是200状态码,读取响应内容
                responseContents = entityToBytes(connection.getInputStream(), connection.getContentLength());
            } else {
                // 如果是其他状态码,读取错误内容
                responseContents = entityToBytes(connection.getErrorStream(), connection.getContentLength());
            }
            
            // 创建并返回NetworkResponse对象
            networkResponse = new NetworkResponse(responseCode, responseContents,
                    responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
            
            // 处理重定向
            if (request.shouldRetryServerErrors() && isRetriableStatusCode(responseCode)) {
                attemptRetryOnException("server", request,
                        new ServerError(networkResponse));
            }
            
            return networkResponse;
        } catch (SocketTimeoutException e) {
            // 处理超时异常
            attemptRetryOnException("socket", request, new TimeoutError());
        } catch (MalformedURLException e) {
            // 处理URL格式错误
            throw new RuntimeException("Bad URL " + request.getUrl(), e);
        } catch (IOException e) {
            // 处理IO异常
            int statusCode = 0;
            NetworkResponse networkResponse = null;
            
            if (connection != null) {
                statusCode = connection.getResponseCode();
            } else {
                throw new NoConnectionError(e);
            }
            
            // 读取错误响应
            inputStream = connection.getErrorStream();
            try {
                byte[] errorResponse = entityToBytes(inputStream, connection.getContentLength());
                networkResponse = new NetworkResponse(statusCode, errorResponse,
                        convertHeaders(connection.getHeaderFields()), false,
                        SystemClock.elapsedRealtime() - requestStart);
            } catch (IOException ioe) {
                VolleyLog.e("Error reading error response body: %s", ioe.getMessage());
            }
            
            // 处理可重试的错误
            if (statusCode == HttpURLConnection.HTTP_UNAVAILABLE ||
                    (request.shouldRetryServerErrors() && isRetriableStatusCode(statusCode))) {
                attemptRetryOnException("server", request, new ServerError(networkResponse));
            }
            
            // 抛出其他错误
            if (networkResponse != null) {
                throw new ServerError(networkResponse);
            } else {
                throw new NetworkError(e);
            }
        } finally {
            // 关闭资源
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    VolleyLog.v("Error closing stream");
                }
            }
            
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

从上面的代码可以看出,NetworkResponse对象是在网络请求成功后,根据HTTP状态码、响应数据和响应头创建的。如果请求成功(状态码为200),则读取响应内容并创建包含响应数据的NetworkResponse对象;如果请求失败,则读取错误内容并创建包含错误信息的NetworkResponse对象。

四、Response类解析

4.1 Response类的作用

Response类是Volley中用于封装解析后响应数据的核心类。它包含了解析后的数据、缓存条目等信息。

4.2 Response类的源码分析

让我们来看一下Response类的源码:

/**
 * 封装解析后的响应数据和可能的缓存条目。
 */
public class Response<T> {
    /** 解析后的响应数据 */
    public final T result;
    
    /** 错误信息,如果请求失败 */
    public final VolleyError error;
    
    /** 缓存条目,如果响应应该被缓存 */
    public final Cache.Entry cacheEntry;
    
    /** 是否从缓存中获取的响应 */
    public final boolean intermediate;

    /**
     * 响应成功的回调接口
     */
    public interface Listener<T> {
        /**
         * 当请求成功时调用
         *
         * @param response 解析后的响应数据
         */
        void onResponse(T response);
    }

    /**
     * 响应失败的回调接口
     */
    public interface ErrorListener {
        /**
         * 当请求失败时调用
         *
         * @param error 错误信息
         */
        void onErrorResponse(VolleyError error);
    }

    /**
     * 私有构造函数,使用静态工厂方法创建实例
     */
    private Response(T result, Cache.Entry cacheEntry, boolean intermediate) {
        this.result = result;
        this.error = null;
        this.cacheEntry = cacheEntry;
        this.intermediate = intermediate;
    }

    /**
     * 私有构造函数,使用静态工厂方法创建实例
     */
    private Response(VolleyError error) {
        this.result = null;
        this.error = error;
        this.cacheEntry = null;
        this.intermediate = false;
    }

    /**
     * 判断响应是否成功
     */
    public boolean isSuccess() {
        return error == null;
    }

    /**
     * 创建一个成功的响应
     *
     * @param result 解析后的响应数据
     * @param cacheEntry 缓存条目
     */
    public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
        return new Response<>(result, cacheEntry, false);
    }

    /**
     * 创建一个中间响应(例如,用于流式响应)
     *
     * @param result 解析后的响应数据
     * @param cacheEntry 缓存条目
     */
    public static <T> Response<T> intermediate(T result, Cache.Entry cacheEntry) {
        return new Response<>(result, cacheEntry, true);
    }

    /**
     * 创建一个失败的响应
     *
     * @param error 错误信息
     */
    public static <T> Response<T> error(VolleyError error) {
        return new Response<>(error);
    }
}

从上面的源码可以看出,Response类是一个泛型类,其泛型参数T表示解析后的数据类型。它包含了以下几个重要的成员变量:

  • result:解析后的响应数据,类型为T
  • error:错误信息,如果请求失败
  • cacheEntry:缓存条目,如果响应应该被缓存
  • intermediate:一个布尔值,表示是否为中间响应(例如,用于流式响应)

Response类还定义了两个重要的内部接口:Listener和ErrorListener,分别用于处理成功和失败的响应。

4.3 Response对象的创建

在Volley中,Response对象是在Request的parseNetworkResponse方法中创建的。具体来说,不同类型的Request会根据自己的需求创建不同类型的Response对象。

例如,StringRequest的parseNetworkResponse方法会创建一个包含字符串响应的Response对象:

/**
 * 解析网络响应为字符串
 */
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
        // 根据响应头中的字符集解析响应数据
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
        // 如果字符集不支持,使用默认字符集
        parsed = new String(response.data);
    }
    // 返回成功响应,包含解析后的字符串和缓存条目
    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

而JsonObjectRequest的parseNetworkResponse方法会创建一个包含JSONObject的Response对象:

/**
 * 解析网络响应为JSONObject
 */
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
    try {
        // 将响应数据转换为字符串
        String jsonString = new String(
                response.data,
                HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
        
        // 将字符串解析为JSONObject
        return Response.success(
                new JSONObject(jsonString),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        // 处理编码异常
        return Response.error(new ParseError(e));
    } catch (JSONException je) {
        // 处理JSON解析异常
        return Response.error(new ParseError(je));
    }
}

五、parseNetworkResponse方法解析

5.1 parseNetworkResponse方法的作用

parseNetworkResponse方法是Request类中的一个抽象方法,用于将从服务器接收到的原始响应数据解析为应用程序需要的类型。所有具体的Request类都必须实现这个方法。

5.2 parseNetworkResponse方法的源码分析

Request类中的parseNetworkResponse方法定义如下:

/**
 * 解析网络响应数据。
 * 子类必须重写此方法来解析网络响应数据并返回解析后的结果。
 *
 * @param response 从网络接收到的响应
 * @return 包含解析结果的Response对象
 */
protected abstract Response<T> parseNetworkResponse(NetworkResponse response);

从上面的代码可以看出,parseNetworkResponse方法接收一个NetworkResponse对象作为参数,并返回一个Response对象。具体的解析逻辑由子类实现。

5.3 不同Request子类的parseNetworkResponse实现

Volley提供了多个内置的Request子类,每个子类都有自己的parseNetworkResponse实现。下面我们来看几个常见的Request子类的实现:

5.3.1 StringRequest的实现
/**
 * StringRequest将响应数据解析为字符串
 */
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
        // 根据响应头中的字符集解析响应数据
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
        // 如果字符集不支持,使用默认字符集
        parsed = new String(response.data);
    }
    // 返回成功响应,包含解析后的字符串和缓存条目
    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
5.3.2 JsonObjectRequest的实现
/**
 * JsonObjectRequest将响应数据解析为JSONObject
 */
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
    try {
        // 将响应数据转换为字符串
        String jsonString = new String(
                response.data,
                HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
        
        // 将字符串解析为JSONObject
        return Response.success(
                new JSONObject(jsonString),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        // 处理编码异常
        return Response.error(new ParseError(e));
    } catch (JSONException je) {
        // 处理JSON解析异常
        return Response.error(new ParseError(je));
    }
}
5.3.3 JsonArrayRequest的实现
/**
 * JsonArrayRequest将响应数据解析为JSONArray
 */
@Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
    try {
        // 将响应数据转换为字符串
        String jsonString = new String(
                response.data,
                HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
        
        // 将字符串解析为JSONArray
        return Response.success(
                new JSONArray(jsonString),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        // 处理编码异常
        return Response.error(new ParseError(e));
    } catch (JSONException je) {
        // 处理JSON解析异常
        return Response.error(new ParseError(je));
    }
}
5.3.4 ImageRequest的实现
/**
 * ImageRequest将响应数据解析为Bitmap
 */
@Override
protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
    // 同步锁,确保BitmapFactory.Options只被配置一次
    synchronized (sDecodeLock) {
        try {
            // 解码响应数据为Bitmap
            return Response.success(
                    decodeBitmap(response.data),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (OutOfMemoryError e) {
            // 处理内存溢出错误
            VolleyLog.e("Caught OOM for %d byte image, url=%s",
                    response.data.length, getUrl());
            return Response.error(new ParseError(e));
        }
    }
}

/**
 * 解码字节数组为Bitmap
 */
private Bitmap decodeBitmap(byte[] data) {
    // 如果没有指定最大宽度和高度,直接解码
    if (mMaxWidth <= 0 && mMaxHeight <= 0) {
        return BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    }
    
    // 先获取图片尺寸,不加载整个图片
    mDecodeOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    int actualWidth = mDecodeOptions.outWidth;
    int actualHeight = mDecodeOptions.outHeight;
    
    // 计算目标尺寸
    int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
            actualWidth, actualHeight);
    int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
            actualHeight, actualWidth);
    
    // 计算inSampleSize
    mDecodeOptions.inSampleSize = calculateInSampleSize(actualWidth, actualHeight,
            desiredWidth, desiredHeight);
    
    // 加载实际图片
    mDecodeOptions.inJustDecodeBounds = false;
    Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    
    // 如果需要缩放图片到精确尺寸
    if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
            tempBitmap.getHeight() > desiredHeight)) {
        Bitmap bitmap = Bitmap.createScaledBitmap(tempBitmap,
                desiredWidth, desiredHeight, true);
        tempBitmap.recycle();
        return bitmap;
    } else {
        return tempBitmap;
    }
}

六、ResponseDelivery机制解析

6.1 ResponseDelivery接口的作用

ResponseDelivery接口定义了将响应数据传递到主线程的方法。它是Volley中响应分发机制的核心接口。

6.2 ResponseDelivery接口的源码分析

ResponseDelivery接口的源码如下:

/**
 * 响应分发接口,负责将响应和错误传递到主线程。
 */
public interface ResponseDelivery {
    /**
     * 将响应传递到主线程
     *
     * @param request 原始请求
     * @param response 响应数据
     */
    void postResponse(Request<?> request, Response<?> response);
    
    /**
     * 将响应传递到主线程,并在传递完成后执行回调
     *
     * @param request 原始请求
     * @param response 响应数据
     * @param runnable 传递完成后执行的回调
     */
    void postResponse(Request<?> request, Response<?> response, Runnable runnable);
    
    /**
     * 将错误传递到主线程
     *
     * @param request 原始请求
     * @param error 错误信息
     */
    void postError(Request<?> request, VolleyError error);
}

从上面的代码可以看出,ResponseDelivery接口定义了三个方法:

  • postResponse(Request<?>, Response<?>):将响应传递到主线程
  • postResponse(Request<?>, Response<?>, Runnable):将响应传递到主线程,并在传递完成后执行回调
  • postError(Request<?>, VolleyError):将错误传递到主线程

6.3 ResponseDelivery接口的实现类

Volley提供了一个默认的ResponseDelivery接口实现类:ExecutorDelivery。

ExecutorDelivery类的源码如下:

/**
 * 默认的响应分发器实现,使用Executor将响应和错误传递到主线程。
 */
public class ExecutorDelivery implements ResponseDelivery {
    /** 用于在主线程执行的Executor */
    private final Executor mResponsePoster;

    /**
     * 创建一个新的ExecutorDelivery实例
     *
     * @param handler 用于在主线程执行的Handler
     */
    public ExecutorDelivery(final Handler handler) {
        // 创建一个在主线程执行的Executor
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

    /**
     * 创建一个新的ExecutorDelivery实例,使用提供的Executor
     *
     * @param executor 用于执行的Executor
     */
    public ExecutorDelivery(Executor executor) {
        mResponsePoster = executor;
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        // 标记请求为已完成
        request.markDelivered();
        request.addMarker("post-response");
        
        // 创建一个响应分发Runnable
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

    @Override
    public void postError(Request<?> request, VolleyError error) {
        // 添加错误标记
        request.addMarker("post-error");
        
        // 解析错误响应
        Response<?> response = Response.error(error);
        
        // 创建一个响应分发Runnable
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

    /**
     * 用于在主线程分发响应的Runnable
     */
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // 如果请求已取消,不进行分发
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // 处理成功响应
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                // 处理错误响应
                mRequest.deliverError(mResponse.error);
            }

            // 如果响应是中间响应(例如,用于流式响应)
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                // 完成请求
                mRequest.finish("done");
            }

            // 如果有回调Runnable,执行它
            if (mRunnable != null) {
                mRunnable.run();
            }
        }
    }
}

从上面的代码可以看出,ExecutorDelivery类使用一个Executor来将响应和错误传递到主线程。在创建ExecutorDelivery实例时,我们可以传入一个Handler,这样所有的响应和错误都会通过这个Handler在主线程执行。

6.4 ResponseDelivery的调用流程

在Volley中,ResponseDelivery的调用发生在NetworkDispatcher类的run方法中。当网络请求完成并解析后,会调用ResponseDelivery的postResponse方法将响应传递到主线程:

/**
 * NetworkDispatcher的run方法
 */
@Override
public void run() {
    // 设置线程名称
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    
    // 循环处理请求
    while (true) {
        long startTimeMs = SystemClock.elapsedRealtime();
        Request<?> request;
        
        try {
            // 从请求队列中取出一个请求
            request = mQueue.take();
        } catch (InterruptedException e) {
            // 如果线程被中断,退出循环
            if (mQuit) {
                return;
            }
            continue;
        }
        
        try {
            request.addMarker("network-queue-take");
            
            // 如果请求已取消,跳过处理
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                continue;
            }
            
            // 标记请求开始网络处理
            addTrafficStatsTag(request);
            
            // 执行网络请求
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");
            
            // 如果服务器返回304状态码(未修改),并且我们有缓存条目,则使用缓存响应
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                continue;
            }
            
            // 解析网络响应
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");
            
            // 如果请求需要缓存,将响应写入缓存
            if (request.shouldCache() && response.cacheEntry != null) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }
            
            // 标记请求已交付
            request.markDelivered();
            
            // 将响应传递到主线程
            mDelivery.postResponse(request, response);
        } catch (VolleyError volleyError) {
            // 处理网络错误
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            parseAndDeliverNetworkError(request, volleyError);
        } catch (Exception e) {
            // 处理其他异常
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
            VolleyError volleyError = new VolleyError(e);
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            mDelivery.postError(request, volleyError);
        }
    }
}

七、deliverResponse方法解析

7.1 deliverResponse方法的作用

deliverResponse方法是Request类中的一个抽象方法,用于将解析后的响应数据传递给响应监听器。这个方法在主线程中被调用。

7.2 deliverResponse方法的源码分析

Request类中的deliverResponse方法定义如下:

/**
 * 将解析后的响应数据传递给响应监听器。
 * 子类可以重写此方法来实现自定义的响应分发逻辑。
 *
 * @param response 解析后的响应数据
 */
protected abstract void deliverResponse(T response);

从上面的代码可以看出,deliverResponse方法接收一个泛型参数T,表示解析后的响应数据。具体的响应分发逻辑由子类实现。

7.3 不同Request子类的deliverResponse实现

Volley提供的内置Request子类都实现了deliverResponse方法,将解析后的响应数据传递给我们设置的响应监听器。

下面是几个常见的Request子类的实现:

7.3.1 StringRequest的实现
/**
 * StringRequest的响应分发方法
 */
@Override
protected void deliverResponse(String response) {
    // 调用响应监听器的onResponse方法
    mListener.onResponse(response);
}
7.3.2 JsonObjectRequest的实现
/**
 * JsonObjectRequest的响应分发方法
 */
@Override
protected void deliverResponse(JSONObject response) {
    // 调用响应监听器的onResponse方法
    mListener.onResponse(response);
}
7.3.3 JsonArrayRequest的实现
/**
 * JsonArrayRequest的响应分发方法
 */
@Override
protected void deliverResponse(JSONArray response) {
    // 调用响应监听器的onResponse方法
    mListener.onResponse(response);
}
7.3.4 ImageRequest的实现
/**
 * ImageRequest的响应分发方法
 */
@Override
protected void deliverResponse(Bitmap response) {
    // 调用响应监听器的onResponse方法
    mListener.onResponse(response);
}

八、自定义响应解析器

8.1 为什么需要自定义响应解析器

虽然Volley提供了多种内置的Request类,可以处理常见的响应格式(如字符串、JSON、图片等),但在实际开发中,我们可能会遇到一些特殊的响应格式,或者需要对响应数据进行特殊处理。这时,就需要我们自定义响应解析器。

8.2 如何实现自定义响应解析器

实现自定义响应解析器的关键是创建一个继承自Request类的子类,并重写parseNetworkResponse方法。

下面是一个自定义响应解析器的示例,用于解析XML格式的响应数据:

/**
 * 自定义XML请求类,用于解析XML格式的响应数据
 */
public class XmlRequest extends Request<Document> {
    private final Response.Listener<Document> mListener;
    
    /**
     * 创建一个新的XmlRequest实例
     *
     * @param method 请求方法
     * @param url 请求的URL
     * @param listener 响应监听器
     * @param errorListener 错误监听器
     */
    public XmlRequest(int method, String url, Response.Listener<Document> listener,
                     Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    
    /**
     * 获取请求体的内容类型
     */
    @Override
    public String getBodyContentType() {
        return "application/xml; charset=utf-8";
    }
    
    /**
     * 解析网络响应数据为XML Document
     */
    @Override
    protected Response<Document> parseNetworkResponse(NetworkResponse response) {
        try {
            // 将响应数据转换为InputStream
            ByteArrayInputStream inputStream = new ByteArrayInputStream(response.data);
            
            // 创建DocumentBuilderFactory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            
            // 创建DocumentBuilder
            DocumentBuilder builder = factory.newDocumentBuilder();
            
            // 解析XML数据为Document
            Document document = builder.parse(inputStream);
            
            // 返回成功响应
            return Response.success(document, HttpHeaderParser.parseCacheHeaders(response));
        } catch (ParserConfigurationException e) {
            // 处理解析器配置异常
            return Response.error(new ParseError(e));
        } catch (SAXException e) {
            // 处理SAX解析异常
            return Response.error(new ParseError(e));
        } catch (IOException e) {
            // 处理IO异常
            return Response.error(new ParseError(e));
        }
    }
    
    /**
     * 将解析后的响应分发到主线程
     */
    @Override
    protected void deliverResponse(Document response) {
        mListener.onResponse(response);
    }
}

8.3 使用Gson等第三方库进行响应解析

除了手动解析响应数据外,我们还可以使用Gson、Jackson等第三方库来简化响应解析过程。

下面是一个使用Gson解析JSON响应的示例:

/**
 * 基于Gson的自定义请求类,用于将JSON响应解析为Java对象
 */
public class GsonRequest<T> extends Request<T> {
    private final Gson gson;
    private final Class<T> clazz;
    private final Response.Listener<T> listener;
    
    /**
     * 创建一个新的GsonRequest实例
     *
     * @param method 请求方法
     * @param url 请求的URL
     * @param clazz 响应数据类型
     * @param listener 响应监听器
     * @param errorListener 错误监听器
     */
    public GsonRequest(int method, String url, Class<T> clazz,
                      Response.Listener<T> listener,
                      Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.gson = new Gson();
        this.clazz = clazz;
        this.listener = listener;
    }
    
    /**
     * 解析网络响应数据为Java对象
     */
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            // 将响应数据转换为字符串
            String json = new String(
                    response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            
            // 使用Gson将JSON字符串解析为Java对象
            T result = gson.fromJson(json, clazz);
            
            // 返回成功响应
            return Response.success(result,
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            // 处理编码异常
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            // 处理JSON语法异常
            return Response.error(new ParseError(e));
        }
    }
    
    /**
     * 将解析后的响应分发到主线程

我们已经深入探讨了Volley成功响应解析处理的多个关键部分,接下来将从缓存处理、错误处理、性能优化以及与其他模块的关联等方面继续深入分析,进一步完善对Volley响应解析机制的理解。

九、响应解析与缓存处理的关联

9.1 缓存条目的生成与解析

在Volley中,当请求成功且响应可以被缓存时,会生成缓存条目(Cache.Entry)。这一过程与响应解析紧密相连,在parseNetworkResponse方法中解析响应数据的同时,也会处理缓存相关信息。

StringRequest为例,在其parseNetworkResponse方法中,通过HttpHeaderParser.parseCacheHeaders(response)来解析缓存头信息,并生成缓存条目:

/**
 * StringRequest将响应数据解析为字符串
 */
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
        // 根据响应头中的字符集解析响应数据
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
        // 如果字符集不支持,使用默认字符集
        parsed = new String(response.data);
    }
    // 返回成功响应,包含解析后的字符串和缓存条目
    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

HttpHeaderParser.parseCacheHeaders方法会从响应头中提取如Cache-ControlExpires等关键信息,构建Cache.Entry对象。其部分源码如下:

/**
 * 解析缓存头信息,生成缓存条目
 * @param headers 响应头
 * @return 缓存条目
 */
public static Cache.Entry parseCacheHeaders(Map<String, String> headers) {
    long now = System.currentTimeMillis();
    Map<String, String> serverHeaders = new HashMap<>();
    for (Map.Entry<String, String> entry : headers.entrySet()) {
        String headerName = entry.getKey();
        String headerValue = entry.getValue();
        if (headerName != null && headerValue != null) {
            serverHeaders.put(headerName.toLowerCase(), headerValue);
        }
    }

    // 提取过期时间
    long serverExpires = 0;
    String serverExpiresStr = serverHeaders.get("expires");
    if (serverExpiresStr != null) {
        serverExpires = DateUtils.parseDateAsEpoch(serverExpiresStr);
    }

    // 提取缓存控制信息
    String cacheControl = serverHeaders.get("cache-control");
    if (cacheControl == null) {
        cacheControl = "no-cache";
    }

    // 计算缓存有效时间
    long softExpire = now;
    long maxAge = 0;
    if (cacheControl.contains("max-age=")) {
        maxAge = Long.parseLong(cacheControl.substring(cacheControl.indexOf("max-age=") + 8));
        softExpire = now + maxAge * 1000;
    } else if (serverExpires > 0) {
        softExpire = serverExpires;
    }

    // 创建缓存条目
    Cache.Entry entry = new Cache.Entry();
    entry.data = null;
    entry.etag = serverHeaders.get("etag");
    entry.softTtl = softExpire;
    entry.ttl = softExpire;
    entry.serverDate = serverExpires;
    entry.responseHeaders = serverHeaders;
    return entry;
}

通过这样的处理,将响应头中的缓存相关信息转换为可用于后续缓存管理的Cache.Entry对象。

9.2 缓存响应的使用与更新

当再次发起相同请求时,Volley会先检查缓存。如果缓存有效,直接使用缓存响应,而无需再次进行网络请求和响应解析。在NetworkDispatcher类中,处理请求时会判断缓存是否可用:

// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// 如果服务器返回304状态码(未修改),并且我们有缓存条目,则使用缓存响应
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    request.finish("not-modified");
    continue;
}

当使用缓存响应时,会直接跳过网络请求和parseNetworkResponse的解析过程,将缓存中的数据传递给deliverResponse方法进行处理。

而当网络请求成功且响应可缓存时,会将新的响应数据和缓存条目写入缓存,实现缓存的更新。在NetworkDispatcher类中,若请求应缓存且有有效的缓存条目:

// 如果请求需要缓存,将响应写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
    mCache.put(request.getCacheKey(), response.cacheEntry);
    request.addMarker("network-cache-written");
}

通过这种方式,响应解析与缓存处理相互配合,提升了应用的性能和用户体验。

十、响应解析过程中的错误处理

10.1 解析异常的捕获与处理

parseNetworkResponse方法中,由于涉及到数据格式转换、编码处理等操作,可能会出现各种异常。以JsonObjectRequest为例,在解析JSON数据时,可能会遇到UnsupportedEncodingExceptionJSONException

/**
 * JsonObjectRequest将响应数据解析为JSONObject
 */
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
    try {
        // 将响应数据转换为字符串
        String jsonString = new String(
                response.data,
                HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
        
        // 将字符串解析为JSONObject
        return Response.success(
                new JSONObject(jsonString),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        // 处理编码异常
        return Response.error(new ParseError(e));
    } catch (JSONException je) {
        // 处理JSON解析异常
        return Response.error(new ParseError(je));
    }
}

当捕获到异常时,会创建一个ParseError类型的VolleyError对象,并通过Response.error方法创建包含错误信息的Response对象。这样,在后续的处理中,错误信息会被传递到主线程,由deliverError方法进行处理。

10.2 错误响应的传递与展示

NetworkDispatcher类中,当解析过程中出现异常或网络请求失败时,会调用parseAndDeliverNetworkError方法处理错误:

catch (VolleyError volleyError) {
    // 处理网络错误
    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    parseAndDeliverNetworkError(request, volleyError);
}

parseAndDeliverNetworkError方法会进一步处理错误信息,并通过mDelivery.postError将错误传递到主线程:

private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
    error = mErrorProcessor.process(error);
    mDelivery.postError(request, error);
}

在主线程中,ExecutorDelivery类的postError方法会被调用:

@Override
public void postError(Request<?> request, VolleyError error) {
    // 添加错误标记
    request.addMarker("post-error");
    
    // 解析错误响应
    Response<?> response = Response.error(error);
    
    // 创建一个响应分发Runnable
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}

最终,ResponseDeliveryRunnablerun方法会调用request.deliverError(mResponse.error),将错误信息传递给我们设置的ErrorListener,在应用中展示错误提示,如:

// 创建请求时设置错误监听器
StringRequest request = new StringRequest(Request.Method.GET, url,
    new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            // 处理响应
        }
    },
    new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // 展示错误提示
            Toast.makeText(context, "请求失败: " + error.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });

十一、响应解析的性能优化

11.1 减少不必要的解析操作

在实际应用中,有些响应数据可能并非每次都需要完整解析。例如,当只需要获取响应中的部分字段时,可以在parseNetworkResponse方法中进行选择性解析,避免对整个响应数据进行复杂的解析操作。

以解析JSON数据为例,如果只需要获取其中的一个字段:

/**
 * 自定义JSON请求类,只解析特定字段
 */
public class CustomJsonRequest extends Request<String> {
    private final Response.Listener<String> mListener;
    private static final String TARGET_FIELD = "specific_field";

    public CustomJsonRequest(String url, Response.Listener<String> listener,
                             Response.ErrorListener errorListener) {
        super(Request.Method.GET, url, errorListener);
        mListener = listener;
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(
                    response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            JSONObject jsonObject = new JSONObject(jsonString);
            // 只提取特定字段
            String targetValue = jsonObject.optString(TARGET_FIELD);
            return Response.success(targetValue, HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }
}

通过这种方式,减少了不必要的解析操作,提高了响应解析的效率。

11.2 合理使用内存

在响应解析过程中,尤其是处理较大的响应数据(如图片、大JSON数据等)时,内存管理至关重要。对于图片响应的解析,ImageRequest类在decodeBitmap方法中通过设置BitmapFactory.OptionsinSampleSize来控制图片的加载尺寸,避免一次性加载过大的图片导致内存溢出:

/**
 * 解码字节数组为Bitmap
 */
private Bitmap decodeBitmap(byte[] data) {
    // 如果没有指定最大宽度和高度,直接解码
    if (mMaxWidth <= 0 && mMaxHeight <= 0) {
        return BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    }
    
    // 先获取图片尺寸,不加载整个图片
    mDecodeOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    int actualWidth = mDecodeOptions.outWidth;
    int actualHeight = mDecodeOptions.outHeight;
    
    // 计算目标尺寸
    int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
            actualWidth, actualHeight);
    int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
            actualHeight, actualWidth);
    
    // 计算inSampleSize
    mDecodeOptions.inSampleSize = calculateInSampleSize(actualWidth, actualHeight,
            desiredWidth, desiredHeight);
    
    // 加载实际图片
    mDecodeOptions.inJustDecodeBounds = false;
    Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions);
    
    // 如果需要缩放图片到精确尺寸
    if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
            tempBitmap.getHeight() > desiredHeight)) {
        Bitmap bitmap = Bitmap.createScaledBitmap(tempBitmap,
                desiredWidth, desiredHeight, true);
        tempBitmap.recycle();
        return bitmap;
    } else {
        return tempBitmap;
    }
}

对于JSON等数据的解析,也可以及时释放不再使用的对象引用,避免内存占用过高。

十二、响应解析与Volley其他模块的交互

12.1 与请求队列的交互

请求队列(RequestQueue)负责管理和调度请求。在响应解析完成后,通过ResponseDelivery机制将响应数据传递到主线程,此时请求队列会标记请求已完成相关操作。在ExecutorDelivery类的ResponseDeliveryRunnable中:

@Override
public void run() {
    // 如果请求已取消,不进行分发
    if (mRequest.isCanceled()) {
        mRequest.finish("canceled-at-delivery");
        return;
    }

    // 处理成功响应
    if (mResponse.isSuccess()) {
        mRequest.deliverResponse(mResponse.result);
    } else {
        // 处理错误响应
        mRequest.deliverError(mResponse.error);
    }

    // 如果响应是中间响应(例如,用于流式响应)
    if (mResponse.intermediate) {
        mRequest.addMarker("intermediate-response");
    } else {
        // 完成请求
        mRequest.finish("done");
    }

    // 如果有回调Runnable,执行它
    if (mRunnable != null) {
        mRunnable.run();
    }
}

通过mRequest.finish方法通知请求队列该请求已处理完毕,请求队列可以进行后续的调度和管理操作,如处理队列中的下一个请求。

12.2 与网络模块的协作

网络模块(Network)负责实际的网络请求发送和响应接收,获取到的NetworkResponse是响应解析的基础。在NetworkDispatcher类中,网络模块执行请求后返回NetworkResponse,然后将其传递给parseNetworkResponse方法进行解析:

// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// 解析网络响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");

网络模块获取到的响应数据质量、完整性等直接影响到响应解析的结果,两者紧密协作,共同完成网络请求到响应处理的整个流程。