同时开启录音录像,报错mediarecorder start failed -38

背景:

由于有些手机(如小米、魅族、华为)添加了权限管理,所以项目中需要在正式录音/录像之前,检查是否有权限。

测试中发现,

  • 小米手机没有录音权限的时候,文件可以生成,但是一开始有大小,后面就不变了。
  • 魅族手机没有录音权限的时候,有时候不生成文件,有时候生成了文件,但是文件大小为0。
  • 华为手机待测试。。。(求测试机啊。。。)

判断是否有录音权限的代码:

PackageManager pm = activity.getPackageManager();
String permission_name = "android.permission.RECORD_AUDIO";
boolean permission = (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission_name, activity.getPackageName()));
if(!permission){
    quitWithFailure(IDY_ERROR.AA_PERMISSION_DENIED);
    return;
}

但是测试下来,发现上述手机对于权限的判断,并不能返回正确的值。

第一次安装的时候,可以正确判断,但是允许之后,再改成拒绝,那么这个判断就不准了。 一直是返回的true。这样一来,依然不能保证正确录音。

想来想去,最终决定使用一个笨方法,即正式录音之前,先开始一个测试录音,录个几秒钟,然后每隔2s钟检查一次,录音文件是否存在,且大小是否增大,检查3次之后,符合正常情况的,则认为录音功能正常。否则,提示用户检查权限。

	//2015-4-13 增加检查录音权限
	private void checkMediaPermission(final int answer_type){
		//如果不需要录音,不需要检查。 如果需要录音,则检查。
		final RecordModel recordModel = FlysurveyApplication.recordDao.getRecordModel(project_id);
		if (recordModel != null && !recordModel.getAudio_type().equals("")) {// 需要录音
			//如果已经检查通过了,则不需要再检查
			if (sp.getBooleanValue(project_id, false)) {
				MyLog.getInstance().writeMessage(TAG, "答题前检查权限:已经检查通过,直接通过");
				Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
				Bundle bundle = new Bundle();
				bundle.putSerializable("projectmodel", projectModel);
				intent.putExtras(bundle);
				intent.putExtra("uuid", "");
				intent.putExtra("answer_type", answer_type);
				startActivity(intent);
				return;
			}
			PackageManager pm = getPackageManager();
			String permission_name = "android.permission.RECORD_AUDIO";
			boolean permission = (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission_name, getPackageName()));
			if(!permission){
				//检查不通过,提示检查权限
				MyLog.getInstance().writeMessage(TAG, "答题前检查权限:没有权限");
				Toast.makeText(getApplication(), R.string.answer_failed_need_audio_permission, Toast.LENGTH_LONG).show();
		    	return;
		    }
			
			progressDialog = ProgressDialog.show(context, "", "正在检测答卷环境,请稍后。。。");
			Thread testRecordThread = new Thread(new Runnable() {
				
				@Override
				public void run() {
					
					//1.开启一个录音
					final AudioRecorderUtil audioRecorder = new AudioRecorderUtil(recordModel,
							projectModel.getProject_identifier(), project_id);
					final File tmp_file = audioRecorder.testRecord();
					//2.每隔2s钟检查一次,判断文件是否存在,若存在,则判断文件大小是否变化
					if (tmp_file != null) {
						final Timer timer = new Timer();
						timer.schedule(new TimerTask() {
							
							private boolean pass_test = false; //检查结果 true通过,false未通过
							private long last_length = 0; //上一次的文件大小
							private int check_count = 0; //检查次数

							@Override
							public void run() {
								Log.d(TAG, "check");
								
								long current_length = tmp_file.length();
								pass_test = tmp_file.exists() && current_length > last_length;
								last_length = current_length;
								if (check_count > 1) {
									//3.3次检查之后,若通过,则可以答题。 否则,不能答题。
									if (pass_test) {
										sp.putBooleanValue(project_id, true);
										MyLog.getInstance().writeMessage(TAG, "答题前检查权限:三次验证通过");
										Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
										Bundle bundle = new Bundle();
										bundle.putSerializable("projectmodel", projectModel);
										intent.putExtras(bundle);
										intent.putExtra("uuid", "");
										intent.putExtra("answer_type", answer_type);
										startActivity(intent);
										phandler.sendEmptyMessage(3);
									}else{
										//检查不通过,提示检查权限
										sp.putBooleanValue(project_id, false);
										MyLog.getInstance().writeMessage(TAG, "答题前检查权限:三次验证失败");
										phandler.sendEmptyMessage(1);
									}
									
									//只检查3次,3次之后停止检查,删除测试录音文件
									timer.cancel();
									audioRecorder.closeTestRecord();
									tmp_file.delete();
								}
								check_count ++;
							}
						}, 0, 2000);
					}else{
						//测试录音开启失败
						sp.putBooleanValue(project_id, false);
						MyLog.getInstance().writeMessage(TAG, "答题前检查权限:开启测试录音失败");
						phandler.sendEmptyMessage(2);
					}
				}
			});
			testRecordThread.start();
		}else{
			Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
			Bundle bundle = new Bundle();
			bundle.putSerializable("projectmodel", projectModel);
			intent.putExtras(bundle);
			intent.putExtra("uuid", "");
			intent.putExtra("answer_type", answer_type);
			startActivity(intent);
		}
		
