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

Volley解析JSON字符串数组至全局变量后为空,多选对话框失效

问题分析与解决方案

嘿,这是异步网络请求的经典坑!咱们先搞清楚为什么category数组在onCreateDialog里是null:

核心原因:Volley请求的异步特性

你在onAttach里调用了getJobArray(),但Volley的网络请求是异步执行的——也就是说,onCreateDialog会在网络请求返回数据、填充category数组之前就被系统调用了。这时候category还没被初始化,自然就是null,连长度都打印不出来。

解决方案一:数据加载完成后再构建对话框

这个方案比较直接,先给用户显示一个加载中的对话框,等网络请求完成拿到数据后,再替换成多选对话框:

修改后的代码:

首先调整onCreateDialog返回加载对话框:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    ProgressDialog progressDialog = new ProgressDialog(getActivity());
    progressDialog.setMessage("加载分类中...");
    progressDialog.setCancelable(false);
    return progressDialog;
}

然后在getJobArray的onResponse方法里,完成数组填充后调用更新对话框的方法:

private void getJobArray(){
    String url = "http://api_staging.jab.poweredby.cnddts.at/mobile/metadata/categories?portal=1";
    JsonObjectRequest request = new JsonObjectRequest(com.android.volley.Request.Method.GET, url, null, 
        new com.android.volley.Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONArray jsonArray = response.getJSONArray("categories");
                    category = new String[jsonArray.length()];
                    Log.d(TAG, "onResponse: 开始填充数组");
                    Log.d(TAG, "onResponse: 数组长度" + jsonArray.length());
                    for(int i = 0; i < jsonArray.length(); i++){
                        JSONObject categories = jsonArray.getJSONObject(i);
                        category[i] = categories.getString("name");
                        Log.d(TAG, "onResponse: 填充内容 " + category[i]);
                    }
                    // 数据加载完成,更新对话框
                    updateDialogWithCategories();
                } catch (JSONException e) {
                    e.printStackTrace();
                    Log.d(TAG, "onResponse: JSON解析出错");
                    // 加载失败,提示用户并关闭对话框
                    dismiss();
                    Toast.makeText(mContext, "分类加载失败", Toast.LENGTH_SHORT).show();
                }
            }
        }, 
        new com.android.volley.Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
                dismiss();
                Toast.makeText(mContext, "网络请求失败", Toast.LENGTH_SHORT).show();
            }
        });
    mRequestQ.add(request);
}

// 构建多选对话框并替换当前加载框
private void updateDialogWithCategories() {
    final ArrayList<String> selectedItemList = new ArrayList<>();
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("选择分类")
            .setMultiChoiceItems(category, null, new DialogInterface.OnMultiChoiceClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i, boolean b) {
                    if(b){
                        selectedItemList.add(category[i]);
                    }else{
                        selectedItemList.remove(category[i]);
                    }
                }
            })
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    mListener.onPositiveClicked(category,selectedItemList);
                }
            })
            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    mListener.onNegativeButtonClicked();
                }
            });
    // 替换当前对话框
    Dialog newDialog = builder.create();
    setShowsDialog(false);
    dismiss();
    setShowsDialog(true);
    newDialog.show();
}

解决方案二:用ViewModel+LiveData管理异步数据(更推荐)

如果想让代码更符合Android Jetpack架构,避免内存泄漏,用ViewModel+LiveData是更好的选择:

第一步:创建CategoryViewModel

public class CategoryViewModel extends ViewModel {
    private MutableLiveData<String[]> categoryLiveData = new MutableLiveData<>();
    private RequestQueue requestQueue;

    public void loadCategories(Context context) {
        requestQueue = Volley.newRequestQueue(context);
        String url = "http://api_staging.jab.poweredby.cnddts.at/mobile/metadata/categories?portal=1";
        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
                response -> {
                    try {
                        JSONArray jsonArray = response.getJSONArray("categories");
                        String[] category = new String[jsonArray.length()];
                        for(int i = 0; i < jsonArray.length(); i++){
                            JSONObject categories = jsonArray.getJSONObject(i);
                            category[i] = categories.getString("name");
                        }
                        categoryLiveData.setValue(category);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                },
                error -> error.printStackTrace());
        requestQueue.add(request);
    }

    public LiveData<String[]> getCategoryLiveData() {
        return categoryLiveData;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        // 页面销毁时取消所有请求,避免内存泄漏
        if(requestQueue != null) {
            requestQueue.cancelAll(request -> true);
        }
    }
}

第二步:修改DialogFragment使用ViewModel

public class MultipleChoiceDialogFragment extends DialogFragment {
    private static String TAG = "xd";
    private onMultiChoiceListener mListener;
    private CategoryViewModel viewModel;

    public interface onMultiChoiceListener{
        void onPositiveClicked(String[] list,ArrayList<String> selectedItemList);
        void onNegativeButtonClicked();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mListener = (onMultiChoiceListener) context;
        }catch (Exception e){
            throw new ClassCastException(getActivity() + " 必须实现onMultiChoiceListener接口");
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(this).get(CategoryViewModel.class);
        viewModel.loadCategories(getContext());
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ProgressDialog progressDialog = new ProgressDialog(getActivity());
        progressDialog.setMessage("加载分类中...");
        progressDialog.setCancelable(false);

        // 观察数据变化,数据就绪后更新对话框
        viewModel.getCategoryLiveData().observe(this, categories -> {
            if(categories != null) {
                updateDialogWithCategories(categories);
            }
        });

        return progressDialog;
    }

    private void updateDialogWithCategories(String[] category) {
        final ArrayList<String> selectedItemList = new ArrayList<>();
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("选择分类")
                .setMultiChoiceItems(category, null, (dialogInterface, i, b) -> {
                    if(b){
                        selectedItemList.add(category[i]);
                    }else{
                        selectedItemList.remove(category[i]);
                    }
                })
                .setPositiveButton("确定", (dialogInterface, i) -> {
                    mListener.onPositiveClicked(category,selectedItemList);
                })
                .setNegativeButton("取消", (dialogInterface, i) -> {
                    mListener.onNegativeButtonClicked();
                });
        Dialog newDialog = builder.create();
        setShowsDialog(false);
        dismiss();
        setShowsDialog(true);
        newDialog.show();
    }
}

关键提醒

  • 永远记住:网络请求是异步的,不要假设UI回调(比如onCreateDialog)执行时,异步任务已经完成
  • 方案二更适合长期维护,ViewModel会自动管理生命周期,避免内存泄漏
  • 一定要处理网络请求失败的情况,给用户友好的提示,不要让用户一直等加载框

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:26:41