当尝试使用OkHttp在android上从AVS Web服务器接收HTTP响应时,我收到SocketTimeoutException

问题描述

下面的代码有很多调试打印语句。我在谷歌浏览和浏览其他堆栈溢出帖子时发现,人们通常在发出同步事件请求后返回一个json文件,但是在调用Log.d("syncResponse_body","Result: " + response.body().string());时约30秒我什么都没得到,然后SocketTimeoutException。这是我关注的亚马逊文档: https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/manage-http2-connection.html

private Response makeSyncRequest(OkHttpClient downChannelClient,String accesstoken) throws IOException {
    final Request getRequest = new Request.Builder()
        .url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/directives")//endpoint url
        .get()
        .addHeader("authorization","Bearer " + accesstoken)
        .build();

    Log.d("Request_header",getRequest.toString());
    Call currentCall = downChannelClient.newCall(getRequest);
    Response syncResponse = currentCall.execute();
    Log.d("Response_success",String.valueOf(syncResponse.isSuccessful()));
    Log.d("Response_str",syncResponse.toString());

    return syncResponse;
}

private Response syncronizeWithAVS(OkHttpClient downChannelClient,String accesstoken) throws IOException {
    Response syncResponse = makeSyncRequest(downChannelClient,accesstoken);

    String msgid = UUID.randomUUID().toString();
    String speakToken = "";
    long offsetMili = 20; // if lags put down to 10.
    String playerActivity = "PLAYING";

    // ------------------------------
    final String JSON_SYNC = "{\"context\":[{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":\"RecognizerState\"},\"payload\":{\"wakeword\":\"ALEXA\"}},{\"header\":{\"namespace\":\"SpeechSynthesizer\",\"name\":\"SpeechState\"},\"payload\":{\"token\":\"" + speakToken + "\",\"offsetInMilliseconds\":" + offsetMili + ",\"playerActivity\":\"" + playerActivity + "\"}}],\"event\":{\"header\":{\"namespace\":\"System\",\"name\":\"SynchronizeState\",\"messageId\":\"" + msgid + "\"},\"payload\":{}}}";

    List<MultipartBody.Part> partList = new ArrayList<>();
    MultipartBody.Part syncPart = MultipartBody.Part.create(Headers.of("Content-disposition","form-data; name=\"Metadata\""),RequestBody.create(JSON_SYNC,JSON_TYPE));
    partList.add(syncPart);

    RequestBody body = new MultipartBody(ByteString.encodeUtf8(BOUNDARY_TERM),MultipartBody.FORM,partList);

    Log.d("part",syncPart.headers().toString());
    Log.d("body",body.contentType().toString());

    final Request postRequest = new Request.Builder()
            .url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/events")//endpoint url
            .post(body)
            .addHeader("authorization","Bearer " + accesstoken)
            .build();

    Log.d("post_request",postRequest.toString());
    Log.d("post_req_body",JSON_SYNC);

    Response postResponse = null;
    try {
        postResponse = downChannelClient.newCall(postRequest).execute();
        Log.d("Post_succes","" + postResponse.isSuccessful());
        Log.d("Post_succes","" + postResponse.toString());
        Log.d("Post_Response","Result: " + postResponse.body().string());

    } catch (IOException e) {
        e.printstacktrace();
    }

    return syncResponse;
}

private void login(AuthorizeResult authorizeResult) {
    ...
    Response response = syncronizeWithAVS(downChannelClient,accesstoken);
    Log.d("syncResponse_body","Result: " + response.body().string());
    ...
}

登录到亚马逊以获取访问令牌后,这是我得到的输出

D/accesstoken: authorizeResult.toString(): com.amazon.identity.auth.device.api.authorization.AuthorizeResult@f028508b
    accesstoken: <censored accesstoken for this forum post>
D/Request_header: Request{method=GET,url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives,headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/Response_success: true
D/Response_str: Response{protocol=h2,code=200,message=,url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives}
D/part: Content-disposition: form-data; name="Metadata"
D/body: multipart/form-data; boundary=------------------------qM9tn4VZyj
D/post_request: Request{method=POST,url=https://alexa.na.gateway.devices.a2z.com/v20160207/events,headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/post_req_body: {"context":[{"header":{"namespace":"SpeechRecognizer","name":"RecognizerState"},"payload":{"wakeword":"ALEXA"}},{"header":{"namespace":"SpeechSynthesizer","name":"SpeechState"},"payload":{"token":"","offsetInMilliseconds":20,"playerActivity":"PLAYING"}}],"event":{"header":{"namespace":"System","name":"SynchronizeState","messageId":"c5488d48-c758-46a0-aeea-e305a452df90"},"payload":{}}}
D/Post_succes: true
    Response{protocol=h2,code=204,url=https://alexa.na.gateway.devices.a2z.com/v20160207/events}
D/Post_Response: Result: 

然后,在30秒后,

W/System.err: java.net.socketTimeoutException: timeout
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.kt:677)
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.kt:686)
        at okhttp3.internal.http2.Http2Stream$FramingSource.read(Http2Stream.kt:382)
W/System.err:     at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:276)
        at okio.Buffer.writeall(Buffer.kt:1655)
        at okio.RealBufferedSource.readString(RealBufferedSource.kt:95)
        at okhttp3.ResponseBody.string(ResponseBody.kt:187)
        at aut.rnd.alexa.ui.account.AccountFragment.login(AccountFragment.java:362)
        at aut.rnd.alexa.ui.account.AccountFragment.access$300(AccountFragment.java:65)
        at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:381)
        at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:373)
        at com.amazon.identity.auth.device.api.authorization.AuthorizationManager$3.onSuccess(AuthorizationManager.java:276)
        at com.amazon.identity.auth.device.thread.AuthzCallbackFuture$1.onSuccess(AuthzCallbackFuture.java:36)
        at com.amazon.identity.auth.device.thread.MAPCallbackFuture.onSuccess(MAPCallbackFuture.java:88)
        at com.amazon.identity.auth.device.authorization.InternalAuthManager$2$1.onSuccess(InternalAuthManager.java:119)
        at com.amazon.identity.auth.device.authorization.TokenHelper.getToken(TokenHelper.java:65)
W/System.err:     at com.amazon.identity.auth.device.authorization.InternalAuthManager$2.run(InternalAuthManager.java:115)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

解决方法

guide you mention包含以下信息:

读取超时:因为AVS需要打开下行通道流 在AVS和客户端之间的连接寿命内,设置任何读取 您的客户至少要超时60分钟。

因此,您当然应该增加此连接的读取超时。为此,请使用以下代码(位于this answer中):

downChannelClient.setReadTimeout(60,TimeUnit.MINUTES);