内容
继续前文 MediaPlayer和SeekBar配合起来。这次讲听筒模式下播放音乐的步骤。
参考资料
修改点
先给出和上一篇文章的差异点:
下面是文本模式的代码,方便拷贝:——这里省掉了对AudioManager的声明,可见后面的完整代码。
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(false);
audioManager.setMode(AudioManager.MODE_IN_CALL);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
maxVolume, AudioManager.FLAG_PLAY_SOUND);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
完整代码
package com.example.mediaplayerexample;
import java.io.IOException;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
private MediaPlayer mediaPlayer = null;
private Button start = null;
private Button stop = null;
private SeekBar mediaSeekBar = null;
private AudioManager audioManager = null;
boolean isPlaying = false;
private final int MONITOR_MSG_ID = 0;
private Thread monitor = null;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (!isPlaying || msg.what != MONITOR_MSG_ID) {
super.handleMessage(msg);
return;
}
mediaSeekBar.setProgress(mediaPlayer.getCurrentPosition());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
stop.setEnabled(false);
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
mediaPlayer.prepare();
} catch (IllegalStateException e) {
Log.d(TAG, "start click error", e);
} catch (IOException e) {
Log.d(TAG, "start click error", e);
}
isPlaying = true;
mediaPlayer.start();
mediaSeekBar.setMax(mediaPlayer.getDuration());
start.setEnabled(false);
stop.setEnabled(true);
monitor = new MonitorThread(1000);
monitor.start();
}
});
stop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
isPlaying = false;
mediaPlayer.stop();
start.setEnabled(true);
stop.setEnabled(false);
try {
monitor.join();
} catch (InterruptedException e) {
Log.d(TAG, "stop click error", e);
}
}
});
Log.d(TAG, "new MediaPlyer()");
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(false);
audioManager.setMode(AudioManager.MODE_IN_CALL);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
maxVolume, AudioManager.FLAG_PLAY_SOUND);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
isPlaying = false;
start.setEnabled(true);
stop.setEnabled(false);
try {
monitor.join();
} catch (InterruptedException e) {
Log.e(TAG, "start thread failed.", e);
}
}
});
Log.d(TAG, "Set data source");
AssetFileDescriptor fd = null;
try {
fd = getResources().openRawResourceFd(R.raw.test);
mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
fd.close();
Log.d(TAG, "set data source ok");
} catch (IOException e) {
Log.e(TAG, "set data source failed.", e);
finish();
}
mediaSeekBar = (SeekBar) findViewById(R.id.mediaSeekBar);
mediaSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (isPlaying) {
mediaPlayer.seekTo(seekBar.getProgress());
}
}
});
Log.d(TAG, "create ok");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class MonitorThread extends Thread{
private int interval;
public MonitorThread(int interval) {
this.interval = interval;
}
public void run(){
Log.d(TAG, "MonitorThread::run()");
while(true) {
if (!isPlaying) return;
try {
sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(MONITOR_MSG_ID);
}
}
}
}
补充说明
- 权限问题
在参考文章中说需要在AndroidManifest.xml中增加权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
但本文的例子没有加这个,也能够运行。再查阅AudioManaer.class,查找MODIFY_AUDIO_SETTINGS。发现有两个方法的注释中提到要加权限:
/**
* Start bluetooth SCO audio connection.
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* ...
*/
public void startBluetoothSco(){
...
}
/**
* Stop bluetooth SCO audio connection.
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* ...
* @see #startBluetoothSco()
*/
public void stopBluetoothSco(){
...
}
- am和mp的初始化顺序。
设置AudioManager的属性可以在MediaPlayer的前面,也可以在后面。但都需要在setDataSource()调用之前。
简单地,可以约定先设置AudioManager的属性;再初始化MediaPlayer对象,并设置其属性。
- mp中方法调用顺序。
MediaPlayer的setAudioStreamType()调用需要在setDataSource()的前面。否则,voice-in-call会不发声。
对于MediaPlayer先设置属性,最后设置data source,以及prepare+start+pause+stop等。
源码中的说明:
/**
* Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
* for a list of stream types. Must call this method before prepare() or
* prepareAsync() in order for the target stream type to become effective
* thereafter.
*
* @param streamtype the audio stream type
* @see android.media.AudioManager
*/
public native void setAudioStreamType(int streamtype);
下面两句可以调换位置
setVolumeControlStream(streamType);
mAudioManager.setSpeakerphoneOn(mIsSpeaker);setVolumeControlStream(): 参考《Professional Android 4 Application Development》P632~P633 “Response to the Volume Control”