You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Volley中Listener与Response<ByteArray>报错问题求助

修复Volley自定义ByteArrayRequest的Listener和响应类型错误

核心问题分析

  1. 父类选择错误:原代码继承JsonObjectRequest(泛型为JSONObject),但我们需要处理ByteArray类型响应,应直接继承Request<ByteArray>。
  2. Listener类型不匹配:构造函数中Response.Listener<Unit>与Request<ByteArray>的泛型不兼容,需统一为Response.Listener<ByteArray>(或按需调整为File类型)。
  3. parseNetworkResponse返回值错误:返回Response.success(Unit, ...)不符合Response<ByteArray>的类型要求,需返回对应泛型的成功响应。
  4. 权限判断冗余:使用getExternalFilesDir()存储文件属于应用私有外部存储,Android 10+无需WRITE_EXTERNAL_STORAGE权限,可简化权限检查逻辑。
  5. Activity中语法错误:errorListener的Lambda表达式语法错误(error -应改为error ->)。

修复后的ByteArrayRequest.kt

import android.content.Context
import android.os.Environment
import com.android.volley.NetworkResponse
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.VolleyError
import com.android.volley.toolbox.HttpHeaderParser
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

class ByteArrayRequest(
    private val context: Context,
    private val fileName: String,
    method: Int,
    url: String,
    private val jsonRequest: JSONObject?,
    listener: Response.Listener<ByteArray>,
    errorListener: Response.ErrorListener
) : Request<ByteArray>(method, url, errorListener) {
    private val headers: MutableMap<String, String> = HashMap()
    private val responseListener: Response.Listener<ByteArray> = listener

    fun addHeader(key: String, value: String) {
        headers[key] = value
    }

    override fun parseNetworkResponse(response: NetworkResponse): Response<ByteArray> {
        val data = response.data
        // 保存文件到应用私有外部存储
        val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
        try {
            FileOutputStream(file).use { output ->
                output.write(data)
            }
            // 通知监听器下载完成,同时返回字节数组
            responseListener.onResponse(data)
        } catch (e: IOException) {
            return Response.error(VolleyError(e))
        }
        // 返回字节数组响应,用于缓存或后续处理
        return Response.success(data, HttpHeaderParser.parseCacheHeaders(response))
    }

    // 重写getBody,发送JSON请求体
    override fun getBody(): ByteArray? {
        return jsonRequest?.toString()?.toByteArray(charset("UTF-8"))
    }

    override fun getBodyContentType(): String {
        return "application/json; charset=utf-8"
    }

    override fun getHeaders(): MutableMap<String, String> {
        return headers
    }
}

修复后的Activity代码

import android.widget.Toast
import com.android.volley.DefaultRetryPolicy
import com.android.volley.Request
import com.android.volley.Volley
import org.json.JSONException
import org.json.JSONObject

fun submitdata(url: String) {
    val js = JSONObject()
    try {
        js.put("year", financialyear!!.selectedItem.toString().toInt())
        js.put("month", month!!.selectedItem.toString().toInt())
        js.put("emp_number", emp_num)
        Log.d("volley","js:${js}")
    } catch (e: JSONException) {
        e.printStackTrace()
    }

    val byteArrayRequest = ByteArrayRequest(
        context = applicationContext,
        fileName = "report.pdf",
        method = Request.Method.POST,
        url = url,
        jsonRequest = js,
        listener = { byteArray ->
            // 下载完成,可在这里处理字节数组或直接打开文件
            val file = File(applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "report.pdf")
            Toast.makeText(applicationContext, "文件已保存至: ${file.path}", Toast.LENGTH_LONG).show()
            // 如需预览PDF,可在这里启动PDF查看器
        },
        errorListener = { error ->
            Log.d("volley","Error:${error.message}")
            Toast.makeText(applicationContext, "下载失败: ${error.message}", Toast.LENGTH_SHORT).show()
        }
    )

    byteArrayRequest.retryPolicy = DefaultRetryPolicy(
        100000,
        DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
        DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
    )
    byteArrayRequest.addHeader("Authorization", "Bearer " + SharedPrefrence.getApitoken(applicationContext))
    byteArrayRequest.addHeader("x-api-key", SharedPrefrence.getApikey(applicationContext)!!)

    val requestQueue = Volley.newRequestQueue(applicationContext)
    requestQueue.add(byteArrayRequest)
}

额外说明

  • 如果不需要返回字节数组,只想在下载完成后通知文件路径,可以将ByteArrayRequest的泛型改为File,并在parseNetworkResponse中返回Response.success(file, ...),同时调整Listener为Response.Listener<File>。
  • Android 10及以上版本,getExternalFilesDir()路径无需权限,若需兼容Android 9及以下,可保留权限检查逻辑,但注意动态申请权限。

内容的提问来源于stack exchange,提问作者jyothihayathi g

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:05:58