之前接了一个开发测试手机麦克风寿命apk的任务,主要要求是:1、可以控制手机麦克风的开启与关闭;2、麦克风接收的声音从听筒发出;3、可设置测试时间。下面我对该应用的开发做一相关讲解并附上代码。
开发难点:1、在话筒与听筒均开启的情况下,进入话筒的声音无法直接从听筒发出,需要做相关转换输出处理;
2、时间任务的启动与停止;
3、时间设置与数据类型的转换。
解决方法:1、引入音频缓冲区作为音频接收与播放的中专(Buffer),以音频声音作为对象由缓冲区接收存储(音频录制),以缓冲区的对象作为播放对象由听筒播放出来;
2、将麦克风开启方法以线程体来定义和调用;
3、以EditText作为时间输入的控件来设置测试时长,并前值转换为长整型。
以下是代码:
主类:AndroidMikeTestActivity.java
package com.cfzz.amt;
import com.android.methed.Run_Methed;
import android.os.Bundle;
import android.app.Activity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import android.view.WindowManager;
import android.os.SystemClock;
import android.widget.Chronometer;
import java.util.Timer;
import java.util.TimerTask;
public class AndroidMikeTestActivity extends Activity implements
OnClickListener {
Run_Methed run_Metned = new Run_Methed();
Thread thread = null;
private EditText timelength;
Button playbutton, stopbutton;
private Timer timer;
private Chronometer time;
private long recordingTime = 0;
public void onRecordStart()// time start
{
time.setBase(SystemClock.elapsedRealtime() - recordingTime);// 跳过已经记录了的时间,起到继续计时的作用
time.start();
}
public void onRecordPause()// time pause
{
time.stop();
recordingTime = SystemClock.elapsedRealtime() - time.getBase();// 保存这次记录了的时间
}
public void onRecordStop()// time stop
{
recordingTime = 0;
time.setBase(SystemClock.elapsedRealtime());
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timelength = (EditText) this.findViewById(R.id.timelength);
timelength.setText("0");
time = (Chronometer) this.findViewById(R.id.chronometer);//引入计时器控件
time.setBase(SystemClock.elapsedRealtime());//初始化时间
// 按键监听
playbutton = (Button) findViewById(R.id.playbutton);
stopbutton = (Button) findViewById(R.id.stopbutton);
playbutton.setOnClickListener(this);
stopbutton.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.playbutton:
// 按钮【开】
long t = Long.parseLong("" + timelength.getText());//以小时为单位设置时间
if (t != 0) {
Thread thread = new Thread(run_Metned);//将麦克风开启方法加入线程体中
thread.start();
onRecordStart();
long s = 1000 * 3600 * t ;//将小时数转化为毫秒数
timer = new Timer(true);
TimerTask task = new TimerTask()// 计时任务设置
{
public void run() {
run_Metned.no();
onRecordPause();
timer.cancel();
}
};
timer.schedule(task, s, 1);//s毫秒后停止
} else if (t == 0) {
run_Metned.no();
} else if (timelength.getText() == null) {
run_Metned.no();
timelength.setText("0");
}
break;
case R.id.stopbutton:
// 按钮【关】
run_Metned.no();
timer.cancel();
onRecordStop();
onRecordPause();
timelength.setText("0");
break;
}
}
int i = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) //双击返回键退出
{
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
long t = Long.parseLong("" + timelength.getText());
i++;
if (i == 2 && t != 0) {
run_Metned.no();
timer.cancel();
onRecordStop();
onRecordPause();
}
if (i == 3) {
finish();
}
}
return true;
}
/*
* @Override protected void onDestroy() {
*
* thread.interrupt(); run_Metned.no(); timer.cancel(); onRecordStop();
* super.onDestroy(); }
*/
}
方法类:
Run_Methed.java
package com.android.methed;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
public class Run_Methed implements Runnable {
// 是否录放的标记
boolean isRecording = false;
static final int frequency = 44100;
@SuppressWarnings("deprecation")
static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
// 定义录放音缓冲区大小
int recBufSize, playBufSize;
// 实例化录音对象
AudioRecord audioRecord;
// 实例化播放对象
AudioTrack audioTrack;
public Run_Methed() {
// 调用getMinBufferSize方法获得录音的最小缓冲空间
recBufSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
// 调用getMinBufferSize方法获得放音最小的缓冲区大小
playBufSize = AudioTrack.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
// -----------------------------------------、
// 调用构造函数实例化录音对象
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency,
channelConfiguration, audioEncoding, recBufSize);
// 调用构造函数实例化放音对象,以听筒模式播放
audioTrack = new AudioTrack(AudioManager.<span style="background-color: rgb(51, 204, 255);">STREAM_VOICE_CALL</span>, frequency,
channelConfiguration, audioEncoding, playBufSize,
AudioTrack.MODE_STREAM);
}
// 线程体
public void run() {
try {
byte[] buffer = new byte[recBufSize];
audioRecord.startRecording();// 开始录制
audioTrack.play();// 开始播放
this.isRecording = true;
while (isRecording) {
// 从MIC保存数据到缓冲区
int bufferReadResult = audioRecord.read(buffer, 0, recBufSize);
byte[] tmpBuf = new byte[bufferReadResult];
System.arraycopy(buffer, 0, tmpBuf, 0, bufferReadResult);
// 写入数据就播放
audioTrack.write(tmpBuf, 0, tmpBuf.length);
}
audioTrack.stop();
audioRecord.stop();
} catch (Throwable t) {
}
}
// 停止方法
public void no() {
isRecording = false;
}
}
Track_Methed,java
package com.android.methed;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
public class Track_Methed implements Runnable {
// 播放暂存
int out_size;
// 放音对象
AudioTrack track = null;
public void run() {
}
// 播送方法
public void track() {
out_size = android.media.AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.STREAM_VOICE_CALL, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, out_size,
AudioTrack.MODE_STREAM);
}
}
布局文件:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp"
android:text="测试时间(小时/H)"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="12pt" />
<EditText
android:id="@+id/timelength"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:layout_marginTop="20dp"
<!-- 未输入值时会显示的内容,作为输入提示-->
android:hint="请输入测试小时数!"
android:layout_gravity="center_horizontal"
android:textSize="12pt"
<!-- 限定输入值为整数 -->
android:inputType="number"
android:singleLine="true" />
<Button
android:id="@+id/playbutton"
android:layout_width="100dip"
android:layout_height="60dip"
android:layout_below="@+id/timelength"
android:layout_marginTop="120dp"
android:onClick="onClick"
android:text="@string/playbutton"
android:textSize="12pt" />
<Button
android:id="@+id/stopbutton"
android:layout_width="100dip"
android:layout_height="60dip"
android:layout_alignBaseline="@+id/playbutton"
android:layout_alignBottom="@+id/playbutton"
android:layout_alignParentRight="true"
android:onClick="onClick"
android:text="@string/stopbutton"
android:textSize="12pt" />
<Chronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/stopbutton"
android:layout_centerHorizontal="true"
android:layout_marginBottom="28dp"
android:format="%s"
android:textColor="#00FF00"
android:textSize="80px" />
</RelativeLayout>
AndroidManifest.xml
注意添加相关权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cfzz.amt"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<span style="background-color: rgb(102, 255, 255);"><uses-permission android:name="android.permission.RECORD_AUDIO" /></span>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity android:name="org.achartengine.GraphicalActivity" />
<activity
android:name="com.cfzz.amt.AndroidMikeTestActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
运行效果图:
到此,麦克风测试程序就可以运行了,在编辑框内输入小时数,再点击开始即可运行麦克风,这时可以从听筒中听到麦克风里的声音,当时间到达设定时间时会自动停止,同时计时器也会暂停。当按停止按钮时,时间设置和计时器会同时置为00:00,以便新的测试。