app下载链接,欢迎给我的所谓的app找bug,谢谢指教!听朋友反映,问题还不少,有的手机根本无法打开app,有的出现歌曲列表就闪退,正在努力debug,见谅,浪费各位流量了! http://pan.baidu.com/s/1jHNa5Bg

前言

MusicBox的制作过程涉及到不少知识,如四大组件、MediaStore等等。制作完成后收获颇丰,现在带大家制作自己的音乐播放器。 #开发思路 程序由一个Activity(交互)和Service(后台播放音乐)构成。当发生单击事件时,Activity发送广播通知Service改变播放状态;Service将当前播放状态和当前播放的是哪一首歌曲广播给Activity供其更新UI界面。而歌曲信息(歌名、艺术家、路径等)则由系统的多媒体ContentProvider提供。

musicBox开发思路图

开发细节

1.布局界面有两个TextView及5个ImageButton(分别代表上一首、播放/暂停、下一首、停止、显示歌曲列表).见文末效果展示图.5个Button均注册监听器,当单击事件发生时向Service发送广播。为了让Service知道哪个按钮被按下了,可以这样操作:

    intent.putExra("contral",xxx);  //xxx是Button的flag,比如
    //“上一首”这个Button的flag设置为0x123
    sendBrocast(intent);

2.Activity创建三个String数组,分别存储歌曲的title,artist,display_name,而Service中则创建一个String数组,用于存储歌曲的path。可以做到4个数组的同一下标对应同一首歌曲。这样做的好处是Activity和Service之间只需要交换数组的下标就可以实现TextView的更新以及歌曲的切换,避免了直接传输字符串。

3.Service设置一个status标志播放状态,并广播给Activity。Activity依据此改变表示播放/暂停的那个ImageButton显示的图片。

4.当用户点击用于显示歌曲列表的Button时,可以使用AlertDialog加载一个单选列表对话框,这相比跳转到另一Activity的思路更加简单易行。要使用户点击列表的某一首歌时能切歌并使列表消失,只需覆写 DialogInterface.OnClickListener的onClick方法即可,如下:

DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent=new Intent(actionByActivity);//actionByActivity是自定义的action
intent.putExtra("contral",which);//which从0开始,表示第which+1首歌被点击
sendBroadcast(intent);
dialog.dismiss();//使dialog消失
}
}

5.Service使用MediaPlayer播放音乐,为了使在无用户交互时能顺序播放,为MediaPlayer对象注册播放完毕监听器,如下:

     //player为MediaPlayer对象
     player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                player.reset();
                try {
                    current=(current+1)%(num-1);//current是正在播放的歌曲的数组下标
                    player.setDataSource(path[current]);//path[]为路径数组
                    player.prepare();
                    player.start();
                }catch (IOException e){
                    e.printStackTrace();
                }
                Intent intent=new Intent(MainActivity.actionByService);
                intent.putExtra("current",current);
                sendBroadcast(intent);//把current广播给Activity
            }
        });

重点知识讲解

MediaStore获取歌曲信息

类MediaStore可以看作是安卓Media数据库的配置说明(说明该数据库表名、列名、访问的uri等等),MediaStore这个类里面包含了ImageAudioVideo这几个内部类。其中类Audio中有以下表名:MediaGenresPlaylistsArtistsAlbums,每一个表都有其供外部访问的uri和许多String类型的列名。顾名思义,要找专辑信息应当在Albums表找,其他类似。为了有个直观印象,看一下Media表的源码:

Media表源码1

Media表源码2

AudioColums

AudioColums 我们看到AudioColums继承了MediaColums,后者有title、display_name等列名,如图:

MediaColums

MediaColums

必备知识

1.AlertDialog创建对话框知识

2.SQLite数据库知识以及通过Cursor对象访问的知识

掌握了以上内容看起源码才能不费力气,鉴于篇幅限制,读者自行补充

