"return response.body().string() " 是空的 okhttp3

"return response.body().string() " is empty with okhttp3

我创建了以下 class:

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

它曾经在 activity 中工作,我将一些数据写入服务器上的 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 class 中,我将正确的结果写入 System.out。那就return吧。但是响应是空的。 我不明白为什么。

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

我将 class 的结尾更改如下:

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

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

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

okhttp3 中还有一个新方法可用,即 peekBody(byte count),根据文档,它从响应正文中查看 byteCount 个字节,returns 它们作为一个新的响应正文。

您在调用 string() 并清空支持源时阅读正文。 OkHttp 尝试尽快释放支持资源。将正文读入变量是传递它或多次使用它的正确方法。

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

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

The response body can be consumed only once.

This class may be used to stream very large responses. For example, it is possible to use this class to read a response that is larger than the entire memory allocated to the current process. It can even stream a response larger than the total storage on the current device, which is a common requirement for video streaming applications.

Because this class does not buffer the full response in memory, the application may not re-read the bytes of the response. Use this one shot to read the entire response into memory with bytes() or string(). Or stream the response with either source(), byteStream(), or charStream().

我们要把响应内容保存到一个字符串类型的变量中。

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();
    }

对于 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()
  }
}

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

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

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

一段代码:

   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(),流将是空的。