web-dev-qa-db-ja.com

「return response.body()。string()」はokhttp3で空です

次のクラスを作成しました。

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":"[email protected]","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に書き込みます。その後、それを返します。しかし、応答は空になります。理由がわかりません。

10
Marc

少なくとも私には奇妙に聞こえる解決策を見つけました。

クラスの終わりを次のように変更しました:

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

したがって、response.body().string()を2回呼び出す代わりに、変数に入れます。そしてそれはうまくいきます!

13
Marc

response.body().string()を呼び出すと本体が消費されるため、2回目に呼び出すことはできません。解決策は、さらに処理する必要がある場合に変数に格納することです。

_okhttp3_で利用できる新しいメソッドもあります。これはpeekBody(byte count)であり、ドキュメントごとに、応答本文から最大でbyteCountバイトを調べ、新しい応答本文として返します。

11
MarkoMilos

string()を呼び出してバッキングソースを空にしたときに本体を読み取りました。 OkHttpはできるだけ早くバッキングリソースを解放しようとします。本体を変数に読み込むことは、それを渡したり、複数回使用したりして保存するための正しい方法です。

通常は本体を閉じる必要はありませんが、try-with-resourcesがないAndroidでは、最終的に手書きで閉じることをお勧めします。

これは件名に関するドキュメントです: http://square.github.io/okhttp/3.x/okhttp/

レスポンスボディは1回だけ使用できます。

このクラスは、非常に大きな応答をストリーミングするために使用できます。たとえば、このクラスを使用して、現在のプロセスに割り当てられているメモリ全体よりも大きい応答を読み取ることができます。現在のデバイスの総ストレージよりも大きな応答をストリーミングすることもできます。これは、ビデオストリーミングアプリケーションの一般的な要件です。

このクラスは完全な応答をメモリにバッファリングしないため、アプリケーションは応答のバイトを再度読み取ることができません。このワンショットを使用して、bytes()またはstring()で応答全体をメモリに読み込みます。または、source()、byteStream()、charStream()のいずれかを使用して応答をストリーミングします。

6
user823629

応答の内容を文字列型の変数に保存する必要があります。

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

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()
  }
}
0
santosh devnath