V1.0-最简单的一种,只有MediaPlay
activity.xml
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<Button
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
manifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
MainActivity.java
package com.xks.musicplayer;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import java.io.IOException;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MediaPlayer mediaPlayer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mediaPlayer = new MediaPlayer();
//这里通过getExternalStoragePublicDirectory方法获取到了sd卡中的公共路径中的Music目录,同理我们还可以获取其他的公共目录
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath() + "/许巍 - 那一年.mp3";
try {
//这里设置了mediaPlayer要播放的资源所在路径
mediaPlayer.setDataSource(path);
//如果不准备就进行play会报state 0错误,如果多次准备会报state 1错误 这里mediaPlayer的使用流程请参照下图 不需要prepare操作的情况是当pause后重新start
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_play:
mediaPlayer.start();
break;
case R.id.btn_pause:
mediaPlayer.pause();
break;
}
}
}
V2.0-加入服务和播放列表
总结:
- 因为在manifest.xml文件中忘记对服务进行注册导致出错
- 使用MediaPlayer特别需要了解下面的流程,否则容易出现state 0,1,8等错误
- 不要忘记权限,特别是如果手机是6.0我们还要动态权限
- 使用mediaPlayer类必须要对下图的使用流程有较深的理解
- 为什么会有prepareAsync呢?因为有些要播放的文件可能很大,所以需要进行异步准备,以免ANR(application not responding)
- 我们可以为mediaPlayer注册一个监听器setOnPreparedListener,在其中重写onPrepare方法,当监听到已经准备好了后就调用mediaPlayer.start()方法
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
isPause = false;
}
});
代码的目录结构
MyMusicAdapter>这是一个很简单的播放列表的适配器
public class MyMusicAdapter extends BaseAdapter {
@Override
public int getCount() {
return MyApp.musicBeanList == null ? 0 : MyApp.musicBeanList.size();
}
@Override
public MusicBean getItem(int position) {
return MyApp.musicBeanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
MusicBean musicBean = MyApp.musicBeanList.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_music, null);
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView) convertView.findViewById(R.id.tv_music);
viewHolder.mTextView.setText(musicBean.getMusicName());
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.mTextView.setText(musicBean.getMusicName());
}
return convertView;
}
class ViewHolder {
private TextView mTextView;
}
}
MusicBean>歌曲实体类,存放了歌曲名称和路径以及getter/setter/toString方法
public class MusicBean {
private String musicName;
private String musicPath;
public MusicBean() {
}
public MusicBean(String musicName, String musicPath) {
this.musicName = musicName;
this.musicPath = musicPath;
}
public String getMusicName() {
return musicName;
}
public void setMusicName(String musicName) {
this.musicName = musicName;
}
public String getMusicPath() {
return musicPath;
}
public void setMusicPath(String musicPath) {
this.musicPath = musicPath;
}
@Override
public String toString() {
return "MusicBean{" +
"musicName='" + musicName + '\'' +
", musicPath='" + musicPath + '\'' +
'}';
}
}
MyApp extends Application 特别注意需要在manifest.xml文件中声明:
android:name=”MyApp”
里面存放的是歌曲集合,由于本类继承自application,因此歌曲集合是一个全局变量,只需要在服务中对目标文件夹进行遍历将以.mp3结尾的歌曲加入集合即可
public class MyApp extends Application {
//播放列表音乐集合,MusicBean实体类中着存放歌曲名称和歌曲所在目录
public static List<MusicBean> musicBeanList;
@Override
public void onCreate() {
super.onCreate();
musicBeanList = new ArrayList<MusicBean>();
}
}
MusicService>音乐服务类,使用绑定服务的方式打开后再使用startService方法,使得播放器可以在用户打开activity的时候与服务进行交互,在用户关闭了activity的时候在onDestroy方法中解除绑定,使服务可以后台运行播放音乐
public class MusicService extends Service {
MediaPlayer mediaPlayer;//播放歌曲的类
private int currentPlaying = 0;//正在播放的歌曲
private static final String TAG = "MusicService";
public static boolean isPlaying = false;//是否处于播放状态
public static boolean isPause = false;//是否处于暂停状态
/**
* 服务Create,此时应当生成播放列表并初始化MediaPlay类
*/
@Override
public void onCreate() {
super.onCreate();
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "方法名>>onBind()");
final MyBinder myBinder = new MyBinder();
if (MyApp.musicBeanList.size() == 0) {
myBinder.createPlayList();
Log.e(TAG, "方法名>>onBind()" + MyApp.musicBeanList.toString());
}
return myBinder;
}
/**
* 服务与activity进行交互的方法
*/
public class MyBinder extends Binder {
/**
* 播放指定位置的歌曲
*
* @param music
*/
public void playMusic(MusicBean music, int position) {
try {
/*
* Resets the MediaPlayer to its uninitialized state. After calling this method,
* you will have to initialize it again by setting the data source and calling prepare().
*/
if (isPlaying&&isPause) {//播放状态但是被暂停了
if (currentPlaying != position) {//正在播放的与点击的歌不是同一首
setMusic(music);
} else if (currentPlaying == position) {//是同一首歌
mediaPlayer.start();//=>无需prepare,直接start就行
isPause = false;
}
}else{//没有播放
setMusic(music);
}
currentPlaying = position;
} catch (IOException e) {
e.printStackTrace();
}
}
private void setMusic(MusicBean music) throws IOException {
mediaPlayer.reset();//reset后需要重新确定要播放的音乐所在路径
mediaPlayer.setDataSource(music.getMusicPath());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
isPause = false;
isPlaying = true;
}
});
}
/**
* 暂停播放
*/
public void pauseMusic() {
if (mediaPlayer.isPlaying()) {//正在播放时
mediaPlayer.pause();
isPlaying = true;
isPause = true;
}
}
/**
* 子线程中扫描歌曲>生成播放列表
* 遍历SD卡下的Music文件夹(mnt>shell>emulated>0>MUSIC),获取以.mp3结尾的文件并装入集合
*/
public void createPlayList() {
new Thread() {
@Override
public void run() {
MyApp.musicBeanList.clear();
File musicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
File[] files = musicDirectory.listFiles();
for (File file : files) {
if (file.getName().endsWith(".mp3")) {
MusicBean musicBean = new MusicBean();
musicBean.setMusicName(file.getName());
musicBean.setMusicPath(file.getAbsolutePath());
MyApp.musicBeanList.add(musicBean);
}
}
}
}.start();
}
}
@Override
public void onDestroy() {
//在服务中的onDestroy方法中对mediaPlayer资源进行释放
mediaPlayer.release();
//将mediaPlayer赋空便于垃圾回收器进行回收
mediaPlayer = null;
super.onDestroy();
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener ,AdapterView.OnItemClickListener {
private static final String TAG = "MainActivity";
private Button playButton;
private Button pauseButton;
private ListView mListView;
private MyMusicAdapter myMusicAdapter;
private MusicService.MyBinder myBinder;//Binder对象
private MyServiceConnected myServiceConnected;
private int currentPlaying = 0;//正在播放的是第几首歌
private Button mRefreshPlayList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playButton = (Button) findViewById(R.id.btn_play);
pauseButton = (Button) findViewById(R.id.btn_pause);
mRefreshPlayList = (Button) findViewById(R.id.btn_flush_playlist);
mListView = (ListView) findViewById(R.id.lv_listview);
//通过绑定的方式开启服务,该方式可以使activity与服务进行通信
Intent intent = new Intent(this,MusicService.class);
myServiceConnected = new MyServiceConnected();
bindService(intent, myServiceConnected,BIND_AUTO_CREATE);
startService(intent);
playButton.setOnClickListener(this);
pauseButton.setOnClickListener(this);
mRefreshPlayList.setOnClickListener(this);
mListView.setOnItemClickListener(this);
}
/**
* 退出activity时要解除与服务的绑定
*/
@Override
protected void onDestroy() {
unbindService(myServiceConnected);
super.onDestroy();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_play://播放音乐
Log.d(TAG, "myBinder ==null? " + (myBinder == null));
myBinder.playMusic(MyApp.musicBeanList.get(currentPlaying),currentPlaying);
break;
case R.id.btn_pause://暂停音乐
myBinder.pauseMusic();
break;
case R.id.btn_flush_playlist:
myBinder.createPlayList();
myMusicAdapter.notifyDataSetChanged();
break;
}
}
private class MyServiceConnected implements ServiceConnection{
/**
* 绑定服务成功回调
* @param componentName
* @param service
*/
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
Log.e(TAG, "类名>>MainActivity>>方法名>>onServiceConnected()");
myBinder = (MusicService.MyBinder) service;
myMusicAdapter = new MyMusicAdapter();
mListView.setAdapter(myMusicAdapter);
}
/**
* 服务意外挂掉回调
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
MusicBean musicBean = myMusicAdapter.getItem(position);
currentPlaying = position;
Log.e(TAG, "类名>>MainActivity>>方法名>>onItemClick()"+musicBean.getMusicName());
myBinder.playMusic(musicBean,position);
}
}
activity_main.xml
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<Button
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/btn_play"
android:text="暂停" />
<Button
android:id="@+id/btn_flush_playlist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="刷新播放列表"
android:layout_toRightOf="@+id/btn_pause"/>
<ListView
android:id="@+id/lv_listview"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/btn_play" />
item_music
<TextView
android:id="@+id/tv_music"
android:textSize="25sp"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp" />
GitHub 源代码下载:https://github.com/a1265137718/AndroidProjectRepository.git