Android实现音乐播放器,Service后台播放&Notification控制播放状态

本音乐播放器的功能:
1.实现访问手机本地的.mp3音乐文件;
2.在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以);
3.在播放音乐页面实现上一首、下一首、播放/暂停、进度条随音乐播放滑动、动态显示播放时间,拖动进度条,当前播放时间动态改变并且播放进度也会改变、自动切换下一首歌;
4.从在Activity中播放音乐过渡到在Service中播放音乐;
5.改进该项目,使用广播的方式播放音乐并能够在Notification中控制音乐的播放状态。

由于我将全部代码都贴在了本博客,所以文章篇幅较长。

我的最终效果图:
在这里插入图片描述
本项目效果图:

在这里插入图片描述

----------------------------------------以下就开始分别实现----------------------------------------
第一步:实现访问手机本地的.mp3音乐文件
  1. 编写一个音乐类Music(以 周杰伦 - 最长的电影.mp3 为例):编写四个成员变量,并在其中提供对应的set()和get()方法

//音乐的名字(周杰伦 - 最长的电影.mp3),截取后缀获得.mp3的文件,subString("-")截取歌曲的名字
private String name;
//音乐文件的作者
private String artist;
//音乐文件的路径
private String url;
//音乐播放的时间
private int time;

  1. 编写一个专门访问本地音乐文件的类MusicList,这里用到读取内存的权限,因此别忘了要在AndroidManifest.xml中添加权限
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

实现步骤:
①编写静态方法getMusicData(),返回值就是查询到的音乐数据集合;
②在该方法中创建ContentRecolver实例,通过上下文的getContentResolver()方法;
③判断获取到的ContentResolver是否为空,如果不为空,调用contentResolver.query (Uri uri, String[] projection,String selection,String[] selectionArgs, String sortOrder)方法查询本地的音乐文件,返回一个Cursor对象。如果Cursor为空,说明没有数据,直接返回null;
④如果有数据,利用Cursor对象的moveToFirst()查询第一条数据,如果不为空,就循环调用moveToNext()方法查询下一条数据直到没有数据为止;
⑤在查询中,通过cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.××))获取到相应的数据,该数据就对应等于Music中的成员变量;’
⑥将查询到的数据赋值到Music对象,完成查询数据的存储,并把该Music对象添加到Music集合,这样就获取到了本地音乐数据集合。
对上面的补充说明(对ContentResolver和ContentProvider了解的可直接跳过)
ContentProvider在android中的作用是对外共享数据,也就是说你可以通过 ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。
ContentResolver:提供访问ContentProvider的能力,可以使用ContentResolver读取和操作其他应用程序已经通过ContentProvider暴露出来的数据。

/**
 * 从内部存储中读取下载好的后缀名为.mp3的音乐文件,返回值为Music集合
 */
public class MusicList {

    public static ArrayList<Music> getMusicData(Context context){
        ArrayList<Music> musicList = new ArrayList<Music>();
        ContentResolver contentResolver = context.getContentResolver();
        if(contentResolver != null){
            Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
            if(cursor == null){
                return null;
            }
            if(cursor.moveToFirst()) {
                do {
                    Music music = new Music();
                    String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
                    String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
                    if ("<unknown>".equals(artist)) {
                        String[] split = name.split("-");
                        artist =  split[0];
                    }
                    int time = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
                    String url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
                    String isMp3 = name.substring(name.length() - 3, name.length());
                    if (isMp3.equals("mp3")) {
                        music.setArtist(artist);
                        music.setTime(time);
                        music.setUrl(url);
                        music.setName(name.substring(0,name.length()-4));
                        musicList.add(music);
                    }
                } while (cursor.moveToNext());
            }
        }
        return musicList;
    }
}

第二步:在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以)

实现步骤:

1.请求运行时权限(Android 6.0及以上系统在使用危险权限时都必须进行运行时权限的处理,这里如果不进行处理,会出现闪退的现象)
	//自定义方法requestPermission()
    private void requestPermission() {
    	//创建允许权限的集合
        List<String> permissionList = new ArrayList<String>();
        //如果该权限没同意,就加入到允许权限的集合
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        //如果允许权限不为空,说明有权限还没申请,就一起申请
        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1);
        }
		//权限申请完成之后,才去初始化视图
		 else {
			 //适配器的适配工作
            initView();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        int grantResult = grantResults[i];
                        if (grantResult == PackageManager.PERMISSION_DENIED) {
                            String s = permissions[i];
                            Toast.makeText(this, s + "权限被拒绝了", Toast.LENGTH_SHORT).show();
                        } else {
                            initView();
                        }
                    }
                }
        }
    }
2.设置适配器显示数据

①编写主页的xml(activity_main.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/listView_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

②在主页中获取到该控件,并给该控件设置适配器进行显示

MusicAdapter
public class MusicAdapter extends BaseAdapter {
    private Context mContext;
    private List<Music> mMusicList;

    public MusicAdapter(Context context,List<Music> musicList){
        mContext = context;
        mMusicList = musicList;
    }

    @Override
    public int getCount() {
        return mMusicList.size();
    }

    @Override
    public Object getItem(int position) {
        return mMusicList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        Music music = mMusicList.get(position);
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.music_item, parent,false);
            viewHolder = new ViewHolder();
            viewHolder.music_item_name = convertView.findViewById(R.id.music_item_name);
            viewHolder.music_item_artist = convertView.findViewById(R.id.music_item_artist);
            viewHolder.music_item_time = convertView.findViewById(R.id.music_item_time);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
            viewHolder.music_item_name.setText(music.getName());
            viewHolder.music_item_artist.setText(music.getArtist());
            viewHolder.music_item_time.setText(formatTime(music.getTime()));
        return convertView;
    }

    class ViewHolder {
        TextView music_item_name;
        TextView music_item_artist;
        TextView music_item_time;
    }
	//将时间转换为××:××格式
    private String formatTime(int time) {
        int ms2s = (time / 1000);
        int minute = ms2s / 60;
        int second = ms2s % 60;
        return String.format("%02d:%02d", minute, second);
    }
}

music.item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">
        <TextView
            android:id="@+id/music_item_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp">

            <TextView
                android:id="@+id/music_item_artist"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/music_item_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@id/music_item_artist" />
        </RelativeLayout>
</LinearLayout>

MainActivity
public class MainActivity extends AppCompatActivity {
    private ArrayList<Music> arrayList;
    private MusicAdapter mMusicAdapter;
    private ListView mListView;

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

    private void initView() {
        arrayList = MusicList.getMusicData(this);
        mListView = findViewById(R.id.listView_main);
        mMusicAdapter = new MusicAdapter(this, arrayList);
        mListView.setAdapter(mMusicAdapter);
        //实现页面跳转,将当前点击的音乐条目传过去给音乐播放页面,这样它才知道播放哪条音乐
        mListView.setOnItemClickListener(new AdapterView.OnItemClick
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值