//		if (!recordModel.getVideo_type().equals("")) {// 录像
//			
//		}
	}
测试下来,ok。

然后,类似的,录像是否也需要检查呢?于是,想当然的,按照录音的方式,把录像也搞了下。。。并且使用了两个线程,美其名曰,一起检查,省时间啊!

然后写了一个又臭又长的方法:

//2015-4-13 增加检查录音权限
	private void checkMediaPermission(final int answer_type){
		//如果不需要录音,不需要检查。 如果需要录音,则检查。
		final RecordModel recordModel = FlysurveyApplication.recordDao.getRecordModel(project_id);
		final boolean needAudio = !recordModel.getAudio_type().equals("");
		final boolean needVideo = !recordModel.getVideo_type().equals("");
		
		if (needAudio) {
			MyLog.getInstance().writeMessage(TAG, "【录音】需要录音权限");
		}
		
		if (needVideo) {
			MyLog.getInstance().writeMessage(TAG, "【录像】需要录像权限");
		}
		
		boolean pass_audio = needAudio ? false : true; //不需要录音的时候,当作通过验证
		boolean pass_video = needVideo ? false : true;
		
		if (recordModel != null && (needAudio || needVideo)) {// 需要录音或录像
			// ======第一步检查: 如果已经检查通过了,则不需要再检查 ==========
			if (needAudio && sp.getBooleanValue(project_id + "_audio", false)) {
				MyLog.getInstance().writeMessage(TAG, "【录音】第一步检查通过,以前允许过");
				pass_audio = true;
			}else{
				MyLog.getInstance().writeMessage(TAG, "【录音】不需要录音,或,第一步检查失败,以前未通过");
			}
			
			if(needVideo && sp.getBooleanValue(project_id + "_video", false)){
				MyLog.getInstance().writeMessage(TAG, "【录像】:第一步检查通过,以前允许过");
				pass_video = true;
			}else{
				MyLog.getInstance().writeMessage(TAG, "【录像】不需要录像,或,第一步检查失败,以前未通过");
			}
			
			if (pass_audio && pass_video) {
				Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
				Bundle bundle = new Bundle();
				bundle.putSerializable("projectmodel", projectModel);
				intent.putExtras(bundle);
				intent.putExtra("uuid", "");
				intent.putExtra("answer_type", answer_type);
				startActivity(intent);
				return;
			}
			
			// ===== 第二步检查: 检查权限 =========
			PackageManager pm = getPackageManager();
			if (needAudio) {
				String permission_name = "android.permission.RECORD_AUDIO";
				pass_audio = (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission_name, getPackageName()));
				if(!pass_audio){
					//检查不通过,提示检查权限
					MyLog.getInstance().writeMessage(TAG, "【录音】第二步检查不通过,没有权限");
					Toast.makeText(getApplication(), R.string.answer_failed_need_audio_permission, Toast.LENGTH_LONG).show();
			    	return;
			    }else{
			    	MyLog.getInstance().writeMessage(TAG, "【录音】第二步检查通过,有权限");
			    }
			}
			
			if (needVideo) {
				String permission_name = "android.permission.CAMERA";
				pass_video = (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission_name, getPackageName()));
				if(!pass_video){
					//检查不通过,提示检查权限
					MyLog.getInstance().writeMessage(TAG, "【录像】第二步检查不通过,没有权限");
					Toast.makeText(getApplication(), R.string.answer_failed_need_video_permission, Toast.LENGTH_LONG).show();
			    	return;
			    }else{
			    	MyLog.getInstance().writeMessage(TAG, "【录像】第二步检查通过,有权限");
			    }
			}
			
			if (!pass_video && !pass_audio) {
				//两个都不通过
				Toast.makeText(getApplication(), R.string.answer_failed_need_audio_video_permission, Toast.LENGTH_LONG).show();
		    	return;
			}else if(!pass_video){
				//录像不通过
				Toast.makeText(getApplication(), R.string.answer_failed_need_video_permission, Toast.LENGTH_LONG).show();
		    	return;
			}else if(!pass_audio){
				//录音不通过
				Toast.makeText(getApplication(), R.string.answer_failed_need_audio_permission, Toast.LENGTH_LONG).show();
		    	return;
			}
			
			// ===== 第三步检查: 生成测试文件,判断大小 =====
			progressDialog = ProgressDialog.show(context, "", "正在检测答卷环境,请稍后。。。");
			if (needAudio) {
				final Thread testRecordThread = new Thread(new Runnable() {
					
					@Override
					public void run() {
						MyLog.getInstance().writeMessage(TAG, "【录音】准备第三步检查,开启测试录音");
						//1.开启一个录音
						audioRecorder = new AudioRecorderUtil(recordModel,
								projectModel.getProject_identifier(), project_id);
						audioRecorder.activity = ProjectActivity.this;
						tmp_audio_file = audioRecorder.testRecord();
					}
				});
				testRecordThread.start();
			}
			
			if (needVideo) {
				final Thread testVideoThread = new Thread(new Runnable() {
					@Override
					public void run() {
						MyLog.getInstance().writeMessage(TAG, "【录像】准备第三步检查,开启测试录像");
						//1.开启一个录像
						videoRecorder = new VideoRecorderUtil(_surfaceView, recordModel,
								projectModel.getProject_identifier(), project_id);
						videoRecorder.activity = ProjectActivity.this;
						tmp_video_file = videoRecorder.testRecord();
					}
				});
				testVideoThread.start();
			}
			
			final Timer timer = new Timer();
			timer.schedule(new TimerTask() {
				
				private boolean pass_test_audio = needAudio ? false : true; //检查结果 true通过,false未通过
				private boolean pass_test_video = needVideo ? false : true; //检查结果 true通过,false未通过
				private long last_audio_length = 0; //上一次的文件大小
				private long last_video_length = 0; //上一次的文件大小
				private int check_count = 0; //检查次数

				@Override
				public void run() {
					Log.d(TAG, "check");
					
					if (needAudio) {
						if (tmp_audio_file != null) {
							long current_length = tmp_audio_file.length();
							pass_test_audio = tmp_audio_file.exists() && current_length > last_audio_length;
							last_audio_length = current_length;
						}
						
						MyLog.getInstance().writeMessage(TAG, "【Timer_" + check_count +"】是否通过录音测试:" + pass_test_audio + ", 上一次大小:" + last_audio_length);
					}
					
					if (needVideo) {
						if (tmp_video_file != null) {
							long current_length = tmp_video_file.length();
							pass_test_video = tmp_video_file.exists() && current_length > last_video_length;
							last_video_length = current_length;
						}
						
						MyLog.getInstance().writeMessage(TAG, "【Timer_" + check_count +"】是否通过录像测试:" + pass_test_video + ", 上一次大小" + last_video_length);
					}
					
					if (check_count > 1) {
						//3.3次检查之后,若通过,则可以答题。 否则,不能答题。
						if (!pass_test_video && !pass_test_audio) {
							//检查都不通过,提示检查权限
							sp.putBooleanValue(project_id + "_audio", false);
							sp.putBooleanValue(project_id + "_video", false);
							MyLog.getInstance().writeMessage(TAG, "答题前检查录音/录像权限:三次验证都失败");
							phandler.sendEmptyMessage(AUDIO_VIDEO_FORBID);
							
						}else if(!pass_test_audio){
							//检查录音不通过,提示检查权限
							sp.putBooleanValue(project_id + "_audio", false);
							MyLog.getInstance().writeMessage(TAG, "答题前检查录音权限:三次验证失败");
							phandler.sendEmptyMessage(AUDIO_FORBID);
						}else if(!pass_test_video){
							//检查录音不通过,提示检查权限
							sp.putBooleanValue(project_id + "_video", false);
							MyLog.getInstance().writeMessage(TAG, "答题前检查录像权限:三次验证失败");
							phandler.sendEmptyMessage(VIDEO_FORBID);
						}else{

							sp.putBooleanValue(project_id + "_video", true);
							sp.putBooleanValue(project_id + "_audio", true);
							
							MyLog.getInstance().writeMessage(TAG, "答题前检查录音/录像权限:三次验证都通过");
							
							Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
							Bundle bundle = new Bundle();
							bundle.putSerializable("projectmodel", projectModel);
							intent.putExtras(bundle);
							intent.putExtra("uuid", "");
							intent.putExtra("answer_type", answer_type);
							startActivity(intent);
							phandler.sendEmptyMessage(ALLPASS);
						}
						
						
						//只检查3次,3次之后停止检查,删除测试录音文件
						timer.cancel();
						
						if (needAudio) {
							audioRecorder.closeTestRecord();
							if (tmp_audio_file != null && tmp_audio_file.exists()) {
								tmp_audio_file.delete();
							}
						}
						
						if (needVideo) {
							videoRecorder.closeTestRecord();
							if (tmp_video_file != null && tmp_video_file.exists()) {
								tmp_video_file.delete();
							}
						}
					}
					check_count ++;
				}
			}, 2000, 2000);
			
		}else{
			Intent intent = new Intent(ProjectActivity.this, AnswerPageActivity.class);
			Bundle bundle = new Bundle();
			bundle.putSerializable("projectmodel", projectModel);
			intent.putExtras(bundle);
			intent.putExtra("uuid", "");
			intent.putExtra("answer_type", answer_type);
			startActivity(intent);
		}
	}

