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

从远程MySQL获取JSON时触发IllegalStateException异常问题排查

问题:切换MySQL查询表后触发OkHttp ResponseBody closed异常

我原本用Android代码从远程MySQL数据库获取tb_tickets_reparacion表的数据时运行正常,但把PHP中的SQL语句改成查询tb_ventas表后,触发了如下异常:

2019-10-09 12:12:13.448 27800-27875/? E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #2
Process: com.mpidesarrollo.jogua, PID: 27800
java.lang.RuntimeException: An error occurred while executing doInBackground()
    at android.os.AsyncTask$3.done(AsyncTask.java:353)
    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
    at java.util.concurrent.FutureTask.run(FutureTask.java:271)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
    at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.IllegalStateException: closed
    at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:408)
    at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:402)
    at okhttp3.internal.Util.bomAwareCharset(Util.java:432)
    at okhttp3.ResponseBody.string(ResponseBody.java:174)
    at com.mpidesarrollo.jogua.activity.MisComprasFragment$4.doInBackground(MisComprasFragment.java:271)
    at com.mpidesarrollo.jogua.activity.MisComprasFragment$4.doInBackground(MisComprasFragment.java:251)
    at android.os.AsyncTask$2.call(AsyncTask.java:333)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    ... 4 more

相关代码

Android获取数据代码:

private void getComprasFromDB(int id) {
    Log.d("HOLA ADDRESSES", "DIRECCION LEIDA SIN BUSCAR: ");
    compras.clear();
    AsyncTask<Integer, Void, Void> asyncTask = new AsyncTask<Integer, Void, Void>() {
        @Override
        protected Void doInBackground(Integer... addressesIds) {
            Log.d("HOLA ADDRESSES", "DIRECCION LEIDA SIN BUSCAR: "+id_usuario);
            OkHttpClient client = new OkHttpClient();
            okhttp3.Request request = new okhttp3.Request.Builder()
                    .url("https://.../mis_compras.php?id="+id_usuario)
                    .build();
            try {
                okhttp3.Response response = client.newCall(request).execute();
                JSONArray array = new JSONArray(response.body().string());
                for (int i = 0; i < array.length(); i++) {
                    JSONObject object = array.getJSONObject(i);
                    Log.d("HOLA PERFIL", "mis compras response: " + response.body().string());
                    Compras compra = new Compras(
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas"),
                            object.getString("id_ventas")
                    );
                    compras.add(compra);
                }
            } catch (IOException e) {
                Log.d("HOLA ADDRESSES", "DIRECCION LEIDA SIN BUSCAR:IO "+e);
                e.printStackTrace();
            } catch (JSONException e) {
                Log.d("HOLA ADDRESSES", "DIRECCION LEIDA SIN BUSCAR:json "+e);
                e.printStackTrace();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void aVoid) {
            adapter.notifyDataSetChanged();
            if (compras.size() == 0){
                txtNohaytickets.setVisibility(View.VISIBLE);
            } else {
                txtNohaytickets.setVisibility(View.GONE);
            }
        }
    };
    asyncTask.execute(id);
}

原本正常的PHP查询代码:

$con->set_charset("utf8");
if (mysqli_connect_errno()) {
    echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$id = mysqli_real_escape_string($con,$_GET["id"]);
$sql= "Select tr.*,tc.nombre as nombre_categoria,tm.nombre as nombre_marca, modelos.nombre as nombre_modelo from tb_tickets_reparacion tr LEFT JOIN tb_categorias tc ON tc.id = tr.categoria LEFT JOIN tb_marcas tm ON tm.id = tr.marca LEFT JOIN tb_modelos modelos ON modelos.id = tr.modelo WHERE id_cliente = '" .$id. "' ";
$result = mysqli_query($con ,$sql);
while ($row = mysqli_fetch_assoc($result)) {
    $array[] = $row;
}
header('Content-Type:Application/json');
echo json_encode($array);
mysqli_free_result($result);
mysqli_close($con);

修改后触发异常的PHP查询代码:

$con->set_charset("utf8");
if (mysqli_connect_errno()) {
    echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$id = mysqli_real_escape_string($con,$_GET["id"]);
$sql= "Select tr.* from tb_ventas tr ";
$result = mysqli_query($con ,$sql);
while ($row = mysqli_fetch_assoc($result)) {
    $array[] = $row;
}
header('Content-Type:Application/json');
echo json_encode($array);
mysqli_free_result($result);
mysqli_close($con);

问题原因与解决方案

核心问题:重复读取ResponseBody流

OkHttp的ResponseBody.string()方法会一次性读取流中的所有数据,然后直接关闭流。你的代码里犯了一个关键错误:

  1. 先调用JSONArray array = new JSONArray(response.body().string());读取了一次流并关闭
  2. 紧接着在循环里又调用Log.d("HOLA PERFIL", "mis compras response: " + response.body().string());,这时候流已经关闭,直接触发IllegalStateException: closed异常

之前查询tickets表时可能因为日志代码没被执行到(比如数组长度为0)或者其他巧合没暴露问题,但切换表后数据正常返回,循环执行到日志代码就触发了异常。

修复步骤:

  1. 先把响应内容读取到变量,重复使用该变量
    修改Android代码中的doInBackground部分,先把响应内容存入字符串变量,后续解析和日志都用这个变量:

    try {
        okhttp3.Response response = client.newCall(request).execute();
        // 先读取一次响应内容到变量,避免重复读取流
        String responseContent = response.body().string();
        Log.d("HOLA PERFIL", "mis compras response: " + responseContent);
        JSONArray array = new JSONArray(responseContent);
        for (int i = 0; i < array.length(); i++) {
            JSONObject object = array.getJSONObject(i);
            Compras compra = new Compras(
                    // 这里要改成tb_ventas表对应的字段,别全用id_ventas
                    object.getString("id_ventas"),
                    object.getString("campo2"),
                    object.getString("campo3"),
                    // ... 其他字段
            );
            compras.add(compra);
        }
    } catch (IOException | JSONException e) {
        Log.e("HOLA ADDRESSES", "Error fetching data", e);
        e.printStackTrace();
    }
    
  2. 修正Compras构造函数的参数
    你现在的构造函数所有参数都是object.getString("id_ventas"),这明显是测试代码,一定要改成tb_ventas表中实际存在的字段,不然就算解析成功也拿不到正确数据。

  3. PHP端的健壮性优化
    修改后的PHP代码如果查询不到数据,$array会未定义,导致json_encode返回null,Android端解析JSON会报错。建议先初始化空数组:

    $array = []; // 初始化空数组,避免未定义问题
    while ($row = mysqli_fetch_assoc($result)) {
        $array[] = $row;
    }
    header('Content-Type:Application/json');
    echo json_encode($array);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:50:50