网站如何调用微博/线下推广的渠道和方法
这两个在集成环信实现IM功能,在开发过程中遇到一个问题,发送文字、表情、图片、位置都是正常的,但是语音就是一直发送不成功,并且只是在第一次安装app登录账号之后发送语音的时候才会出现发送不成功的问题,如果把app杀掉重新打开或者退出账号重新登录再发送语音就能成功发送了。一开始以为是某些初始化的代码没有加,但是仔细查看该加的初始化的东西都加了,没办法,问题要解决啊,于是开始一行一行的看环信的代码,最开始单点到ACTION_UP的时候发现调用recorder.stop()方法的时候会一直报IllegalStateException,显示在这句话这里找原因,一直没找到解决办法。后来开始转换思路,既然recorder.stop()方法会出现问题,肯定是创建的时候哪里出了问题,然后开始在ACTION_DOWN的地方找原因,最后发现是环信给的代码里有一个文件创建的问题,导致文件创建不成功,语音无法发送成功。在说这个问题之前先了解一下环信发送语音功能的实现过程。
环信的聊天页面的底部是一个自定义View,
当点击“按住说话”按钮的时候会出发它的onTouch事件,
//发送语音按钮被点击的时候
buttonPressToSpeak.setOnTouchListener(new OnTouchListener() {@Override
public boolean onTouch(View v, MotionEvent event) {if (listener != null) {return listener.onPressToSpeakBtnTouch(v, event);}return false;}});
}
然后这个listener会回调到聊天页面,EaseChatFragment,在这个页面处理它的touch事件,
/**
* 发送语音
* @param v
* @param event
* @return
*/
@Override
public boolean onPressToSpeakBtnTouch(View v, MotionEvent event) {return voiceRecorderView.onPressToSpeakBtnTouch(v, event, new EaseVoiceRecorderCallback() {@Override
public void onVoiceRecordComplete(String voiceFilePath, int voiceTimeLength) {sendVoiceMessage(voiceFilePath, voiceTimeLength);}});
}
我们进入onPressSpeakBtnTouch()方法,会看到在这里面对touch的按下、移动、抬起手势进行了处理。
public boolean onPressToSpeakBtnTouch(View v, MotionEvent event, EaseVoiceRecorderCallback recorderCallback) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:try {if (EaseChatRowVoicePlayClickListener.isPlaying)EaseChatRowVoicePlayClickListener.currentPlayListener.stopPlayVoice();v.setPressed(true);startRecording();} catch (Exception e) {v.setPressed(false);}return true;case MotionEvent.ACTION_MOVE:if (event.getY() < 0) {showReleaseToCancelHint();} else {showMoveUpToCancelHint();}return true;case MotionEvent.ACTION_UP:v.setPressed(false);if (event.getY() < 0) {// discard the recorded audio. discardRecording();} else {// stop recording and send voice file try {int length = stopRecoding();if (length > 0) {if (recorderCallback != null) {recorderCallback.onVoiceRecordComplete(getVoiceFilePath(), length);}} else if (length == EMError.FILE_INVALID) {Toast.makeText(context, R.string.Recording_without_permission, Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, R.string.The_recording_time_is_too_short, Toast.LENGTH_SHORT).show();}} catch (Exception e) {e.printStackTrace();Toast.makeText(context, R.string.send_failure_please, Toast.LENGTH_SHORT).show();}}return true;default:discardRecording();return false;} }
ACTION_DOWN的时候,开始录音,首先是先判断一下当前是否正处于录音状态,如果是,先关闭录音,重新录,然后开始录音,接下来就是录音的代码了:在录音的代码中,带删除线的那一行代码是环信原来自己的代码,红色标记的部分是我自己添加进去的,就是这里出的错,这里要执行的操作是创建对应的录音文件,但是创建一个文件的时候需要先创建这个文件对应的目录,这里没有创建目录的过程就直接创建文件,所以在执行接下来的语句的时候就会报no such file or directory错误。
public String startRecording(Context appContext) {file = null;try {// need to create recorder every time, otherwise, will got exception // from setOutputFile when try to reuse if (recorder != null) {recorder.release();recorder = null;}recorder = new MediaRecorder();recorder.setAudioSource(MediaRecorder.AudioSource.MIC);recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);recorder.setAudioChannels(1); // MONO recorder.setAudioSamplingRate(8000); // 8000Hz recorder.setAudioEncodingBitRate(64); // seems if change this to voiceFileName = getVoiceFileName(EMClient.getInstance().getCurrentUser());voiceFilePath = PathUtil.getInstance().getVoicePath() + "/" + voiceFileName;//file = new File(voiceFilePath);File file2=PathUtil.getInstance().getVoicePath();if(!file2.exists()){file2.mkdirs();}file = new File(voiceFilePath);if(!file.exists()){file.createNewFile();}recorder.setOutputFile(file.getAbsolutePath());recorder.prepare();isRecording = true;recorder.start();} catch (IOException e) {EMLog.e("voice", "prepare() failed");}new Thread(new Runnable() {@Override public void run() {try {while (isRecording) {android.os.Message msg = new android.os.Message();msg.what = recorder.getMaxAmplitude() * 13 / 0x7FFF;handler.sendMessage(msg);SystemClock.sleep(100);}} catch (Exception e) {EMLog.e("voice", e.toString());}}}).start();startTime = new Date().getTime();EMLog.d("voice", "start voice recording to file:" + file.getAbsolutePath());return file == null ? null : file.getAbsolutePath(); }
ACTION_MOVE的时候,根据手指所在的位置执行不同的结果。
public void showReleaseToCancelHint() {recordingHint.setText(context.getString(R.string.release_to_cancel));recordingHint.setBackgroundResource(R.drawable.ease_recording_text_hint_bg); }public void showMoveUpToCancelHint() {recordingHint.setText(context.getString(R.string.move_up_to_cancel));recordingHint.setBackgroundColor(Color.TRANSPARENT); }
ACTION_UP的时候,停止录音,保存语音文件。
public int stopRecoding() {if(recorder != null){isRecording = false;recorder.stop();recorder.release();recorder = null;if(file == null || !file.exists() || !file.isFile()){return EMError.FILE_INVALID;}if (file.length() == 0) {file.delete();return EMError.FILE_INVALID;}int seconds = (int) (new Date().getTime() - startTime) / 1000;EMLog.d("voice", "voice recording finished. seconds:" + seconds + " file length:" + file.length());return seconds;}return 0; }