Android小项目之音乐播放器简易版

V1.0-最简单的一种,只有MediaPlay


activity.xml

 

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-加入服务和播放列表

总结:

  1. 因为在manifest.xml文件中忘记对服务进行注册导致出错
  2. 使用MediaPlayer特别需要了解下面的流程,否则容易出现state 0,1,8等错误
  3. 不要忘记权限,特别是如果手机是6.0我们还要动态权限
  4. 使用mediaPlayer类必须要对下图的使用流程有较深的理解
  5. 为什么会有prepareAsync呢?因为有些要播放的文件可能很大,所以需要进行异步准备,以免ANR(application not responding)
  6. 我们可以为mediaPlayer注册一个监听器setOnPreparedListener,在其中重写onPrepare方法,当监听到已经准备好了后就调用mediaPlayer.start()方法
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
        isPause = false;
    }
});


代码的目录结构
Android小项目之音乐播放器简易版_第1张图片

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 musicBeanList;

    @Override
    public void onCreate() {
        super.onCreate();
        musicBeanList = new ArrayList();
    }
}

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

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

你可能感兴趣的:(Android项目)