标签:案例 ++ direct min ram stop dma 注意 empty
public LongSparseArray<RecordBean> recordList=new LongSparseArray<>();
public class RecordBean {public static final String VOICE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "voice";public static final String XF_VOICE_FILE_PATH = VOICE_PATH + File.separator + "xf_temp_file.wav";public String pcmPath;public String aacPath;public boolean startRecordSuccess = false;//开始录音成功,即调用JJMediaSDK.startRecord的返回值public boolean endRecordSuccess = false;//结束录音成功,即调用JJMediaSDK.stopRecord的返回值public long startTime;//开始录音时间public long endTime;//结束录音时间public boolean sendVoiceSuccess = false;//上传并发送成功public boolean translateSuccess = false;//翻译成功public String qnKey;//上传到七牛的语音地址public long clientid;//当上传七牛成功后发送一条socket语音消息,此消息需要客户端生成一个clientid。同时这也是集合recordList中的keypublic int msgid;//当上传七牛成功后发送一条socket语音消息,服务器响应时会返回一个msgid,以后就需根据msgid发送及获取语音对应的文本public String content;//讯飞翻译出的内容public RecordBean(long time) {clientid = time;String data = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss SSS", Locale.getDefault()).format(new Date(time));this.aacPath = VOICE_PATH + File.separator + "aac_" + data + ".aac";this.pcmPath = VOICE_PATH + File.separator + "pcm_" + data + ".pcm";L.i("语音文件保存路径:" + pcmPath + "\n" + aacPath);}public static class VoiceBean {//发送的语音消息的内容。仅仅包含语音URL路径以及语音时长String path;int duration;public VoiceBean(String path, int duration) {this.path = path;this.duration = duration;}}}
//长按说话mChatInputWidget.setPressListener(new PressListener() {private long key;//集合recordList中的key,同时也是RecordBean中的clientid,同时也是语音消息的clientid@Overridepublic void pressing() {if (micOnoff == 1) {//是否正在上麦(占用录音设备):0,未指定;1,开;2,关ToastHelper.showToastInThread("亲,发送语音功能需要下麦哦...");} else { //注意,以下代码要在具有录音权限时访问AudioManager audioManager = (AudioManager) ChatActivity.this.getSystemService(Context.AUDIO_SERVICE);audioManager.setMicrophoneMute(false);//关闭麦克风静音//键为当前时间,以保证唯一性key = System.currentTimeMillis();RecordBean recordBean = new RecordBean(key);recordList.put(key, recordBean);//调用JNI代码启动录音,录音成功后会生成两个文件,这两个文件的存放位置封装在了recordBean中JJMediaSDK.captureStop();//停止录音recordBean.startRecordSuccess = JJMediaSDK.startRecord(recordBean.pcmPath, recordBean.aacPath);recordBean.startTime = System.currentTimeMillis();//记录开始和结束录音时间,以获取录音时长if (!recordBean.startRecordSuccess) Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();else pressSpeeking("正在说话...");//一个自定义的UI,当按住的时候一直显示,当松开的时候消失}}@Overridepublic void pressStop() {RecordBean recordBean = recordList.get(key);//获取当前key对应的RecordBeanif (recordBean == null || !recordBean.startRecordSuccess) {//肯定是录音失败了Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();return;}AudioManager audioManager = (AudioManager) ChatActivity.this.getSystemService(Context.AUDIO_SERVICE);audioManager.setMicrophoneMute(true);//开启麦克风静音//调用JNI代码结束录音。JNI同事说这里不会失败的。谁信啊,万一存储空间不足或手机突然爆掉了呢?recordBean.endRecordSuccess = JJMediaSDK.stopRecord();recordBean.endTime = System.currentTimeMillis();pressSpeekingdimiss();if (!recordBean.endRecordSuccess) {Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();return;}//****************************************【第一个异步:异步上传语音】*****************************************//异步上传语音到七牛,当上传成功后发送一条socket消息通知语音的URL路径,此时其他用户就可以播放此语音了//同时,服务器收到此消息后会做出响应,主要是返回一个msgid,以后就可以根据msgid通知及获取语音对应的文本了VoiceUtils.uploadVoiceAndSendMsg(ChatActivity.this, recordBean);//****************************************【第二个异步:异步讯飞翻译】*****************************************if (mIat == null) mIat = VoiceUtils.getSpeechRecognizerInstance(ChatActivity.this);//讯飞SDK初始化if (mIat == null) return;//讯飞SDK初始化失败//讯飞SDK翻译过程及状态监听RecognizerListener recognizerListener = VoiceUtils.getRecognizerListener(content -> {//翻译成功回调recordBean.content = content;recordBean.translateSuccess = true;Toast.makeText(ChatActivity.this, recordBean.content, Toast.LENGTH_SHORT).show();//**************************【将上传到七牛的语音和讯飞翻译的内容关联起来】******************************//如果第一个异步已经完成,则直接发一条socket消息,将两者内容联系在一起;否则等第一个异步完成以后再做处理if (recordBean.sendVoiceSuccess) SendMsgUtils.sendVoiceContentMsg(ChatActivity.this, recordBean);});// 函数调用返回值int ret = mIat.startListening(recognizerListener);if (ret != ErrorCode.SUCCESS) {Toast.makeText(ChatActivity.this, "翻译失败", Toast.LENGTH_SHORT).show();L.i("讯飞SDK翻译失败,错误码:" + ret);} else {byte[] audioData = VoiceUtils.readSDFile(recordBean.pcmPath);//读取SDK返回的pcm文件流if (null != audioData) {L.i("讯飞SDK开始音频流识别");mIat.writeAudio(audioData, 0, audioData.length);mIat.stopListening();} else {mIat.cancel();Toast.makeText(ChatActivity.this, "讯飞SDK读取音频流失败", Toast.LENGTH_SHORT).show();}}}});
public class SendMsgUtils {/*** 发送语音、Gif图等通用聊天消息** @param activity 必须是ChatActivity或ChatActivityPrivate* @param msgType 例如Common.e_MsgType.MSGTYPE_GIF_VALUE* @param content 消息内容,一般都是json格式*/public static void sendNormalMsg(Activity activity, int msgType, String content, long clientid) {if (activity instanceof ChatActivity) {if (msgType == Common.e_MsgType.MSGTYPE_VOICE_VALUE || msgType == Common.e_MsgType.MSGTYPE_GIF_VALUE) {int roleType = ((ChatActivity) activity).getRoleType();if (roleType <= 0) {//游客Toast.makeText(activity, "您没有权限发送此类型消息", Toast.LENGTH_SHORT).show();return;}}}int groupId = 0;boolean isGroup = false;int dstId = 0;if (activity instanceof ChatActivity) {groupId = ((ChatActivity) activity).getGroupId();isGroup = ((ChatActivity) activity).isGroup();dstId = ((ChatActivity) activity).getDstId();} else if (activity instanceof ChatActivityPrivate) {groupId = ((ChatActivityPrivate) activity).getGroupId();isGroup = ((ChatActivityPrivate) activity).isGroup();dstId = ((ChatActivityPrivate) activity).getDstId();}ChatReqHelper.sendNormalMsg(content, groupId, isGroup, dstId, clientid);ChatModel model = new ChatModel(content, ChatModel.RIGHT, msgType);model.clientid = clientid;model.setMsgTime((int) (System.currentTimeMillis() / 1000));model.setGroupId(groupId);model.setSrcUser(ChatReqHelper.getUserInfo());if (activity instanceof ChatActivity) {((ChatActivity) activity).getmGroupChats().add(0, model);((ChatActivity) activity).getmRvSquareChat().getAdapter().notifyItemInserted(0);((ChatActivity) activity).getmRvSquareChat().scrollToPosition(0);} else if (activity instanceof ChatActivityPrivate) {Common.UserInfo_t dstUserInfo = Common.UserInfo_t.getDefaultInstance().newBuilderForType().setUserId(dstId).build();model.setDstUser(dstUserInfo);((ChatActivityPrivate) activity).getmGroupChats().add(0, model);((ChatActivityPrivate) activity).getmRvSquareChat().getAdapter().notifyItemInserted(0);((ChatActivityPrivate) activity).getmRvSquareChat().scrollToPosition(0);}}/*** 发送语音消息(里面只有语音的URL路径及时长),里面用的完全就是sendNormalMsg的逻辑** @param activity 必须是ChatActivity或ChatActivityPrivate* @param recordBean 封装所有信息的bean*/public static void sendNormalVoiceMsg(Activity activity, RecordBean recordBean) {//当上传七牛成功后发送一条socket消息,当收到响应时(会返回一个msgid)说明此socket消息发送成功了int duration = (int) ((recordBean.endTime - recordBean.startTime) / 1000);RecordBean.VoiceBean voiceBean = new RecordBean.VoiceBean(recordBean.qnKey, duration);String content = new Gson().toJson(voiceBean);//{"path:":"", duration:100}sendNormalMsg(activity, Common.e_MsgType.MSGTYPE_VOICE_VALUE, content, recordBean.clientid);L.i("发送语音消息。content=" + content + "。clientid=" + recordBean.clientid);}/*** 发送语音所对应的文本消息,目的是将上传到七牛的【语音】和讯飞翻译的【内容】关联起来*/public static void sendVoiceContentMsg(Activity activity, RecordBean recordBean) {if (recordBean == null) return;if (recordBean.sendVoiceSuccess && recordBean.translateSuccess && recordBean.msgid != 0) {int groupId = 0, dstId = 0;if (activity instanceof ChatActivity) {groupId = ((ChatActivity) activity).getGroupId();dstId = ((ChatActivity) activity).getDstId();} else if (activity instanceof ChatActivityPrivate) {groupId = ((ChatActivityPrivate) activity).getGroupId();dstId = ((ChatActivityPrivate) activity).getDstId();}ChatSvr.CMDVoiceContentSubmit message = ChatSvr.CMDVoiceContentSubmit.newBuilder().setGroupid(groupId)//群id.setSrcuid(AccountManager.getInstance().getServiceUserId())//消息发起者id.setDstuid(dstId)//消息接收者id(群聊传0).setMsgid(recordBean.msgid)//msgid,此msgid是由服务器返回的.setContent(recordBean.content)//语音消息翻译成文本的内容.build();LoginConnection.voiceContentSubmit(message.toByteArray(), null);L.i("发送语音文本消息。content=" + recordBean.content + "。msgid=" + recordBean.msgid);if (recordBean.pcmPath != null && new File(recordBean.pcmPath).delete()) L.i("成功删除pcm临时文件");if (recordBean.aacPath != null && new File(recordBean.aacPath).delete()) L.i("成功删除aac临时文件");//移除集合中的此RecordBeanLongSparseArray<RecordBean> recordList = null;if (activity instanceof ChatActivity) recordList = ((ChatActivity) activity).recordList;else if (activity instanceof ChatActivityPrivate) recordList = ((ChatActivityPrivate) activity).recordList;if (recordList != null) {recordList.remove(recordBean.clientid);L.i("成功移除recordBean:" + recordBean.clientid);}} else L.i("RecordBean状态错误" + recordBean.toString());}/*** 查询语音文本消息。这时已经与RecordBeanme没有任何关系了,这里的msgid是后台返回的*/public static void qryVoiceContentMsg(Activity activity, int msgid) {int groupId = 0, dstId = 0;if (activity instanceof ChatActivity) {groupId = ((ChatActivity) activity).getGroupId();dstId = ((ChatActivity) activity).getDstId();} else if (activity instanceof ChatActivityPrivate) {groupId = ((ChatActivityPrivate) activity).getGroupId();dstId = ((ChatActivityPrivate) activity).getDstId();}ChatSvr.CMDQryChatMsgReq req = ChatSvr.CMDQryChatMsgReq.newBuilder().setGroupid(groupId)//群id.setSrcuid(AccountManager.getInstance().getServiceUserId())//消息发起者id.setDstuid(dstId)//消息接收者id(群聊传0).setMsgid(msgid)//msgid.build();LoginConnection.qryChatMsgReq(req.toByteArray(), null);L.i("查询语音文本消息。msgid=" + msgid + "。groupId=" + groupId);}}
public void onEventMainThread(BaseEvent event) {if (event.getEvent() == EventHelper.EVENT_GROUPMSGRECV) {if (event.getData() instanceof ChatSvr.CMDGroupMsgRecv) {//异步上传语音到七牛成功后,客户端发送一条socket消息通知语音的URL路径//这是当服务器收到此消息后做出的响应,我们主要是拿到回一个msgid,以后就可以根据msgid通知语音对应的文本了ChatSvr.CMDGroupMsgRecv msgRecv = (ChatSvr.CMDGroupMsgRecv) event.getData();for (int i = 0; i < recordList.size(); i++) {Long key = recordList.keyAt(i);if (msgRecv.getClientMSgId() == key) {//RecordBean的clientid和recordList的key时同一个数RecordBean recordBean = recordList.get(key);recordBean.sendVoiceSuccess = true;recordBean.msgid = msgRecv.getMsgId();L.i("发送语音响应:msgid=" + recordBean.msgid + " clientid=" + recordBean.clientid);//**************************【将上传到七牛的语音和讯飞翻译的内容关联起来】******************************//如果第二个异步已经完成,则直接发一条socket消息,将两者内容联系在一起;否则等第二个异步完成以后再做处理if (recordBean.translateSuccess) SendMsgUtils.sendVoiceContentMsg(this, recordBean);break;}}}return;}if (event.getEvent() == EventHelper.EVENT_QRY_CHAT_MSG) {//**********************************************【响应查询语音消息】************************************************if (event.getData() instanceof ChatSvr.CMDQryChatMsgResp) {ChatSvr.CMDQryChatMsgResp resp = (ChatSvr.CMDQryChatMsgResp) event.getData();if (resp.getMsg() != null) {int msgID = resp.getMsg().getMsgId();String voiceContent;if (resp.getMsg().getVoiceContent() != null) voiceContent = resp.getMsg().getVoiceContent().getData();else voiceContent = "翻译失败";Toast.makeText(this, "msgID:" + msgID + " 内容:" + voiceContent, Toast.LENGTH_SHORT).show();L.i("查询语音文本响应。" + "msgID:" + msgID + " 内容:" + voiceContent);}}return;}}
public class VoiceUtils {public interface OnRecognizerResultListener {void onResult(String content);}public static RecognizerListener getRecognizerListener(OnRecognizerResultListener listener) {return new RecognizerListener() {StringBuilder sb = new StringBuilder();@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入L.i("onBeginOfSpeech");}@Overridepublic void onError(SpeechError error) {L.i("onError:" + error.getErrorCode() + " " + error.getErrorDescription());//http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=13056&fromuid=44990// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。// 如果使用本地功能(语记)需要提示用户开启语记的录音权限。//没有数据,vad_bos设置问题}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入L.i("onEndOfSpeech");}@Overridepublic void onResult(RecognizerResult results, boolean isLast) {L.i("onResult:" + results.getResultString() + "。格式化内容:" + getFormatResult(results) + "。isLast:" + isLast);sb.append(getFormatResult(results));if (isLast) {L.i("完整结果为:" + sb.toString());listener.onResult(sb.toString());}}@Overridepublic void onVolumeChanged(int volume, byte[] data) {L.i("onVolumeChanged。当前正在说话,音量大小:" + volume + "。返回音频数据长度:" + data.length);}@Overridepublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因// 若使用本地能力,会话id为nullif (SpeechEvent.EVENT_SESSION_ID == eventType) {String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);L.i("onEvent:" + eventType + " session_id =" + sid);}}};}public static SpeechRecognizer getSpeechRecognizerInstance(Context context) {//*****************************************************初始化监听器************************************************InitListener mInitListener = code -> {if (code != ErrorCode.SUCCESS) L.i("初始化失败,错误码:" + code);};SpeechRecognizer mIat = SpeechRecognizer.createRecognizer(context, mInitListener);if (null == mIat) {// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688L.i("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");Toast.makeText(context, "语音引擎初始化失败", Toast.LENGTH_SHORT).show();return null;}//******************************************************参数设置**************************************************// 清空参数mIat.setParameter(SpeechConstant.PARAMS, null);mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);// 设置听写引擎mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");// 设置返回结果格式mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");// 设置语言mIat.setParameter(SpeechConstant.ACCENT, "mandarin");// 设置语言区域,mandarin为普通话;// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理mIat.setParameter(SpeechConstant.VAD_BOS, "4000");//4000// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音mIat.setParameter(SpeechConstant.VAD_EOS, "4000");//1000// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点mIat.setParameter(SpeechConstant.ASR_PTT, "1");// 设置音频【保存】路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");// 注:AUDIO_FORMAT参数语记需要更新版本才能生效mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, RecordBean.XF_VOICE_FILE_PATH);// 设置音频来源为外部文件mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");// 也可以像以下这样直接设置音频文件路径识别(要求设置文件在sdcard上的全路径)://mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-2");//mIat.setParameter(SpeechConstant.ASR_SOURCE_PATH, "sdcard/XXX/XXX.pcm");return mIat;}/*** 获取解析出的字符串内容*/private static String getFormatResult(RecognizerResult results) {String text = parseIatResult(results.getResultString());String sn = null;// 读取json结果中的sn字段try {JSONObject resultJson = new JSONObject(results.getResultString());sn = resultJson.optString("sn");} catch (JSONException e) {e.printStackTrace();}// 用HashMap存储听写结果HashMap<String, String> mIatResults = new LinkedHashMap<>();mIatResults.put(sn, text);StringBuilder resultBuffer = new StringBuilder();for (String key : mIatResults.keySet()) {resultBuffer.append(mIatResults.get(key));}return resultBuffer.toString();}private static String parseIatResult(String json) {StringBuilder ret = new StringBuilder();try {JSONArray words = new JSONObject(new JSONTokener(json)).getJSONArray("ws");for (int i = 0; i < words.length(); i++) {// 转写结果词,默认使用第一个结果JSONArray items = words.getJSONObject(i).getJSONArray("cw");JSONObject obj = items.getJSONObject(0);ret.append(obj.getString("w"));}} catch (Exception e) {e.printStackTrace();}return ret.toString();}/*** 上传语音消息,然后发送语音消息*/public static void uploadVoiceAndSendMsg(BaseActivity activity, RecordBean recordBean) {if (recordBean == null || recordBean.aacPath == null) return;final File file = new File(recordBean.aacPath);if (!file.exists()) {ToastHelper.showErrorToast("录音文件不存在");return;}if (AccountManager.getInstance().getServiceInfo() == null) {ToastHelper.showToastInThread("您尚未登录");return;}activity.showLoadingDialog();//**************************************************上传语音消息****************************************String keyQN = LoginConnection.getUploadKey(recordBean.aacPath);HBApplication.getInstance().getUploadManager().put(recordBean.aacPath,keyQN,LoginConnection.getUploadToken(),(key, info, res) -> {L.i("上传语音骑牛key:" + key);//*************************************发送语音消息*****************************************//当上传七牛成功后发送一条socket消息,当收到响应时(会返回一个msgid)说明此socket消息发送成功了recordBean.qnKey = key;SendMsgUtils.sendNormalVoiceMsg(activity, recordBean);},new UploadOptions(null, null, false, (key, percent) -> {//if (activity.dialog != null) activity.dialog.update((int) percent * 100);}, null));}/*** 【读】SD卡也即/mnt/sdcard/目录下的文件*/public static byte[] readSDFile(String fileName) {if (TextUtils.isEmpty(fileName)) return null;File file = new File(fileName);if (!file.exists()) return null;try {InputStream ins = new FileInputStream(file);byte[] data = new byte[ins.available()];ins.read(data);ins.close();return data;} catch (Exception e) {e.printStackTrace();}return null;}}
I/RecordBean: 包青天bqt(RecordBean.java:39)#<init>【语音文件保存路径:/storage/emulated/0/voice/pcm_2017_07_04 17_05_23 100.pcm/storage/emulated/0/voice/aac_2017_07_04 17_05_23 100.aac】I/ChatActivity$5: 包青天bqt(ChatActivity.java:908)#pressStop【讯飞SDK开始音频流识别】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:97)#onVolumeChanged【onVolumeChanged。当前正在说话,音量大小:0。返回音频数据长度:33280】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:81)#onEndOfSpeech【onEndOfSpeech】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:106)#onEvent【onEvent:20001 session_id =iatad118bff@gz02600cb6a6553cf300】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:86)#onResult【onResult:{"sn":1,"ls":false,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":0.00,"w":"你好"}]}]}。格式化内容:你好。isLast:false】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:106)#onEvent【onEvent:20001 session_id =iatad118bff@gz02600cb6a6553cf300】I/VoiceUtils: 包青天bqt(VoiceUtils.java:222)#lambda$uploadVoiceAndSendMsg$1【上传语音骑牛key:_F-UBAN-YeQIB.aac】I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:87)#sendNormalVoiceMsg【发送语音消息。content={"duration":1,"path":"_F-UBAN-YeQIB.aac"}。clientid=1499159123100】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:86)#onResult【onResult:{"sn":2,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":0.00,"w":"。"}]}]}。格式化内容:。。isLast:true】I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:89)#onResult【完整结果为:你好。】I/ChatActivity: 包青天bqt(ChatActivity.java:1703)#onEventMainThread【发送语音响应:msgid=13878 clientid=1499159123100】I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:113)#sendVoiceContentMsg【发送语音文本消息。content=你好。。msgid=13878】I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:115)#sendVoiceContentMsg【成功删除pcm临时文件】I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:116)#sendVoiceContentMsg【成功删除aac临时文件】I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:124)#sendVoiceContentMsg【成功移除recordBean:1499159123100】
I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:148)#qryVoiceContentMsg【查询语音文本消息。msgid=13879。groupId=36】I/ChatActivity: 包青天bqt(ChatActivity.java:1723)#onEventMainThread【查询语音文本响应。msgID:13879 内容:需要。】
标签:案例 ++ direct min ram stop dma 注意 empty
原文地址:http://www.cnblogs.com/baiqiantao/p/7117835.html