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

Android Kotlin:使用URL获取公开JSON的线程结果获取问题

Android 不用第三方库,用 java.net.URL 异步获取 JSON 数据的正确姿势

你遇到的问题本质是:子线程是异步执行的,主线程的return语句不会等子线程跑完就执行了,所以直接返回空字符串。下面给你几种原生实现方式,不用Volley、OkHttp,帮你搞懂线程异步的工作机制:

方法一:回调接口(最常用,推荐)

这是Android异步操作的标准玩法,通过接口把异步任务的结果传递回来,不会阻塞主线程。

步骤1:定义回调接口

先整个接口,用来传递成功/失败的结果:

public interface JsonCallback {
    void onSuccess(String jsonData); // 获取成功时调用
    void onFailure(Exception e);    // 获取失败时调用
}

步骤2:实现网络请求方法

把网络操作放到子线程里,完成后通过回调把结果抛出去,还要用Handler切回主线程(因为Android只能在主线程更新UI):

public void fetchJson(String urlStr, JsonCallback callback) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            BufferedReader reader = null;
            StringBuilder jsonBuilder = new StringBuilder();

            try {
                URL url = new URL(urlStr);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000); // 连接超时5秒
                connection.setReadTimeout(5000);    // 读取超时5秒

                // 检查响应码,200表示请求成功
                if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        jsonBuilder.append(line);
                    }
                    // 切回主线程通知成功
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onSuccess(jsonBuilder.toString());
                        }
                    });
                } else {
                    throw new IOException("请求失败,响应码:" + connection.getResponseCode());
                }
            } catch (Exception e) {
                // 切回主线程通知失败
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        callback.onFailure(e);
                    }
                });
            } finally {
                // 关闭流,避免资源泄漏
                try {
                    if (reader != null) reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}

步骤3:调用示例

直接传入URL和回调,在回调里处理结果:

fetchJson("https://api.deezer.com/search/playlist?q=test", new JsonCallback() {
    @Override
    public void onSuccess(String jsonData) {
        // 这里处理拿到的JSON,比如解析、更新UI
        Log.d("JSON_RESULT", "获取成功:" + jsonData);
    }

    @Override
    public void onFailure(Exception e) {
        // 处理错误,比如弹个提示给用户
        Log.e("JSON_ERROR", "请求失败", e);
    }
});

方法二:用 FutureTask + ExecutorService

这种方式可以直接拿到异步任务的返回值,但绝对不能在主线程调用get(),不然会阻塞UI导致ANR。

步骤1:写同步请求方法

先写一个同步的网络请求方法,直接返回JSON字符串:

private String getJsonSync(String urlStr) throws Exception {
    URL url = new URL(urlStr);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.setConnectTimeout(5000);
    connection.setReadTimeout(5000);

    if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
        throw new IOException("请求失败,响应码:" + connection.getResponseCode());
    }

    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    StringBuilder jsonBuilder = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        jsonBuilder.append(line);
    }
    reader.close();
    return jsonBuilder.toString();
}

步骤2:用FutureTask异步执行

通过FutureTask包装任务,在子线程里等待结果,再切回主线程处理:

ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
    @Override
    public String call() throws Exception {
        return getJsonSync("https://api.deezer.com/search/playlist?q=test");
    }
});

executor.execute(futureTask);

// 开一个新线程等待结果,别在主线程等!
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            String jsonData = futureTask.get(); // 等待任务完成,拿到结果
            // 切回主线程处理
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    Log.d("JSON_RESULT", "获取成功:" + jsonData);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();

// 记得在页面销毁时关闭executor,避免内存泄漏
// executor.shutdown();

方法三:Thread.join()(不推荐)

join()会让当前线程等待目标线程完成,但如果在主线程调用,会直接卡死UI,所以只能在其他子线程里用,实际开发中很少用这种方式:

public void fetchJsonWithJoin(String urlStr, JsonCallback callback) {
    Thread networkThread = new Thread(new Runnable() {
        String result;
        Exception error;

        @Override
        public void run() {
            try {
                URL url = new URL(urlStr);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                connection.setReadTimeout(5000);

                if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    StringBuilder jsonBuilder = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        jsonBuilder.append(line);
                    }
                    reader.close();
                    result = jsonBuilder.toString();
                } else {
                    error = new IOException("请求失败");
                }
            } catch (Exception e) {
                error = e;
            }
        }
    });

    networkThread.start();

    // 开新线程等待networkThread完成
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                networkThread.join(); // 等待网络线程执行完
                // 切回主线程通知结果
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        if (result != null) {
                            callback.onSuccess(result);
                        } else {
                            callback.onFailure(error);
                        }
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

总结下:

  • 优先用回调接口,这是Android异步操作的标准玩法,既不卡UI,还能方便处理成功/失败的情况。
  • FutureTask适合需要直接拿返回值的场景,但记住别在主线程调用get()。
  • Thread.join()尽量别碰,尤其是主线程里用,很容易导致ANR。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:15:54