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

如何按顺序调用双URL API,展示农贸市场名称与地址?

解决农贸市场数据加载的异步时序问题

你的核心问题在于异步请求的执行时序不匹配:在getCurrentDetails方法中,你循环发起获取市场详情的异步OkHttp请求,但这些请求还未返回结果时,就已经创建了Market对象并初始化了Adapter——此时marketAddress还没有被赋值(异步回调还没执行),导致最终ListView里的地址为空。另外marketAddressArrayList未初始化,直接调用add会触发空指针异常。

下面提供两种可行的解决方案,你可以根据需求选择:


方案1:等待所有详情请求完成后统一更新UI

适合需要所有数据加载完成后再一次性展示的场景,用CountDownLatch来跟踪所有异步请求的完成状态。

修改步骤与代码:

  1. 先初始化marketAddressArrayList(在onCreate中):
marketAddressArrayList = new ArrayList<>();
  1. 修改getCurrentDetails方法:
private void getCurrentDetails(String jsonData) throws JSONException {
    JSONObject usdaJSON = new JSONObject(jsonData);
    JSONArray resultsJSON = usdaJSON.getJSONArray("results");
    final List<Market> marketList = new ArrayList<>();
    // 计数器初始值等于市场数量,每完成一个请求就减1
    final CountDownLatch latch = new CountDownLatch(resultsJSON.length());

    // 先收集所有市场的基础信息(名称、ID)
    for(int i = 0; i < resultsJSON.length(); i++){
        JSONObject marketJSON = resultsJSON.getJSONObject(i);
        String marketname = marketJSON.getString("marketname");
        String id = marketJSON.getString("id");
        // 地址先设为null,后续再填充
        Market market = new Market(marketname, id, null);
        marketList.add(market);
    }

    // 逐个发起详情请求
    for(int i = 0; i < marketList.size(); i++){
        final Market currentMarket = marketList.get(i);
        String id = currentMarket.getId();

        // 复用全局OkHttpClient实例,不要每次循环创建新对象
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://search.ams.usda.gov/farmersmarkets/v1/data.svc/mktDetail?id=" + id)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                e.printStackTrace();
                latch.countDown(); // 失败也要减少计数器
            }

            @Override
            public void onResponse(Response response) throws IOException {
                try {
                    if (response.isSuccessful()) {
                        String marketDetailsJsonData = response.body().string();
                        JSONObject detailsJSON = new JSONObject(marketDetailsJsonData);
                        JSONObject marketDetailsJSON = detailsJSON.getJSONObject("marketdetails");
                        String address = marketDetailsJSON.getString("Address");
                        currentMarket.setMarketAddress(address); // 更新当前市场的地址
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 无论成功失败,计数器减1
                }
            }
        });
    }

    // 开启线程等待所有请求完成,再更新UI
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                latch.await(); // 阻塞直到所有请求完成
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        MarketAdapter adapter = new MarketAdapter(MainActivity.this, new ArrayList<>(marketList));
                        mListView.setAdapter(adapter);
                        // 打印测试数据
                        for(Market market : marketList){
                            Log.d(TAG, market.getMarketname() + " - " + market.getMarketAddress());
                        }
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

方案2:加载一个更新一个,实时刷新UI

适合需要逐步展示数据的场景,先初始化Adapter显示基础信息,每获取到一个市场的地址就更新对应项并通知Adapter刷新。

修改步骤与代码:

  1. 在Activity中添加全局变量:
private MarketAdapter marketAdapter;
private List<Market> marketList;
  1. 在onCreate中初始化集合:
marketAddressArrayList = new ArrayList<>();
marketList = new ArrayList<>();
  1. 修改getCurrentDetails方法:
private void getCurrentDetails(String jsonData) throws JSONException {
    JSONObject usdaJSON = new JSONObject(jsonData);
    JSONArray resultsJSON = usdaJSON.getJSONArray("results");

    // 先初始化市场列表,地址显示"加载中"
    for(int i = 0; i < resultsJSON.length(); i++){
        JSONObject marketJSON = resultsJSON.getJSONObject(i);
        String marketname = marketJSON.getString("marketname");
        String id = marketJSON.getString("id");
        Market market = new Market(marketname, id, "Loading...");
        marketList.add(market);
    }
    // 初始化Adapter并设置到ListView
    marketAdapter = new MarketAdapter(this, new ArrayList<>(marketList));
    mListView.setAdapter(marketAdapter);

    // 逐个发起详情请求
    for(int i = 0; i < marketList.size(); i++){
        final int index = i;
        final Market currentMarket = marketList.get(i);
        String id = currentMarket.getId();

        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://search.ams.usda.gov/farmersmarkets/v1/data.svc/mktDetail?id=" + id)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                e.printStackTrace();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        currentMarket.setMarketAddress("加载失败");
                        marketAdapter.notifyDataSetChanged(); // 通知Adapter更新
                    }
                });
            }

            @Override
            public void onResponse(Response response) throws IOException {
                try {
                    if (response.isSuccessful()) {
                        String marketDetailsJsonData = response.body().string();
                        JSONObject detailsJSON = new JSONObject(marketDetailsJsonData);
                        JSONObject marketDetailsJSON = detailsJSON.getJSONObject("marketdetails");
                        final String address = marketDetailsJSON.getString("Address");
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                currentMarket.setMarketAddress(address);
                                marketAdapter.notifyDataSetChanged(); // 更新单个项
                            }
                        });
                    } else {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                currentMarket.setMarketAddress("加载失败");
                                marketAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentMarket.setMarketAddress("加载失败");
                            marketAdapter.notifyDataSetChanged();
                        }
                    });
                }
            }
        });
    }
}

额外注意事项

  1. OkHttpClient复用:不要在循环中每次创建新的OkHttpClient,建议全局初始化一个实例,提升性能。
  2. 权限处理:确保Manifest中添加了定位权限(ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION),并在运行时请求权限,否则GPS无法获取位置。
  3. 内存泄漏:如果GPSTracker持有Activity引用,需在Activity销毁时解绑,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:31:22