Android之MediaPlayer(两种)基本使用方式

2/13/2017来源:iOS开发人气:716

1.播放应用的资源文件(res/raw/)

java代码示例:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    PRivate MediaPlayer mediaPlayer;
    private SeekBar seekBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        seekBar = (SeekBar) findViewById(R.id.seekBar);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //获取拖动结束之后的位置
                int progress=seekBar.getProgress();
                //跳转到某个位置播放
                mediaPlayer.seekTo(progress);
            }
        });
    }
    public void play(View view){
        ImageButton imageButton= (ImageButton) view;
       if(mediaPlayer==null){
           //实例化MediaPlayer
           mediaPlayer = MediaPlayer.create(this, R.raw.one);
           mediaPlayer.start();
           //把图标变为暂停图标
           imageButton.setImageResource(android.R.drawable.ic_media_pause);
          //获取音乐的总时长
           int duration=mediaPlayer.getDuration();
           //设置进度条的最大值为音乐总时长
           seekBar.setMax(duration);
           new MyThread().start();
        }else if(mediaPlayer.isPlaying()){
            mediaPlayer.pause();
            //把图标变为播放图标
            imageButton.setImageResource(android.R.drawable.ic_media_play);
        }else{
           mediaPlayer.start();
           //把图标变为暂停图标
           imageButton.setImageResource(android.R.drawable.ic_media_pause);
       }
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            while(seekBar.getProgress()<=seekBar.getMax()){
              //获取当前位置音乐播放的位置
                 int currentPosition=mediaPlayer.getCurrentPosition();
              //让进度条滚动起来
                seekBar.setProgress(currentPosition);
            }
        }
    }
}

简单的一个布局文件:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="16dp"
    android:layout_marginLeft="16dp"
    >

    <SeekBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:id="@+id/seekBar" />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_media_play"
        android:onClick="play"
        />
</LinearLayout>

程序说明:

以上例子描述了MediaPlayer播放应用内的音频步骤和方式.下面介绍的是播放内存卡中的音频

2.播放外部存储上的音频资源文件(sdcard)

代码实现

1)导入歌曲到手机SD卡的QQmusic/song目录中,这里我随便导入了几首歌曲:《三生三世》、《爱丫爱丫》、《安和桥》和《和你在一起》,路径可随自己而定。

新建一个类MusicService继承Service,在类中定义一个MyBinder,有一个方法用于返回MusicService本身,在重载onBind()方法的时候返回

 public final IBinder binder = new MyBinder();
    public class MyBinder extends Binder{
        MusicService getService() {
            return MusicService.this;
        }
    }
    /**
     * onBind 是 Service 的虚方法,因此我们不得不实现它。
     * 返回 null,表示客服端不能建立到此服务的连接。
     */
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
2)在MusicService中,声明一个MediaPlayer变量,进行设置歌曲路径,这里我选择歌曲1作为初始化时候的歌曲

private String[] musicDir = new String[]{
	    Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/三生三世.mp3",
            Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/爱丫爱丫.mp3",
            Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/安和桥.mp3",
            Environment.getExternalStorageDirectory().getAbsolutePath() +"/qqmusic/song/和你在一起.mp3"};
    private int musicIndex = 1;
    public static MediaPlayer mp = new MediaPlayer();
    public MusicService() {
        try {
            musicIndex = 1;
            mp.setDataSource(musicDir[musicIndex]);
            mp.prepare();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3)功能:有相关歌曲播放、暂停、停止、退出的方法,还可以切换上一首和下一首,主要方法如下:

//播放/暂停按钮
    public void playOrPause() {
        if(mp.isPlaying()){
            mp.pause();
        } else {
            mp.start();
        }
    }
    //下一首
    public void nextMusic() {
        if(mp != null && musicIndex < 3) {
            mp.stop();
            try {
                mp.reset();
                mp.setDataSource(musicDir[musicIndex+1]);
                musicIndex++;
                mp.prepare();
                mp.seekTo(0);
                mp.start();
            } catch (Exception e) {
                Log.d("hint", "can't jump next music");
                e.printStackTrace();
            }
        }else {
            Toast.makeText(this, "没有更多歌曲啦", Toast.LENGTH_SHORT).show();
        }
    }
    //上一首
    public void preMusic() {
        if(mp != null && musicIndex > 0) {
            mp.stop();
            try {
                mp.reset();
                mp.setDataSource(musicDir[musicIndex-1]);
                musicIndex--;
                mp.prepare();
                mp.seekTo(0);
                mp.start();
            } catch (Exception e) {
                Log.d("hint", "can't jump pre music");
                e.printStackTrace();
            }
        }else{
            Toast.makeText(this, "已经是第一首啦", Toast.LENGTH_SHORT).show();
        }
    }

4)注册MusicService并赋予权限,允许读取外部存储空间。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <service android:name="com.pxd.mediaplayer.MusicService" android:exported="true"/>

以上步骤是MusicService类核心代码。

接下来我们需在

