GSON解析方法:解决Activity不等待API完成及大JSON解析问题
解决PriceActivity不等待API请求完成+大型JSON解析问题
嘿,这个问题我太熟了!核心原因是网络请求是异步操作,你直接在Activity里调用API的方法,它会立刻往下走,根本不会等请求返回数据。我分两块给你讲解决方案,先搞定异步等待的问题,再处理大型JSON的解析:
一、让Activity正确等待API请求完成
这里有几种主流的实现方式,你可以根据自己的项目语言(Java/Kotlin)选:
1. 用Callback回调接口(Java/Kotlin通用)
先定义一个回调接口,让API类在请求完成后主动通知Activity:
// 定义回调接口 public interface CryptoPriceCallback { void onDataLoaded(List<CryptoPrice> prices); void onError(String errorMessage); } // API类里的请求方法 public class CryptoApi { public void fetchPrices(CryptoPriceCallback callback) { // 开启子线程执行网络请求(避免阻塞主线程) new Thread(() -> { try { // 执行网络请求,获取输入流(这里用OkHttp举例) Request request = new Request.Builder().url("你的加密货币API地址").build(); Response response = new OkHttpClient().newCall(request).execute(); InputStream inputStream = response.body().byteStream(); // 解析数据(后面讲大型JSON的流式解析) List<CryptoPrice> prices = parseLargeJson(inputStream); // 切回主线程回调结果,保证UI操作安全 new Handler(Looper.getMainLooper()).post(() -> callback.onDataLoaded(prices)); } catch (Exception e) { new Handler(Looper.getMainLooper()).post(() -> callback.onError(e.getMessage())); } }).start(); } } // PriceActivity里调用并实现回调 public class PriceActivity extends AppCompatActivity implements CryptoPriceCallback { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_price); CryptoApi api = new CryptoApi(); api.fetchPrices(this); // 传入回调实现 } @Override public void onDataLoaded(List<CryptoPrice> prices) { // 这里就拿到完整数据了,放心更新UI updatePriceList(prices); } @Override public void onError(String errorMessage) { // 处理请求失败的情况,比如弹提示 Toast.makeText(this, "请求失败:" + errorMessage, Toast.LENGTH_SHORT).show(); } }
2. 用Kotlin协程(推荐Kotlin项目使用)
协程能让异步代码写得像同步一样简洁,还能自动处理线程切换:
// API类定义挂起函数 class CryptoApi { private val okHttpClient = OkHttpClient() suspend fun fetchPrices(): List<CryptoPrice> { val request = Request.Builder() .url("你的加密货币API地址") .build() // 切换到IO线程执行网络请求,避免阻塞主线程 return withContext(Dispatchers.IO) { val response = okHttpClient.newCall(request).execute() val inputStream = response.body?.byteStream() ?: throw Exception("空响应") // 流式解析大型JSON parseLargeJson(inputStream) } } } // PriceActivity里调用 class PriceActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_price) val api = CryptoApi() // 用lifecycleScope绑定Activity生命周期,避免内存泄漏 lifecycleScope.launch { try { val prices = api.fetchPrices() // 协程自动切回主线程,直接更新UI updatePriceList(prices) } catch (e: Exception) { Toast.makeText(this@PriceActivity, "请求失败:${e.message}", Toast.LENGTH_SHORT).show() } } } }
二、正确解析大型JSON数据
如果JSON数据量很大,直接把整个字符串转成对象列表很容易触发OOM(内存溢出),这时候必须用流式解析,逐行读取JSON内容,而不是一次性加载到内存:
1. Gson流式解析示例(Java/Kotlin通用)
private List<CryptoPrice> parseLargeJson(InputStream inputStream) throws IOException { List<CryptoPrice> prices = new ArrayList<>(); JsonReader reader = new JsonReader(new InputStreamReader(inputStream)); reader.beginArray(); // 假设API返回的根节点是数组 while (reader.hasNext()) { // 逐个解析单个JSON对象,只把当前对象加载到内存 CryptoPrice price = new Gson().fromJson(reader, CryptoPrice.class); prices.add(price); } reader.endArray(); reader.close(); return prices; }
2. 额外优化建议
- 分页请求:如果API支持分页,尽量分多次请求小批量数据(比如每次请求50条),比一次拿几千条数据更高效,也更省内存。
- 忽略无用字段:如果JSON里有很多你不需要的字段,在实体类里用
@Expose(serialize = false, deserialize = false)(Gson)或者@Json(ignore = true)(Moshi)忽略它们,减少内存占用。 - 用Moshi替代Gson:Moshi的流式解析性能更好,对Kotlin的空安全支持更友好,语法和Gson接近,学习成本很低。
最后再提醒一句:不管用哪种方式,网络请求绝对不能放在主线程,必须在子线程执行;更新UI的时候一定要切回主线程,不然会直接崩溃哦!
内容的提问来源于stack exchange,提问作者mrobertini1239
相关产品推荐
相关产品推荐