此方法,只需要录音或者只需要录像的时候,都没有问题。但是,一旦既需要录音、又需要录像,死活报错!

同时开启录音录像,报错mediarecorder start failed -38_第1张图片

各种查原因,找到资料, http://stackoverflow.com/questions/23971817/mediarecorder-start-failed-38,看起来是开始录像的时候,已经开启录音了,抢麦克风了。

好吧,难怪正式答题的时候,录音、录像不能同时使用。自己写过的代码,自己也不记得。纠结下就当加深印象了。

最终,把录音和录像分开来,先检测录音,通过了再检测录像。

检测录音的代码:

/**
	 * 2015-4-13 增加检查录音权限
	 * 先检测录音,录音通过再检测录像
	 * @param answer_type
	 */
	private void checkMediaPermission(final int answer_type) {
		// 如果不需要录音,不需要检查。 如果需要录音,则检查。
		final RecordModel recordModel = FlysurveyApplication.recordDao
				.getRecordModel(project_id);
		if (recordModel == null) {
			goToAnswerPage(answer_type);
			return;
		}

		final boolean needAudio = !recordModel.getAudio_type().equals("");

		// 需要录音,则先判断录音,后判断录像。不需要录音,直接判断录像
		if (needAudio) {
			MyLog.getInstance().writeMessage(TAG, "【录音】需要录音权限");
			boolean pass_audio = false;
			// ======第一步检查: 如果已经检查通过了,则不需要再检查 ==========
			if (sp.getBooleanValue(project_id + "_audio", false)) {
				MyLog.getInstance().writeMessage(TAG, "【录音】第一步检查通过,以前允许过");
				pass_audio = true;
				checkVideoPermission(recordModel, answer_type);
				return;
			} else {
				MyLog.getInstance().writeMessage(TAG, "【录音】第一步检查失败,以前未通过");
			}
			
			// ===== 第二步检查: 检查权限 =========
			PackageManager pm = getPackageManager();
			String permission_name = "android.permission.RECORD_AUDIO";
			pass_audio = (PackageManager.PERMISSION_GRANTED == pm
					.checkPermission(permission_name, getPackageName()));
			if (!pass_audio) {
				// 检查不通过,提示检查权限
				MyLog.getInstance().writeMessage(TAG, "【录音】第二步检查不通过,没有权限");
				Toast.makeText(getApplication(),
						R.string.answer_failed_need_audio_permission,
						Toast.LENGTH_LONG).show();
				return;
			} else {
				MyLog.getInstance().writeMessage(TAG, "【录音】第二步检查通过,有权限");
			}

			// ===== 第三步检查: 生成测试文件,判断大小 =====
			progressDialog = ProgressDialog
					.show(context, "", "正在检测答卷环境,请稍后。。。");
			final Thread testRecordThread = new Thread(new Runnable() {

				@Override
				public void run() {
					MyLog.getInstance().writeMessage(TAG, "【录音】准备第三步检查,开启测试录音");
					// 1.开启一个录音
					audioRecorder = new AudioRecorderUtil(recordModel,
							projectModel.getProject_identifier(), project_id);
					audioRecorder.activity = ProjectActivity.this;
					tmp_audio_file = audioRecorder.testRecord();
				}
			});
			testRecordThread.start();

			final Timer timer = new Timer();
			timer.schedule(new TimerTask() {

				private boolean pass_test_audio = false; // 检查结果 true通过,false未通过
				private long last_audio_length = 0; // 上一次的文件大小
				private int check_count = 0; // 检查次数

				@Override
				public void run() {

					if (tmp_audio_file != null) {
						long current_length = tmp_audio_file.length();
						pass_test_audio = tmp_audio_file.exists()
								&& current_length > last_audio_length;
						last_audio_length = current_length;
					}

					MyLog.getInstance().writeMessage(
							TAG,
							"【AudioTimer_" + check_count + "】是否通过录音测试:"
									+ pass_test_audio + ", 上一次大小:"
									+ last_audio_length);

					if (check_count > 2) {
						//3. 3次检查之后,关闭测试文件。(必须先关闭,否则影响后面测试录像)
						// 只检查3次,3次之后停止检查,删除测试录音文件
						timer.cancel();
						audioRecorder.closeTestRecord();
						if (tmp_audio_file != null && tmp_audio_file.exists()) {
							tmp_audio_file.delete();
						}
						
						//若通过,则可以答题。 否则,不能答题。
						if (!pass_test_audio) {
							// 检查录音不通过,提示检查权限
							sp.putBooleanValue(project_id + "_audio", false);
							MyLog.getInstance().writeMessage(TAG,
									"答题前检查录音权限:三次验证失败");
							phandler.sendEmptyMessage(AUDIO_FORBID);
						} else {
							sp.putBooleanValue(project_id + "_audio", true);
							MyLog.getInstance().writeMessage(TAG,
									"答题前检查录音权限:三次验证都通过");

							checkVideoPermission(recordModel, answer_type);
						}
					}
					check_count++;
				}
			}, 2000, 2000);
		}else{
			checkVideoPermission(recordModel, answer_type);
		}
	}