5)MainAcitvity中声明ServiceConnection,调用bindService保持与MusicService通信,通过intent的事件进行通信,在onCreate()函数中绑定Service

 private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            musicService = ((MusicService.MyBinder)iBinder).getService();
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            musicService = null;
        }
    };
    private void bindServiceConnection() {
        Intent intent = new Intent(Main.this, MusicService.class);
        startService(intent);
        bindService(intent, sc, this.BIND_AUTO_CREATE);
    }
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        musicService = new MusicService();
        bindServiceConnection();
        seekBar = (SeekBar)this.findViewById(R.id.MusicSeekBar);
        seekBar.setProgress(musicService.mp.getCurrentPosition());
        seekBar.setMax(musicService.mp.getDuration());

        musicBegin = (TextView)this.findViewById(R.id.MusicBegin);
        musicTime = (TextView)this.findViewById(R.id.MusicTime);
        musicStatus=(TextView)this.findViewById(R.id.MusicStatus);
        btnPlayOrPause = (ImageButton) findViewById(R.id.BtnPlayorPause);

        Log.d("hint", Environment.getExternalStorageDirectory().getAbsolutePath()+"/You.mp3");
    }

bindService函数回调onSerciceConnented函数,通过MusiceService函数下的onBind()方法获得binder对象并实现绑定

6)通过Handle实时更新UI,这里主要使用了post方法并在Runnable中调用postDelay方法实现实时更新UI,Handle.post方法在onResume()中调用,使得程序刚开始时和重新进入应用时能够更新UI

在Runnable中更新SeekBar的状态,并设置SeekBar滑动条的响应函数,使歌曲跳转到指定位置

public android.os.Handler handler = new android.os.Handler();
    public Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if(musicService.mp.isPlaying()) {
              musicStatus.setText("状态:正在播放.....");
              btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause);
            } else {
              musicStatus.setText("状态:未播放");
               btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play);
            }
            musicBegin.setText(time.format(musicService.mp.getCurrentPosition()));
            musicTime.setText(time.format(musicService.mp.getDuration()));

            seekBar.setProgress(musicService.mp.getCurrentPosition());
            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    if (fromUser) {
                        musicService.mp.seekTo(seekBar.getProgress());
                    }
                }
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {

                }
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {

                }
            });
            handler.postDelayed(runnable, 100);
        }
    };
 @Override
    protected void onResume() {
        if(musicService.mp.isPlaying()) {
            musicStatus.setText("状态:正在播放.....");
            btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause);
        } else {
            musicStatus.setText("状态:未播放");
            btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play);
        }
        seekBar.setProgress(musicService.mp.getCurrentPosition());
        seekBar.setMax(musicService.mp.getDuration());
        handler.post(runnable);
        super.onResume();
        Log.d("hint", "handler post runnable");
    }

7)给每个按钮设置响应函数,在onDestroy()中添加解除绑定,避免内存泄漏

public void onClick(View view) {
        switch (view.getId()) {
            case R.id.BtnPlayorPause:
                musicService.playOrPause();
                break;
            case R.id.BtnQuit:
                handler.removeCallbacks(runnable);
                unbindService(sc);
                try {
                    System.exit(0);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            case R.id.btnPre:
                musicService.preMusic();
             // Toast.makeText(musicService, "上一首切换", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btnNext:
                musicService.nextMusic();
                //Toast.makeText(musicService, "下一首切换", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }

    @Override
    public void onDestroy() {
        unbindService(sc);
        super.onDestroy();
    }
8)在Button中赋予onClick属性指向接口函数

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"
    android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".Main">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/MusicStatus"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text=""/>
    <SeekBar
        android:layout_marginTop="16dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/MusicSeekBar"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/MusicBegin"
         android:text="  "/>
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/MusicTime"
         android:layout_marginLeft="270dp"
         android:text="  "/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal">
        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnPre"
            android:src="@android:drawable/ic_media_previous"
            android:onClick="onClick" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/BtnPlayorPause"
            android:src="@android:drawable/ic_media_play"
            android:onClick="onClick"
            />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnNext"
            android:src="@android:drawable/ic_media_next"
            android:onClick="onClick"/>



    </LinearLayout>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/BtnQuit"
            android:text="退出"
            android:onClick="onClick"/>

</LinearLayout>

这样就可以实现简单音乐播放器的播放。

效果图

      

总结

读取SD卡内存的时候,应该使用android.os.Environment库中的getExternalStorageDirectory()方法,然而并不能生效。应该再使用getAbsolutePath()获取绝对路径后读取音乐才生效。切换歌曲的时候try块不能正确执行。检查过后,也是执行了stop()函数后再重新setDataSource()来切换歌曲的,但是没有效果。查阅资料后,发现setDataSource()之前需要调用reSet()方法,才可以重新设置歌曲。

简述如何使用Handler实时更新UI

方法一: Handle的post方法,在post的Runable的run方法中,使用postDelay方法再次post该Runable对象,在Runable中更新UI,达到实时更新UI的目的 方法二: 多开一个线程,线程写一个持续循环,每次进入循环内即post一次Runable,然后休眠1000ms,亦可做到实时更新UI