语音消息以及未读提醒连续播放
来源:互联网 发布:连接数据库出现异常 编辑:程序博客网 时间:2024/06/04 17:54
微信引入语音聊天后,使得语音聊天非常火。最近做了一个关于语音聊天demo,现在介绍下整个实现过程:包括录音、保存音频文件至SD卡、发送录音文件、接收新语音消息红点提醒、播放暂停、未读语音消息连续播放、播放模式切换。
录音部分包括:音频源、音频格式、编码器、音频通道、采样率、编码率等步骤,具体实现如下:
try {this.recorder = new MediaRecorder();this.recorder.setAudioSource(AudioSource.MIC);//音频源this.recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);//音频格式this.recorder.setAudioEncoder(1);//编码器this.recorder.setAudioChannels(1);//音频通道this.recorder.setAudioSamplingRate(8000);//采样率this.recorder.setAudioEncodingBitRate(64);//编码率this.voiceFilePath = getVoiceFilePath();this.file = new File(this.voiceFilePath);this.recorder.setOutputFile(this.file.getAbsolutePath());//输出至指定文件路径this.recorder.prepare();//准备录音this.isRecording = true;this.recorder.start();//开始录音 }catch(IOException localIOException) {Log.e(TAG, "voice prepare() failed"); }
录音过程中根据音量大小刷新动画,100ms刷新一遍,开一个子线程操作,通过handler发送消息通知主线程:
new Thread(new Runnable() {public void run() {try {while(VoiceRecorder.this.isRecording) {Message localMessage = new Message();double ratio = (double)recorder.getMaxAmplitude();localMessage.arg1 = (int)((14*ratio)/32768);localMessage.what = MessageActivity.VOICE_REFRESH;VoiceRecorder.this.handler.sendMessage(localMessage); SystemClock.sleep(100L);}}catch(Exception localException) {Log.e(TAG , localException.toString());}}}).start();
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case VOICE_REFRESH: if (msg.arg1 <= 1) micImage.setImageDrawable(micImages[0]); else micImage.setImageDrawable(micImages[msg.arg1 >= 14 ? 14 : msg.arg1 - 1]); break; } } };
录音时长可以自己设定,最后的10秒倒计时,也是开一个子线程进行计时,通过handler通知主线程提示用户:
new Thread(new Runnable() {@Overridepublic void run() {while(isRecording) {try {voice_duration++;if(MAX_DURATION - voice_duration < TIME_TO_COUNT_DOWN) {//开始进入倒计时10sMessage msg = handler.obtainMessage();if(MAX_DURATION - voice_duration < 0) {//达到最大时长,停止录音msg.arg1 = stopRecoding();msg.what = MessageActivity.VOICE_LONG;voice_duration = 0;handler.sendMessage(msg);}else {msg.arg1 = MAX_DURATION - voice_duration;msg.what = MessageActivity.VOICE_TIP;handler.sendMessage(msg);}}Thread.sleep(1000);//每隔1秒计时1次}catch(Exception e) {e.printStackTrace();}}}}).start();
录音完毕,需要停止并释放recorder,计算录音时间,保存录音文件。
public int stopRecoding() {if(this.recorder != null) {this.isRecording = false;this.voice_duration = 0;this.recorder.stop();//停止录音this.recorder.release();//释放录音器this.recorder = null;int i = (int)(new Date().getTime() - this.startTime) / 1000;//计算录音时间,并保存至指定路径Log.e("voice", " voice recording finished. seconds:" + i + " file length:" + new File(this.voiceFilePath).length());return i;}return 0;}
播放语音消息,使用MediaPayer进行播放,步骤包括:设置报放音频流类型、播放数据源、准备以及开始播放、设置播放完毕回调监听:
((Activity)context).setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);requestAudioFocus();mediaPlayer = new MediaPlayer();mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);//音频流类型try {mediaPlayer.setDataSource(filePath);//播放数据源mediaPlayer.prepare();//准备播放mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {//设置播放完毕的回调// TODO Auto-generated method stubmMediaPlayerCallback.onStop();stopPlayVoice(); // stop animation}});isPlaying = true;playSource = filePath;currentPlayListener = this;mediaPlayer.start();//开始播放mMediaPlayerCallback.onStart();}catch(Exception e) {Log.d(TAG, "playErr--" + e.toString());}
在播放过程中,用户也许会暂停播放,那么需要暂停当前MediaPlayer播放,并且记录当前已播放的时长,以便下次续播:
public void stopPlayVoice() {if(mediaPlayer != null) {mediaPlayer.stop();mediaPlayer.release();mediaPlayer = null;mMediaPlayerCallback.onStop();}abandonAudioFocus();isPlaying = false;}
播放模式切换,两种模式:听筒播放、扬声器播放,并且在onStop()方法里,应该把当前设置模式写入sharePreference里保存起来,以便下次记住并调用设置后的播放模式。
mMediaPlayerCallback = callback;AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);if(MessageActivity.outPlayMode) {//扬声器播放audioManager.setSpeakerphoneOn(true);audioManager.setMode(AudioManager.MODE_NORMAL);} else {//听筒播放audioManager.setSpeakerphoneOn(false);audioManager.setMode(AudioManager.MODE_IN_CALL);}
接收到的未读语音消息,旁边有小红点提示:
private void handleAudioMessage(View view, ViewHolder holder, AudioMessage msg,int read) { if (msg.getSendOrReceive() == Provider.MessageColumns.MSG_SEND) {//发送的语音消息 ...... } else {//接收的语音消息 holder.imgVoiceReadTip = (ImageView) view.findViewById(R.id.img_voice_unread_tip); if (read == Provider.MessageColumns.UNREAD_AUDIO) { holder.imgVoiceReadTip.setVisibility(View.VISIBLE);//如果未读,红点显示 } else { holder.imgVoiceReadTip.setVisibility(View.GONE);//如果已读,红点消失 } } }
/** * 通过输入当前的messageId,查找后面是否有未读的语音消息,如果有,返回cursor的位置,没有返回-1; */ private int queryUnReadVoice(int id) { Cursor cursor = mAdapter.getCursor(); boolean currentPosition = false; int index = -1; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { do { int message_id = cursor.getInt(cursor.getColumnIndex(Provider.MessageColumns._ID)); if (message_id == id) { currentPosition = true; continue; } if (currentPosition) { int isRead = cursor.getInt(cursor.getColumnIndexOrThrow(Provider.MessageColumns._MESSAGE_READ)); if (isRead == Provider.MessageColumns.UNREAD_AUDIO) { index = cursor.getPosition(); break; } } } while (cursor.moveToNext()); } } return index; }
/** * 查询到未读语音消息id,通过handler发送消息通知主线程往下播放 * @param id */ private void continueToPlay(int id) { int index = queryUnReadVoice(id); if (index != -1) { Message myMsg = Message.obtain(); myMsg.what = index; handler.sendMessage(myMsg); } }
当handler收到消息后,往下播放语音消息,小红点消失,未读状态置为已读,并且把该消息状态写进数据库:
private Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { int index = msg.what; View childView = mAdapter.getView(index, null, mListView); AudioMessage message = (AudioMessage) childView.getTag(); ContentValues values = new ContentValues(); values.put(Provider.MessageColumns._MESSAGE_READ, Provider.MessageColumns.READ_AUDIO); context.getApplicationContext().getContentResolver().update(Provider.MessageColumns.CONTENT_URI, values, Provider.MessageColumns._ID + "=?", new String[]{message.getId() + ""}); mAdapter.notifyDataSetChanged(); childView.invalidate(); playAudio((ImageView) childView.findViewById(R.id.iv_audio), message); } };
录音过程中,用户可能又不想发送了,那么应该提供取消录音操作。这个可以在onTouchListener监听器的onTouch方法里设置,如果event.getAction() == MotionEvent.ACTION_MOVE,并且检测到手势是往上滑动,执行取消操作:
v.setPressed(true); if (event.getY() < 0) { // 上滑 notShowTip = true; recordingHint.setText(getString(R.string.msg_voice_do_cancel_send_2)); recordingHint.setBackgroundResource(R.drawable.recording_text_hint_bg); } else { notShowTip = false; if (VoiceRecorder.MAX_DURATION - voiceRecorder.voice_duration > VoiceRecorder.TIME_TO_COUNT_DOWN) { recordingHint.setText(getString(R.string.msg_voice_do_cancel_send_1)); recordingHint.setBackgroundColor(Color.TRANSPARENT); } } return true;<pre name="code" class="java">
当Activity失去焦点,回调onPause方法时,如果正在播放语音消息,应该暂停播放,如果正在录音也应该暂停录音:
@Override protected void onPause() { super.onPause(); audioPresenter.onPauseAudio(); try { // 停止录音 if (voiceRecorder.isRecording()) { voiceRecorder.discardRecording(); recordingContainer.setVisibility(View.INVISIBLE); } } catch (Exception e) { e.printStackTrace(); } }
相关的权限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
2 0
- 语音消息以及未读提醒连续播放
- 未读消息提醒
- 关于微信、QQ语音消息,语音电话的时候控制应用音频播放以及暂停
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- Android 之应用启动图标未读消息BadgeNumber动态提醒(如微信 QQ等)
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- 【Android】viewbadger实现未读消息红点提醒,并显示条数
- Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒
- GitHub控件之BadgeView(数字提醒) android 未读消息红点
- android 桌面快捷键未读的消息数目,未接来电,短信提醒那个右上角的数字
- android 桌面快捷键未读的消息数目,未接来电,短信提醒那个右上角的数字
- 网页播放微信多媒体语音消息
- 仿微信语音消息的录制和播放
- RTX发送消息提醒实现以及注意事项
- 移动端语音播放以及语音条拖动的实现
- Java程序员应该知道的10个调试技巧
- RadioButton设置默认选中后无法取消的问题
- CSS颜色代码大全
- Nmap命令的29个实用范例
- Filter转发报错
- 语音消息以及未读提醒连续播放
- Hadoop架构介绍——Hadoop的三种运行模式
- 十大编程算法
- 【Poj2392 Space Elevator】多重背包
- c#调用C++写的DLL,传递二维指针参数
- Method 'NewStringUTF' could not be resolved
- scala的柯里化
- PB获取应用程序路径以及exe名
- Android webView.loadData 乱码解决