Android使用OkHttp对接AVS无DownchannelStream响应问题
解决Android OkHttp与AVS HTTP/2 Downchannel流读取问题
首先先帮你理清几个正常现象,避免你误解:
- SynchronizeState和Recognize请求返回204是正常的:AVS的
/events端点处理这类事件时,不会在请求的响应体里返回内容,而是通过你之前建立的Downchannel长连接异步推送指令。所以syncResp和speechResp的空响应完全符合预期,不用纠结这部分。
接下来针对你遇到的核心问题,拆解分析并给出解决方案:
1. 为什么response.body().string()会阻塞Downchannel处理?
Downchannel是HTTP/2长连接,AVS会持续保持连接并在有指令时推送数据,流不会主动关闭。response.body().string()会尝试读取整个流直到结束,这会导致线程无限阻塞,所以你看到downChannelResp后续代码无法执行是必然结果——永远不要用string()读取长连接的响应体。
2. 音频请求的Content-Disposition错误
在testRecognizeEventAVS方法中,你给音频部分的Content-Disposition设置成了name="metadata",这是关键错误!AVS要求multipart请求里的音频部分必须是name="audio",否则AVS无法正确解析你的语音请求,自然不会推送任何指令到Downchannel。
修正这部分代码:
// Audio Part Map<String, String> audioHeaders = new HashMap<String, String>(); audioHeaders.put("Content-Disposition", "form-data; name=\"audio\""); // 把metadata改成audio MultipartBody.Part audioPart = MultipartBody.Part.create(Headers.of(audioHeaders), RequestBody.create(createTestFile(), AUDIO_TYPE)); partList.add(audioPart);
3. Downchannel流的正确读取方式
你当前用while (!bufferedSource.exhausted())的方式会阻塞线程,因为exhausted()会等待流结束。正确的做法是用异步线程持续监听流,读取AVS推送的换行分隔的JSON指令:
替换你的processResponse中针对Downchannel的处理逻辑,改成如下:
private void processDownChannelResponse(Response response) { // 开启单独线程读取长连接,避免阻塞OkHttp的回调线程 new Thread(() -> { BufferedSource source = response.body().source(); Buffer buffer = new Buffer(); try { while (!Thread.currentThread().isInterrupted()) { // 尝试读取数据,无数据时会短暂等待,不会无限阻塞 long bytesRead = source.read(buffer, 8192); if (bytesRead == -1) { Log.d("downChannelResp", "Downchannel连接已关闭"); break; } // AVS的Downchannel指令是每行一个JSON,所以按行读取 String rawDirective = buffer.readUtf8Line(); if (rawDirective != null && !rawDirective.trim().isEmpty()) { Log.d("downChannelResp", "收到指令: " + rawDirective); // 这里可以将rawDirective解析成JSON对象,处理具体的指令(比如Speak、Play等) } } } catch (IOException e) { Log.e("downChannelResp", "读取Downchannel失败", e); } finally { // 关闭响应体,释放资源 try { response.body().close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
然后在Downchannel的回调中调用这个方法:
@Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { Log.d("downChannelResp", "Down channel recieved! Test 1"); processDownChannelResponse(response); Log.d("downChannelResp", "Down channel recieved! Test 2"); responseDirective = response; }
4. 优化OkHttpClient配置,确保长连接稳定
为了让Downchannel连接保持活跃,需要调整OkHttpClient的超时设置:
OkHttpClient downChannelClient = new OkHttpClient.Builder() .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) // 维持连接池,复用连接 .readTimeout(0, TimeUnit.MILLISECONDS) // 读取超时设为0,禁用超时(因为长连接会一直保持) .writeTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS) .build();
最后验证步骤:
- 修正音频部分的Content-Disposition
- 用新的Downchannel读取逻辑
- 确保access token有效且权限正确
- 发送Recognize请求后,应该能在Downchannel的日志里看到AVS推送的指令(比如SpeechSynthesizer的Speak指令)
内容的提问来源于stack exchange,提问作者W --
相关产品推荐
相关产品推荐

