【问题标题】:"return response.body().string() " is empty with okhttp3“return response.body().string()”为空,带有 okhttp3
【发布时间】:2016-02-26 10:54:47
【问题描述】:

我创建了以下类:

package com.inverseo.marc.t372lematin;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


public class PostJSON {
    public static final MediaType JSON
            = MediaType.parse("application/json; charset=utf-8");

    OkHttpClient client = new OkHttpClient();
    public String postJSONRequest(String url, String json) throws IOException {
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        System.out.println("postJSONRequest response.body : "+response.body().string());
        return response.body().string() ;
    }   //postJSONRequete
} //class PostJSON

它曾经在我将一些数据写入服务器上的 MySQL 的活动中工作。 当我从以下代码中调用它时,我得到一个空响应!

      System.out.println("début appel "+getString(R.string.CF_URL)+"authentication2.php" );
        PostJSON client2 = new PostJSON();
        JSONObject obj = new JSONObject();
        try {
            obj.put("username", mUserName);
            obj.put("password", mPassword);
            obj.put("email", mEmail);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        System.out.println("obj.toString()="+obj.toString());
        String response = null;
        try {
            response = client2.postJSONRequest(getString(R.string.CF_URL)+ "authentication2.php", obj.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("appel d'authentication2.php échoué : " + e);
        }
        System.out.println("fin authentication2, response = "+response);
        return response;

这是我在 logcat 中得到的内容

02-25 05:52:24.938 26130-26226/com.inverseo.marc.t372lematin I/System.out: Accès à Internet : OK
02-25 05:52:24.938 26130-26226/com.inverseo.marc.t372lematin I/System.out: début appel http://mywebsite.fr/Inverseo/authentication2.php
02-25 05:52:24.938 26130-26226/com.inverseo.marc.t372lematin I/System.out: obj.toString()={"email":"xxx-ts@laposte.net","password":"xxx","username":"MarcUser"}
02-25 05:52:25.068 26130-26130/com.inverseo.marc.t372lematin I/Choreographer: Skipped 30 frames!  The application may be doing too much work on its main thread.
02-25 05:52:25.448 26130-26130/com.inverseo.marc.t372lematin I/Choreographer: Skipped 34 frames!  The application may be doing too much work on its main thread.
02-25 05:52:27.898 26130-26226/com.inverseo.marc.t372lematin I/System.out: postJSONRequest response.body : {"result":1,"message":"AUTHENTICATION OK","Id_profile":"1394","DebutDerCycle":"2016-01-31"}
02-25 05:52:27.898 26130-26226/com.inverseo.marc.t372lematin I/System.out: fin authentication2, response = 
02-25 05:52:28.588 26130-26130/com.inverseo.marc.t372lematin I/Choreographer: Skipped 140 frames!  The application may be doing too much work on its main thread.

总结一下,在我的 PostJSON 类中,我将正确的结果写入 System.out。然后返回它。但是响应是空的。 我不知道为什么。

【问题讨论】:

    标签: android okhttp3


    【解决方案1】:

    我找到了一个听起来很奇怪的解决方案,至少对我来说是这样。

    我将课程的结尾更改如下:

    String MyResult = response.body().string();
    System.out.println("postJSONRequest response.body : "+MyResult);
    return MyResult ;
    

    所以我没有调用两次response.body().string(),而是将它放在一个变量中。 它有效!

    【讨论】:

    • 你说得对,这很奇怪。但这也是 OkHttp 的工作方式(按设计)。一旦你调用 response.body().string() 信息就会被清除,这意味着你第二次调用它你什么也不会收到。
    • 在 source 后面有一个缓冲区,string() 会消耗它,因此任何后续调用都找不到任何消耗。如果您想多次阅读,可以使用“中继”类。
    【解决方案2】:

    调用response.body().string() 会消耗身体 - 因此,您不能第二次调用它。 如果需要进一步处理,解决方案是将其存储在变量中。

    okhttp3 中还有一个可用的新方法,即 peekBody(byte count),根据文档,它可以从响应正文中窥视 byteCount 字节并将它们作为新的响应正文返回。

    【讨论】:

      【解决方案3】:

      当您调用string() 并清空支持源时,您阅读了正文。 OkHttp 尝试尽快释放支持资源。将 body 读入一个变量是传递它或存储它的正确方法。

      通常你不需要关闭 body,但是在我们没有 try-with-resources 的 Android 上,我建议关闭一个手写的 finally。

      以下是有关该主题的文档: http://square.github.io/okhttp/3.x/okhttp/

      响应正文只能使用一次。

      此类可用于流式传输非常大的响应。例如,可以使用此类读取大于分配给当前进程的整个内存的响应。它甚至可以流式传输比当前设备上的总存储容量更大的响应,这是视频流应用程序的常见要求。

      因为这个类不会在内存中缓冲完整的响应,所以应用程序可能不会重新读取响应的字节。使用这一镜头通过 bytes() 或 string() 将整个响应读入内存。或者使用 source()、byteStream() 或 charStream() 流式传输响应。

      【讨论】:

        【解决方案4】:

        我们必须将响应内容保存到字符串类型的变量中。

        OkHttpClient client = new OkHttpClient();
        
            try{
                MediaType mediaType = MediaType.parse("application/json");
                RequestBody body = RequestBody.create(mediaType, "'{\"id\":\"10001\"}'");
                Request request = new Request.Builder()
                        .url("mydomain")
                        .post(body)
                        .addHeader("content-type", "application/json")
                        .addHeader("cache-control", "no-cache")
                        .build();
        
                Response response= client.newCall(request).execute();
                String MyResult = response.body().string();
                return MyResult;
            }catch (Exception ex){
                return  ex.toString();
            }
        

        【讨论】:

          【解决方案5】:

          就我而言,我想更进一步,一方面将响应主体存储为字符串,但不消耗缓冲区,以便能够像往常一样使用它。

          我在这里找到了一个有效的解决方案:https://github.com/square/okhttp/issues/2869

          基本上,您可以获得缓冲区的只读视图,您可以从中读取而不消耗它,因此以后仍然可以使用它。

          一段代码:

             final BufferedSource source = responseBody.source();
             if (!source.request(Integer.MAX_VALUE)) {
               LOGGER.debug("Error reading response body");
             }
             final String bodyAsStr = source.buffer().snapshot().utf8();
             // you can still use your response body as usual
             responseParser.apply(responseBody.byteStream())
          

          使用source.string() 而不是source.buffer().snapshot().utf8(),流将为空。

          【讨论】:

            【解决方案6】:

            对于 Kotlin 用户

            val topNewsList = gson.fromJson(body, NewsList::class.java)
            
            getActivity()?.runOnUiThread {
                try {
            
                   if(topNewsList.post_data != null) {
                      //set adapter
                      recyclerView_news.adapter = TopNewsAdapter(topNewsList, layout.row_news)
                   }
                   else{
                      // handle the empty list
                   }
            
               } 
               catch (e: Exception) {
                Toast.makeText(context, "Please refresh again", Toast.LENGTH_SHORT).show()
              }
            }
            

            【讨论】:

              猜你喜欢
              • 2020-08-19
              • 2021-03-04
              • 1970-01-01
              • 1970-01-01
              • 2013-12-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多