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

Android中JSONObjectRequest调用不稳定问题求助

问题排查与解决方案

核心问题分析

从代码和描述来看,getPictureOfAllIngredients请求间歇性失效、仅在MainActivity的onCreate二次触发时执行的问题,主要集中在异步请求逻辑错误、RequestQueue滥用、生命周期绑定漏洞这三个核心点:

1. 异步请求后的同步读取逻辑错误

initDataIngredients方法中,发起Volley异步请求后立刻读取SharedPreferences:

ingredientService.getPictureOfAllIngredients(/*回调*/);
data = (ArrayList<IngredientEntity>) ObjectSerializer.deserialize(sharedPreferences.getString("ingredients", null));

Volley请求是异步执行的,此时回调还未触发,SP内的ingredients值并未更新,data依然为空。但更关键的是:第一次进入MainActivity时,请求实际已发起,但你在回调完成前就读取SP,导致后续逻辑未感知到请求结果;当从其他页面返回、onCreate二次触发时,SP可能已被第一次请求延迟写入,或者条件判断再次触发请求,才让你误以为请求此时才执行。

2. 重复创建RequestQueue导致请求丢失

两个请求方法中,每次发起请求都新建RequestQueue queue = Volley.newRequestQueue(context);,违反了Volley的设计规范:

  • RequestQueue应全局单例使用,重复创建会导致请求分散在多个队列中,甚至被系统回收的队列丢弃
  • 额外消耗系统资源,加剧网络请求的不稳定性

3. 生命周期与请求的绑定漏洞

onCreate被调用两次,说明MainActivity可能被系统回收后重建,或启动模式配置异常。第一次创建时发起的请求,可能因Activity上下文销毁导致回调无法执行(比如Toast因上下文失效不显示),你无法感知到请求的实际状态;第二次创建时,SP数据的变化触发条件判断,请求才被正常执行并感知到。

4. 错误处理不完整

getPictureOfAllIngredients的ErrorListener仅用Toast提示,但如果Activity处于后台或已销毁,Toast不会显示,你无法看到请求失败的具体原因(如超时、服务器错误),导致误以为请求未被调用。


具体修复步骤

1. 将RequestQueue重构为全局单例

创建单例类统一管理请求队列,避免重复创建:

public class VolleySingleton {
    private static VolleySingleton instance;
    private RequestQueue requestQueue;
    private static Context ctx;

    private VolleySingleton(Context context) {
        ctx = context;
        requestQueue = getRequestQueue();
    }

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

    public RequestQueue getRequestQueue() {
        if (requestQueue == null) {
            // 使用ApplicationContext避免内存泄漏
            requestQueue = Volley.newRequestQueue(ctx.getApplicationContext());
        }
        return requestQueue;
    }

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

修改两个请求方法,使用单例队列:

// 修复getPictureOfAllIngredients方法
public void getPictureOfAllIngredients(final GetAllResponseListener callback){
    String url =""; // 替换为实际接口地址
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    try {
                        String status = response.getString("status");
                        String message = response.getString("message");
                        if(status.equals("success")){
                            callback.onResponse(response.toString());
                        }else{
                            callback.onError("请求失败:" + message);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                        callback.onError("解析响应失败:" + e.getMessage());
                    }
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            String errorMsg;
            if (error instanceof NetworkError) {
                errorMsg = "网络连接失败";
            } else if (error instanceof ServerError) {
                errorMsg = "服务器错误";
            } else if (error instanceof AuthFailureError) {
                errorMsg = "认证失败";
            } else if (error instanceof ParseError) {
                errorMsg = "解析响应失败";
            } else if (error instanceof TimeoutError) {
                errorMsg = "请求超时";
            } else {
                errorMsg = error.toString();
            }
            // 使用ApplicationContext显示Toast,避免Activity销毁导致的异常
            Toast.makeText(context.getApplicationContext(), errorMsg, Toast.LENGTH_SHORT).show();
            callback.onError(errorMsg);
        }
    });
    // 添加请求标签,便于生命周期管理
    jsonObjectRequest.setTag("GET_INGREDIENTS");
    VolleySingleton.getInstance(context).addToRequestQueue(jsonObjectRequest);
}

// getAllRecipes方法执行同样修改,使用单例队列并添加标签

2. 修复initDataIngredients的异步逻辑

移除请求后立刻读取SP的代码,将所有依赖请求结果的逻辑放在回调中执行:

public void initDataIngredients() throws IOException {
    data = (ArrayList<IngredientEntity>) ObjectSerializer.deserialize(sharedPreferences.getString("ingredients", null));

    if( data == null || data.size() == 0 ){
        ingredientService.getPictureOfAllIngredients(new IngredientService.GetAllResponseListener() {
            @Override
            public void onError(String message) {
                Toast.makeText(context, "加载食材数据失败:" + message, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(String message) {
                mapper = new RecipeMapper();
                data = mapper.parseIngredientDatabaseEntityToRegularEntity(message);
                try {
                    editor.putString("ingredients", ObjectSerializer.serialize(data)).apply();
                    // 数据加载完成后执行UI更新等逻辑
                    updateIngredientUI();
                } catch (IOException e) {
                    e.printStackTrace();
                    Toast.makeText(context, "保存食材数据失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
        });
    } else {
        // SP已有数据时直接更新UI
        updateIngredientUI();
    }
}

注意:所有需要使用data的逻辑必须放在回调或else分支中,避免异步请求未完成时读取无效数据。

3. 优化MainActivity的生命周期管理

  • 在AndroidManifest.xml中设置合理的启动模式,避免不必要的Activity重建:
<activity
    android:name=".MainActivity"
    android:launchMode="singleTop"> <!-- 或根据业务需求使用singleTask -->
    <!-- 其他配置 -->
</activity>
  • 在onDestroy中取消未完成的请求,避免内存泄漏和无效请求:
@Override
protected void onDestroy() {
    super.onDestroy();
    VolleySingleton.getInstance(this).getRequestQueue().cancelAll("GET_INGREDIENTS");
}

验证方法

  1. 首次启动App,观察请求是否正常发起、数据是否正确加载并显示
  2. 跳转到其他页面后返回,检查请求执行状态和数据一致性
  3. 查看Logcat日志,确认是否有请求失败的具体原因

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 19:42:53