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
相关产品推荐
相关产品推荐