源码

    MainActivity.java
    -------------------------------------------------------------------------------------
    package com.golfer.www.musicboxdemo;

    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.support.v7.app.AppCompatActivity;
    import android.app.Activity;
    import android.content.BroadcastReceiver;
    import android.content.ContentResolver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.database.Cursor;
    import android.media.MediaScannerConnection;
    import android.provider.MediaStore;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageButton;
    import android.widget.RelativeLayout;
    import android.widget.TextView;

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    //最大歌曲数目
    final static int  max_num=500;
    //实际歌曲数目
    int real_num=0;
    //存储歌曲信息的数组
    private String[] Artist=new String[max_num];
    private String[] songname=new String[max_num];
    private String[] display_name=new String[max_num];
    //当前播放的歌曲的下标
    int current=0;
    //用于注册BrocastReceiver的action
	/**
	在Activity注册BrocastReceiver用到actionByService
	在Service注册BrocastReceiver用到actionByActivity,完全可以将actionByActivity在myService.java里面定义
	**/
    static final String actionByActivity="MusicBoxDemo.action.actionByActivity";
    static final String actionByService="MusicBoxDemo.action.actionByService";
    //控件
    TextView nameOfsong,artist;
    ImageButton stop,playOrpause,previous,next,menu;
	//BrocastReceiver
    ActivityReceiver receiver;
	//Intent
    Intent intent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //匹配控件
        nameOfsong=(TextView)findViewById(R.id.nameOfsong) ;
        artist=(TextView)findViewById(R.id.artist) ;
        previous=(ImageButton)findViewById(R.id.previous);
        playOrpause=(ImageButton)findViewById(R.id.playOrpause);
        stop=(ImageButton)findViewById(R.id.stop) ;
        next=(ImageButton)findViewById(R.id.next) ;
        menu=(ImageButton)findViewById(R.id.menu) ;
        //为按钮注册监听器
        previous.setOnClickListener(this);
        playOrpause.setOnClickListener(this);
        stop.setOnClickListener(this);
        next.setOnClickListener(this);
        menu.setOnClickListener(this);
        //读取MediaDataBase的信息(为数组赋值)
        prepareInformation();
        //注册BrocastReceiver
        IntentFilter filter=new IntentFilter();
        filter.addAction(actionByService);
        receiver=new ActivityReceiver();
        registerReceiver(receiver,filter);
        //启动Service
        this.intent=new Intent(MainActivity.this,myService.class);
        startService(intent);
    }

    @Override
    protected void onDestroy() {
		//取消对BrocastReceiver的注册
        unregisterReceiver(receiver);
		//停止myService
        stopService(this.intent);
        super.onDestroy();
        return;
    }

    //自定义BrocastReceiver,用于处理Service发过来的广播
    private class ActivityReceiver extends BroadcastReceiver{
        @Override
			//覆写onReceive
        public void onReceive(Context context, Intent intent) {
			//update获取当前MediaPlayer的播放状态
            int update=intent.getIntExtra("update",-1);
			//current获取当前播放歌曲
            int current=intent.getIntExtra("current",-1);
            //更新文本框内容
            if(current>=0){
                nameOfsong.setText(songname[current]);
                artist.setText(Artist[current]);
            }
            //更新按钮背景
            switch(update){
                case 0x11:
                    playOrpause.setImageResource(android.R.drawable.ic_media_pause);
                    break;
                case 0x12:
                    playOrpause.setImageResource(android.R.drawable.ic_media_play);
                    break;
                case 0x13:
                    playOrpause.setImageResource(android.R.drawable.ic_media_pause);
                    break;
            }
        }
    }
    //覆写onClick
    public void onClick(View source){
        final Intent intent=new Intent(actionByActivity);
		//根据source的ID为intent的Extra属性赋不同的值
        switch(source.getId()){
            case R.id.previous:
                intent.putExtra("contral",0x124);
                break;
            case R.id.playOrpause:
                intent.putExtra("contral",0x125);
                break;
            case R.id.stop:
                intent.putExtra("contral",0x126);
                break;
            case R.id.next:
                intent.putExtra("contral",0x127);
                break;
            case R.id.menu:
				//加载单选列表对话框
                final AlertDialog dialog=new AlertDialog.Builder(MainActivity.this).setTitle("歌曲列表").setSingleChoiceItems(display_name, -1,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent=new Intent(actionByActivity);
                                intent.putExtra("contral",which);
                                sendBroadcast(intent);
                                System.out.println("which:"+which);
                               dialog.dismiss();
                            }
                        }).create();
                dialog.show();
        }
        sendBroadcast(intent);
    }
     //读取歌曲信息
     private  void prepareInformation(){
       //创建ContentResolver
       ContentResolver resolver=getContentResolver();
       //读取艺术家信息
       Cursor cursor= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
       int index=0;
       while(cursor.moveToNext()){
           Artist[index++]=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST));
       }
       real_num=index;
       index=0;
       //读取歌曲名
       Cursor cursor2= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
       while(cursor2.moveToNext()){
           songname[index++]=cursor2.getString(cursor2.getColumnIndex(MediaStore.MediaColumns.TITLE));
       }
       index=0;
	   //读取歌曲的展示名
       Cursor cursor3= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
       while(cursor3.moveToNext()){
           display_name[index++]=cursor3.getString(cursor3.getColumnIndex(MediaStore.MediaColumns.TITLE));
       }
       }
    }
