Retrofit踩坑日记:response.body().string()只能调用一次

总览和相关链接

这个作业属于哪个课程 2021春/S班
这个作业要求在哪里 软件工程实践总结&个人技术博客
这个作业的目标 总结个人技术

技术概述

我在开发新闻app的过程中需要请求服务器数据,Retrofit框架在网络请求方面封装的很好,因此我选择学习它,该技术的难点在于它的设计原理十分复杂,它利用了注解和okhttp框架给程序员带来了极大的便利。

技术详述

首先先创建一个接口:

public interface NewsService {
    @POST("index") //必须用post请求
    @FormUrlEncoded
    Call<BaseResponse> post(@Field("type") String type, @Field("key") String key);

    @GET("index")
    Call<ResponseBody> get(@Query("type") String type, @Query("key") String key);
}

核心方法:先配置Retrofi构造器的addConverterFactory方法,让它可以自动实现json数据转换成实体类,在baseurl函数的参数里填上要访问的网络地址,利用Retrofit框架对NewsService进行实现,然后调用NewsService的post方法,然后在子线程中进行网络请求,主线程等待子线程处理完毕,将得到的数据返回。

 public List<News> getData(String type) throws InterruptedException {
        List<News>[] newsList = new List[]{new ArrayList<>()};
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://v.juhe.cn/toutiao/")
                .addConverterFactory(GsonConverterFactory.create()) //添加转换器build();
                .build();
        NewsService newsService = retrofit.create(NewsService.class); //Retrofit将这个接口进行实现
        Call<BaseResponse> call = newsService.post(type, "4b3b9c355a55f676605532d37cf0d66c");
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Response<BaseResponse> response = call.execute();
                    BaseResponse baseResponse = response.body();
                    newsList[0] = baseResponse.getResult().getData();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        thread.join();
        Log.d("lifecycle", String.valueOf(newsList[0].size())); //0?
        return newsList[0];
    }

流程图:

技术使用中遇到的问题和解决过程。

问题:我打算将服务器返回的json数据进行解析,解析出来的对象一直是空,我还以为我导入的GSON库有问题,浪费了很多时间。
贴上错误代码

 public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
              try {
                   Log.d("RetrofitActivity", response.body().string());
                  //String result = response.body().string();
                  BaseResponse baseResponse = new Gson().fromJson(response.body().string(), BaseResponse.class);
                  System.out.println(baseResponse);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }

注意看我调用了两次response.body().string(),原来response.body().string()只能调用一次,第二次为空。因为response.body().string()所包含的资源往往很大 ,当客户端接收到数据之后,okhttp的缓冲区就会自动释放资源!!

解决方案:

public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
              try {
                   //Log.d("RetrofitActivity", response.body().string());
                  String result = response.body().string();
                  BaseResponse baseResponse = new Gson().fromJson(result, BaseResponse.class);
                  System.out.println(baseResponse);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }

总结

当调用response.body().string()获取数据时,response.body().string()只能调用一次,因此我们可以新建变量将数据存储下来再进行后面的处理。犯这种错误的原因主要还是因为对网络框架的原理不够了解。

参考文献

OkHttp踩坑记:为何 response.body().string() 只能调用一次? 作者:ruicbAndroid

posted @ 2021-06-24 23:02  linhuaiying  阅读(587)  评论(3编辑  收藏  举报