检测录像的代码:

/**
	 * 检测录像
	 * @param recordModel
	 * @param answer_type
	 */
	private void checkVideoPermission(final RecordModel recordModel, final int answer_type){
		final boolean needVideo = !recordModel.getVideo_type().equals("");
		if (needVideo) {
			// 录音检查通过,开始检查录像
			MyLog.getInstance().writeMessage(TAG, "【录像】需要录像权限");
			boolean pass_video = false;
			// ======第一步检查: 如果已经检查通过了,则不需要再检查 ==========
			if (sp.getBooleanValue(project_id + "_video", false)) {
				MyLog.getInstance().writeMessage(TAG,
						"【录像】:第一步检查通过,以前允许过");
				pass_video = true;
				sp.putBooleanValue(project_id + "_video",
						true);
				goToAnswerPage(answer_type);
				phandler.sendEmptyMessage(ALLPASS);
				return;
			} else {
				MyLog.getInstance().writeMessage(TAG,
						"【录像】第一步检查失败,以前未通过");
			}

			// ===== 第二步检查: 检查权限 =========
			PackageManager pm = getPackageManager();
			String permission_name = "android.permission.CAMERA";
			pass_video = (PackageManager.PERMISSION_GRANTED == pm
					.checkPermission(permission_name, getPackageName()));
			if (!pass_video) {
				// 检查不通过,提示检查权限
				MyLog.getInstance().writeMessage(TAG,
						"【录像】第二步检查不通过,没有权限");
				sp.putBooleanValue(project_id + "_video",
						false);
				phandler.sendEmptyMessage(VIDEO_FORBID);
				return;
			} else {
				MyLog.getInstance()
						.writeMessage(TAG, "【录像】第二步检查通过,有权限");
			}

			// ===== 第三步检查: 生成测试文件,判断大小 =====
			if (progressDialog == null) {
				progressDialog = ProgressDialog.show(context, "",
						"正在检测答卷环境,请稍后。。。");
			}
			
			final Thread testVideoThread = new Thread(new Runnable() {
				@Override
				public void run() {
					MyLog.getInstance().writeMessage(TAG,
							"【录像】准备第三步检查,开启测试录像");
					// 1.开启一个录像
					videoRecorder = new VideoRecorderUtil(_surfaceView,
							recordModel,
							projectModel.getProject_identifier(),
							project_id);
					videoRecorder.activity = ProjectActivity.this;
					tmp_video_file = videoRecorder.testRecord();
				}
			});
			testVideoThread.start();

			final Timer timer = new Timer();
			timer.schedule(new TimerTask() {

				private boolean pass_test_video = false;
				private long last_video_length = 0; // 上一次的文件大小
				private int check_count = 0; // 检查次数

				@Override
				public void run() {

					if (tmp_video_file != null) {
						long current_length = tmp_video_file.length();
						pass_test_video = tmp_video_file.exists()
								&& current_length > last_video_length;
						last_video_length = current_length;
					}

					MyLog.getInstance().writeMessage(
							TAG,
							"【VideoTimer_" + check_count + "】是否通过录像测试:"
									+ pass_test_video + ", 上一次大小"
									+ last_video_length);

					if (check_count > 2) {
						// 只检查3次,3次之后停止检查,删除测试录音文件
						timer.cancel();
						videoRecorder.closeTestRecord();
						if (tmp_video_file != null
								&& tmp_video_file.exists()) {
							tmp_video_file.delete();
						}
						
						// 3.3次检查之后,若通过,则可以答题。 否则,不能答题。
						if (!pass_test_video) {
							// 检查录像不通过,提示检查权限
							sp.putBooleanValue(project_id + "_video",
									false);
							MyLog.getInstance().writeMessage(TAG,
									"答题前检查录像权限:三次验证失败");
							phandler.sendEmptyMessage(VIDEO_FORBID);
						} else {

							sp.putBooleanValue(project_id + "_video",
									true);

							MyLog.getInstance().writeMessage(TAG,
									"答题前检查录像权限:三次验证都通过");

							goToAnswerPage(answer_type);
							phandler.sendEmptyMessage(ALLPASS);
						}
					}
					check_count++;
				}
			}, 2000, 2000);

		} else {
			sp.putBooleanValue(project_id + "_video",
					true);
			goToAnswerPage(answer_type);
			phandler.sendEmptyMessage(ALLPASS);
		}
	}

说实话,代码看得我头疼。。。等长本事了再来优化吧。。。


你可能感兴趣的:(android)