------------------------------------------------------------------------------------------

    myService.java
    
    package com.golfer.www.musicboxdemo;

    import android.app.Service;
    import android.content.BroadcastReceiver;
    import android.content.ContentResolver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.database.Cursor;
    import android.media.AudioManager;
    import android.media.MediaPlayer;
    import android.net.Uri;
    import android.os.IBinder;
    import android.provider.MediaStore;
    import android.support.annotation.Nullable;
    import android.support.annotation.StringDef;

    import java.io.IOException;

    /**
     * Created by Golfer on 2017/2/4.
    */

    public class myService extends Service {
    String[] path=new String[MainActivity.max_num];
    MediaPlayer player;
    int current=0;
    int status=0x11;
    int num;
    ServiceReceiver receiver;
    public void onCreate() {
        System.out.println("---Service已启动---");
        
        IntentFilter filter=new IntentFilter();
        filter.addAction(MainActivity.actionByActivity);
        receiver=new ServiceReceiver();
        registerReceiver(receiver,filter);
        
       if((player=new MediaPlayer())!=null) System.out.println("---MediaPlayer创建成功---");
        else System.out.println("---MediaPlayer创建失败---");
       
        player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                player.reset();
                try {
                    current=(current+1)%(num-1);
                    player.setDataSource(path[current]);
                    player.prepare();
                    player.start();
                }catch (IOException e){
                    e.printStackTrace();
                }
                Intent intent=new Intent(MainActivity.actionByService);
                intent.putExtra("current",current);
                sendBroadcast(intent);
            }
        });
        ContentResolver resolver=getContentResolver();
        Cursor cursor= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
        int index=0;
        while(cursor.moveToNext()){
                path[index]=cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
                System.out.println("path["+index+"]:"+path[index]);
            index++;
             }
        num=index;
        return;

    }


    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        player.release();
        super.onDestroy();
        return;
    }

    private class ServiceReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            int contral=intent.getIntExtra("contral",-1);
            if(contral>=0) {
                switch (contral) {
                    case 0x124:
                        if (status == 0x12 && current > 0) {
                            player.reset();
                            try {
                                player.setDataSource(path[current - 1]);
                                player.prepare();
                                player.start();
                                System.out.println("---成功播放上一首歌曲---");
                                current = current - 1;
                            } catch (IOException E) {
                                E.printStackTrace();
                            }
                        }
                        break;
                    case 0x125:
                        if (status == 0x11) {
                            try {
                                player.reset();
                                player.setDataSource(path[current]);
                                player.prepare();
                                player.start();
                                status = 0x12;
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else if (status == 0x12) {
                            player.pause();
                            status = 0x13;
                        } else if (status == 0x13) {
                            player.start();
                            status = 0x12;
                        }
                        break;
                    case 0x126:
                        if (status == 0x12 || status == 0x13) {
                            player.stop();
                            status = 0x11;
                        }
                        break;
                    case 0x127:
                        if (status == 0x12 && current < num - 1) {
                            player.reset();
                            try {
                                player.setDataSource(path[current + 1]);
                                player.prepare();
                                player.start();
                                System.out.println("---成功播放下一首歌曲---");
                                current = current + 1;
                            } catch (IOException E) {
                                E.printStackTrace();
                            }
                        }
                        break;
                    default:
                        player.reset();
                        try {
                            player.setDataSource(path[contral]);
                            player.prepare();
                            player.start();
                            current=contral;
                            System.out.println("---成功切换播放第"+(current+1)+"首歌---");
                        }catch (IOException e){
                            e.printStackTrace();
                        }
                }
            }
            Intent intent1=new Intent(MainActivity.actionByService);
            intent1.putExtra("update",status);
            intent1.putExtra("current",current);
            sendBroadcast(intent1);
        }
    }
    }

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.golfer.www.musicboxdemo.MainActivity">

    <TextView
        android:text="歌曲名称"
        android:layout_height="wrap_content"
        android:id="@+id/nameOfsong"
        android:layout_alignParentStart="true"
        android:layout_width="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Small" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@android:drawable/ic_media_next"
        android:layout_alignTop="@+id/previous"
        android:layout_alignParentEnd="true"
        android:id="@+id/next" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@android:drawable/ic_media_previous"
        android:layout_marginBottom="132dp"
        android:id="@+id/previous"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@android:drawable/ic_media_pause"
        android:id="@+id/playOrpause"
        android:layout_alignTop="@+id/next"
        android:layout_centerHorizontal="true" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@android:drawable/ic_lock_power_off"
        android:id="@+id/stop"
        android:layout_marginTop="29dp"
        android:layout_below="@+id/playOrpause"
        android:layout_toRightOf="@+id/previous"
        android:layout_toEndOf="@+id/previous" />

    <TextView
        android:text="歌手"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/artist"
        android:textAppearance="@style/TextAppearance.AppCompat.Small"
        android:layout_marginTop="44dp"
        android:layout_below="@+id/nameOfsong"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@android:drawable/ic_menu_sort_by_size"
        android:layout_alignBottom="@+id/stop"
        android:layout_toLeftOf="@+id/next"
        android:layout_toStartOf="@+id/next"
        android:layout_marginRight="11dp"
        android:layout_marginEnd="11dp"
        android:id="@+id/menu" />

    </RelativeLayout>
-------------------------------------------------------------------------------------------------
    AndroidManifest.xml    //记得要在此文件中注册myService,
    //并且为程序添加访问外部存储器的权限,由于两个BrocastReceiver均使用动态注册方式,
    //故无须在此文件注册

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.golfer.www.musicboxdemo">

    <application
        android:allowBackup="true"
        android:icon="mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".myService">
        </service>
    </application>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    </manifest>

效果展示

初始界面

正在播放,第一行中间的按钮呈三角形

歌曲列表

由于知识水平限制,app难免存在各种bug,请各位指教,或者有其他好的开发思路,欢迎共同交流! app下载链接,欢迎给我的所谓的app找bug,谢谢指教! http://pan.baidu.com/s/1jHNa5Bg