使用MediaPlayer播放音频文件

时间:2023-02-01 19:43:01

MediaPlayer是一个支持音频及视频文件播放的Android类,可播放不同来源(本地或网络流媒体)、多种格式(如WAV、MP3、Ogg Vorbis、MPEG-4以及3GPP)的多媒体文件。

新建音视频播放、暂停和停止封装类

package com.huangfei.hellomoon;

import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;

/**
*
* @author huangfeihong 音视频播放、暂停和停止封装类
*/

public class AudioPlayer {

private MediaPlayer mPlayer;

/**
* 暂停
*/

public void pause() {
if (mPlayer != null) {
mPlayer.pause();
}
}

/**
* 停止
*/

public void stop() {
if (mPlayer != null) {
/**
* MediaPlayer.release()方法可销毁MediaPlayer的实例。销毁是“停止”的一种具有攻击意味的说法,
* 但我们有充足的理由使用销毁一词。
* 除非调用MediaPlayer.release()方法,否则MediaPlayer将一直占用着音频解码硬件及其它系统资源
* 。而这些资源是由所有应用共享的。
* MediaPlayer有一个stop()方法。该方法可使MediaPlayer实例进入停止状态,等需要时再重新启动
* 。不过,对于简单的音频播放应用,建议 使用release()方法销毁实例,并在需要时进行重见。基于以上原因,有一个简单可循的规则:
* 只保留一个MediaPlayer实例,保留时长即音频文件 播放的时长。
*/

mPlayer.release();
mPlayer = null;
}
}

/**
* 播放
*/

public void paly(Context c) {
/**
* 开头就调用stop()方法,可避免用户多次单机Play按钮创建多个MediaPlayer实例的情况发生。
*/

stop();

/**
* 音频文件放在res/raw目录下。目录raw负责存放那些不需要Android编译系统特别处理的各类文件。
*/

mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.setOnCompletionListener(new OnCompletionListener() {

@Override
public void onCompletion(MediaPlayer mp) {
stop();
}
});

mPlayer.start();
}
}

创建播放音频的Fragment

package com.huangfei.hellomoon;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class HelloMoonFragment extends Fragment {
private AudioPlayer mPlayer = new AudioPlayer();
private Button mPlayButton;
private Button mStopButton;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 当旋转设备后,音频播放会停止,该如何保证音频一直播放?
* 调用Fragment.setRetainInstance(true)方法可保留fragment。
*
* 当设备旋转时,FragmentManager会检查每个fragment的retainInstance属性值。如果属性值为false(初始默认值),
* FragmentManager会立即销毁该fragment实例。随后,为适应新的设备配置,新的activity的新FragmentManager会创建
* 一个新的fragment及其视图。如属性值为true,则该fragment的视图立即被销毁,但fragment本身不会被销毁。为适应新的设备配置,
* 当新的activity创建后,新的FragmentManager会找到被保留的fragment,并重新创建它的视图。
*
* 只有当activity因设备配置发生改变被销毁时,fragment才会短暂的处于被保留状态。如果activity是因操作系统需要回收内存而被销毁,
* 则所有被保留的fragment也会被随之销毁。
*
* 如果activity或fragment中有需要长久保存的东西,则应覆盖onSaveInstanceState(Bundle)方法,将其保存下来。这样,由于同activity记录的生命周期保持了同步,后续可在需要时对其进行回复。
*/

setRetainInstance(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_hello_moon, container,
false);
mPlayButton = (Button) view.findViewById(R.id.hellomoon_palyButton);
mPlayButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
mPlayer.paly(getActivity());
}
});
mStopButton = (Button) view.findViewById(R.id.hellomoon_stopButton);
mStopButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
mPlayer.stop();
}
});

return view;
}

@Override
public void onDestroy() {
super.onDestroy();
/**
* 调用AudioPlayer.stop()方法,以避免MediaPlayer的不停播放。该Fragment被销毁后,MediaPlayer仍可不停地播放,
* 这是因为MediaPlayer运行在一个不同的线程上。
*/

mPlayer.stop();
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- TableLayout使用起来和LinearLayout差不多。联合使用TableLayout和TableRow,可更容易地布置形成排列整齐的视图。 -->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >


<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:contentDescription="@string/hellomoon_image_description"
android:scaleType="centerInside"
android:src="@drawable/armstrong_on_moon" />


<!--
TableRow组件无需声明高度和宽度的属性定义。实际上,它使用的是TableLayout的高度和宽度属性定义及其所有其他属性定义。
TableRow子组件的行为方式类似于表里的单元格。
-->


<TableRow
android:layout_weight="0"
android:gravity="center|bottom" >


<Button
android:id="@+id/hellomoon_palyButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hellomoon_play" />


<Button
android:id="@+id/hellomoon_stopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hellomoon_stop" />

</TableRow>

</TableLayout>

创建托管Fragment的Activity

package com.huangfei.hellomoon;

import android.os.Bundle;
import android.app.Activity;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;

public class HelloMoonActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_moon);
}

}
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/helloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.huangfei.hellomoon.HelloMoonFragment" />


<!-- 使用布局fragment,即在fragment元素节点中指定fragment的类。在Activity调用setContentView(...)方法,并实例化布局时,发现了fragment元素。
于是FragmentManager接着就创建了指定Fragment的一个实例,并将其添加到fragment对列中。
使用如此简单的方式托管fragment,同时也失去了只有显示地使用FragmentManager才能获得的灵活性与掌控能力。
1、可覆盖fragment的生命周期方法,以响应各种事件。但无法控制调用这些方法的时机。
2、无法提交移除、替换、分离布局fragment的事务。activity被创建后,即无法做出任何改变。
3、无法附加argument给布局fragment。附加argument必须在fragment创建后并被添加给FragmentManager之前完成。如果使用布局fragment,这些事件何时发生,我们无从得知。-->

代码地址