本文是介绍关于Android多媒体的使用的文章,总结于官方文档。拍照会另外写一篇博客。
一、MediaPlayer
MediaPlayer是安卓multimedia framework提供的用于播放多媒体的类。可以用于播放音频,视频等多媒体文件。支持应用raw资源内的文件、本地文件系统中的文件以及网络上获取的数据流。下面讲讲使用。
1.mediaplayer
MediaPlayer类是media framework中最重要的组件。最少的设置,实现获取、解码和播放视频与音频。
1)播放raw文件夹内的多媒体文件:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // 不需要调用prepare();上面的create()方法,已经帮我们完成了调用
系统不会以任何方式解析raw中的文件。但不应该是原始音频,而应该是有正确的编码和格式化的媒体文件,且该文件要是Android支持格式的一种。
2)播放本地文件,通过URI:
Uri myUri = ....;
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
3)播放网络数据流:
String url = "http://........";
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // 可能会花很长时间
mediaPlayer.start();
以url方式流式传输在线媒体文件,要保证文件可以逐行下载。
注意:在setDataSource()时要捕获IOException和IllegalArgumentException,因为文件可能不存在。
4)注意事项:
a.异步prepare()。由于要进行读取和解码的操作,可能会花很长时间,这样可能会造成ANR异常。可以选择自己启动新线程来执行异步操作,也可以使用Mediaplayer的prepareAsync()方法,将在后台进行prepare,完成后回调MediaPlayer.OnPreparedListener接口,通过setOnPreparedListener()设置;
b.管理状态。这里的状态是指Mediaplayer内置的状态。新建Mediaplayer对象之后,处于空闲状态(idle),setDataResource()处于初始化状态,prepare之后则处于准备状态(Prepared),则可以调用start()方法,后面根据运行状态可分为:started、paused、PlaybackCompleted。stop()之后,不可以直接调用start()要重新prepare;
c.释放Mediaplayer。Mediaplayer会消耗系统资源,因此要额外的调用release()方法来释放占用的资源。
mediaPlayer.release();
mediaPlayer = null;
一般,当activity重启时如果没有释放,则会导致Mediaplayer重复创建。当然,如果使用service来控制Mediaplayer,则不必如此。
2.在Service中使用MediaPlayer
当需要不在应用界面也能播放音乐时,则可以使用Service来控制MediaPlayer。官网上有一种在MediaBrowserServiceCompat嵌入MediaPlayer的实现方式,用于与MediaBrowserCompat交互,但是实现上有点复杂,暂不讨论。这里讲述的是在普通Service中的实现方式。
1)异步运行
首先,Service与Activity一样都是运行在主线程的。因此不可以在Service中执行繁重的操作。这里直接使用MediaPlayer的prepareAsync()操作。
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mMediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mMediaPlayer = ... // 上述1中的初始化操作
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** 当prepare好之后回调 */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
2)处理异步错误
通过实现MediaPlayer.OnErrorListener,处理异步错误。
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mMediaPlayer;
public void initMediaPlayer() {
// ...
mMediaPlayer.setOnErrorListener(this);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... 适当的处理 ...
// MediaPlayer已经变为Error状态,需要重启。
}
}
注意,错误发生后,MediaPlayer处于Error状态,重启之后才能再次使用。
3)使用wake locks
当在Service中播放media时,设备有可能会进入睡眠状态。因为系统要节省电源,尤其是6.0之后的低电耗模式,以及7.0的Doze机制下,CPU会进入休眠状态,service也会被停止。如果正在播放音乐就需要避免Service被杀死。因此需要使用“唤醒锁(wake locks)”。用于向系统发送信号,告诉系统,应用正在使用一些功能,即使手机处于空闲状态,也要可以使用。
MediaPlayer中可以在初始化时调用setWakeMode()方法,之后MediaPlayer会持有这个特殊的locks直到释放locks为止。
mMediaPlayer = new MediaPlayer();
// ...
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
当然,除了CPU锁(上述)以外,还有WifiLock:
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
且需要手动调用wifiLock.release()来释放锁。
注:需要请求权限<uses-permission android:name="android.permission.WAKE_LOKE" />。
4)执行释放操作
像上述提到的一样,虽然在Service中使用MediaPlayer,但是仍需要释放,只是没有activity中受到系统约束那样频繁。而且,不应该依赖于系统的gc机制,因为gc不知道啥时候会启动,而且,gc更倾向于回收内存资源,而其他的媒体相关资源则不会得到释放。
public class MyService extends Service {
MediaPlayer mMediaPlayer;
// ...
@Override
public void onDestroy() {
if (mMediaPlayer != null) mMediaPlayer.release();
}
}
3.从ContentResolver中获取media
上代码:
获取media资源:
ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
// 查询失败
} else if (!cursor.moveToFirst()) {
// 没有媒体资源
} else {
int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
do {
long thisId = cursor.getLong(idColumn);
String thisTitle = cursor.getString(titleColumn);
// ...处理...
} while (cursor.moveToNext());
}
设置mediaPlayer与播放:
long id = ...;
Uri contentUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(getApplicationContext(), contentUri);
// ...prepare and start...
二、MediaRecorder
media录制,分为录音和录制视频两种。下面讲录音,录视频会放在拍照那篇一起。
1.请求权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
该权限为危险权限,6.0后需要在运行时做权限请求。
2.使用MediaRecorder
步骤:
1).setAudioSource()设置audio source,(MIC为录音);
2).setOutputFormat()设置输出文件格式;
3).setOutputFile()设置输出文件名称;
4).setAudioEncoder()设置音频编码器;
5).prepare()完成初始化。
启动和停止录制,使用start()和stop();
最后完成录制后要调用release()释放资源。
3.代码示例:
...
public class AudioRecordTest extends AppCompatActivity {
private static final String LOG_TAG = "AudioRecordTest";
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
private static String mFileName = null;
private RecordButton mRecordButton = null;
private MediaRecorder mRecorder = null;
private PlayButton mPlayButton = null;
private MediaPlayer mPlayer = null;
// Requesting permission to RECORD_AUDIO
private boolean permissionToRecordAccepted = false;
private String [] permissions = {Manifest.permission.RECORD_AUDIO};
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_RECORD_AUDIO_PERMISSION:
permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionToRecordAccepted ) finish();
}
private void onRecord(boolean start) {
if (start) {
startRecording();
} else {
stopRecording();
}
}
private void onPlay(boolean start) {
if (start) {
startPlaying();
} else {
stopPlaying();
}
}
private void startPlaying() {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
}
}
private void stopPlaying() {
mPlayer.release();
mPlayer = null;
}
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
}
mRecorder.start();
}
private void stopRecording() {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
class RecordButton extends Button {
boolean mStartRecording = true;
OnClickListener clicker = new OnClickListener() {
public void onClick(View v) {
onRecord(mStartRecording);
if (mStartRecording) {
setText("Stop recording");
} else {
setText("Start recording");
}
mStartRecording = !mStartRecording;
}
};
public RecordButton(Context ctx) {
super(ctx);
setText("Start recording");
setOnClickListener(clicker);
}
}
class PlayButton extends Button {
boolean mStartPlaying = true;
OnClickListener clicker = new OnClickListener() {
public void onClick(View v) {
onPlay(mStartPlaying);
if (mStartPlaying) {
setText("Stop playing");
} else {
setText("Start playing");
}
mStartPlaying = !mStartPlaying;
}
};
public PlayButton(Context ctx) {
super(ctx);
setText("Start playing");
setOnClickListener(clicker);
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Record to the external cache directory for visibility
mFileName = getExternalCacheDir().getAbsolutePath();
mFileName += "/audiorecordtest.3gp";
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
LinearLayout ll = new LinearLayout(this);
mRecordButton = new RecordButton(this);
ll.addView(mRecordButton,
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0));
mPlayButton = new PlayButton(this);
ll.addView(mPlayButton,
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0));
setContentView(ll);
}
@Override
public void onStop() {
super.onStop();
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
}
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
}