zoukankan      html  css  js  c++  java
  • 音频音乐播放 Service

    界面效果:

           

    界面就一个播放的Button和一个进度条SeekBar,也可以自己加上两个显示时间的TextView;

    点击播放时,有音乐声音,进度条也会自动更新,Button文字变成暂停;

    当音乐播放完毕时,Button文字变成播放,进度条重新回原始点;

    1、添加权限

    这里需要可以读取音频文件的权限:

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

    2、添加依赖

    本项目中使用EventBus在Service和Activity之间通讯,需要加入EventBus的库:

    compile 'org.greenrobot:eventbus:3.0.0'

    3、布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MusicActivity">
    
        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.5" />
    
        <Button
            android:id="@+id/button_play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="播放"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.6" />
    
    </android.support.constraint.ConstraintLayout>

    4、Service

    创建Service:new->Service

    在Service中,创建一个MediaPlayer对象和一个Binder类,Binder可以用来与Activity之间传输数据(进度条,点击事件),但是只能Activity主动通过Binder进行相互;播放结束后,音乐跳回最开始,并用EventBus主动通知Activity“播放完成”;

    代码以及注释如下:

    package com.example.m_evolution.Service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.media.MediaPlayer;
    import android.os.Binder;
    import android.os.Environment;
    import android.os.IBinder;
    import android.os.Parcel;
    import android.os.RemoteException;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    import org.greenrobot.eventbus.EventBus;
    
    import java.io.IOException;
    
    public class MusicService extends Service {
        private MediaPlayer mediaPlayer = new MediaPlayer();
        private String file_dir;
    
        private final int CODE_PLAY_MUSIC = 101;  //播放音乐
        private final int CODE_PAUSE_MUSIC = 102;  //暂停音乐
        private final int CODE_UPDATE_SEEKBAR = 103;  //更新进度条
        private final int CODE_CHANGE_SEEKBAR = 104;  //拖动进度条更新音乐
    
        public MusicService() {
    
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            file_dir = intent.getExtras().getString("file_dir", Environment.getExternalStorageDirectory()+"/record/"+"a.mp3");
            try {
                mediaPlayer.setDataSource(file_dir);
                mediaPlayer.prepare();
                mediaPlayer.setLooping(false);
                mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mediaPlayer) {
                        try {
                            mediaPlayer.stop();
                            mediaPlayer.prepare();
                            mediaPlayer.seekTo(0);
                            //用EventBus通知Activity已完成播放
                            EventBus.getDefault().post("finish_music");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            } catch (IOException e) {
                Log.d("MusicService", "未找到文件");
                e.printStackTrace();
            }
            return new MyBinder();
        }
    
        //Service与Activity交互数据
        public class MyBinder extends Binder{
            @Override
            //code为操作代码
            //data为从Activity中传到Service的数据
            //reply为从Service传到Activity的数据
            protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
                switch (code)
                {
                    case CODE_PLAY_MUSIC:  //播放
                        StartMusic();
                        break;
                    case CODE_PAUSE_MUSIC:  //暂停
                        PauseMusic();
                        break;
                    case CODE_UPDATE_SEEKBAR:  //更新Activity中的进度条
                        int out_data[]=new int[2];  //传出的数据
                        out_data[0] = 0;
                        out_data[1] = 0;
                        if(mediaPlayer != null){
                            out_data[0] = mediaPlayer.getCurrentPosition();  //获取现在播放时间
                            out_data[1] = mediaPlayer.getDuration();  //获取总时长
                        }
                        reply.writeIntArray(out_data);
                        break;
                    case CODE_CHANGE_SEEKBAR:
                        int in_data = data.readInt(); //传入的数据:播放时间
                        if(mediaPlayer != null){
                            mediaPlayer.seekTo(in_data);
                        }
                        break;
                }
                return super.onTransact(code, data, reply, flags);
            }
        }
    
        public void StartMusic(){
            if (mediaPlayer!=null && !mediaPlayer.isPlaying()){
                mediaPlayer.start();
            }
        }
    
        public void PauseMusic(){
            if (mediaPlayer!=null && mediaPlayer.isPlaying()){
                mediaPlayer.pause();
            }
        }
    
        public void StopMusic(){
            if (mediaPlayer!=null){
                mediaPlayer.stop();
                mediaPlayer = null;
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            StopMusic();
        }
    }

    5、Activity中的代码及注释如下。

    启动Service的方式有两种:
    1、startService(intent);
    2、bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    第一种是纯粹开启一个Service,跟Activity没有联系,就算Activity退出了,Service也还会运行,但是只能同一个Service只能开启一个,即多个Activity开启某个Service,该Service只会Create一次。=
    第二种方式中,Activity会与Service建立联系,Activity销毁时,Service也会销毁,执行onDestroy函数,但这不代表音乐就停止了,你需要在onDestroy函数中手动停止音乐。

    PS:这里面的IBinder对象,在开启Service后不是马上就有值的,所以不要在StartService之后立马对IBinder进行操作,因为它是null的;可以像我下面这样,建一个线程做个死循环,当IBinder对象不为空时再去进行操作。

    package com.example.m_evolution;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.os.Parcel;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.Button;
    import android.widget.SeekBar;
    
    import com.example.m_evolution.Service.MusicService;
    
    import org.greenrobot.eventbus.EventBus;
    import org.greenrobot.eventbus.Subscribe;
    import org.greenrobot.eventbus.ThreadMode;
    
    public class MusicActivity extends AppCompatActivity {
    
        private SeekBar seekBar;
        private Button button_play;
        private String file_dir; //音频文件路径
        private ServiceConnection serviceConnection; //用来连接Service
        private IBinder mBinder;  //用来与Service交互数据
        private boolean isPlaying;  //是否正在进行播放
    
        private final int CODE_PLAY_MUSIC = 101;  //播放音乐
        private final int CODE_PAUSE_MUSIC = 102;  //暂停音乐
        private final int CODE_UPDATE_SEEKBAR = 103;  //更新进度条
        private final int CODE_CHANGE_SEEKBAR = 104;  //拖动进度条更新音乐
    
        private final int INIT_VIEW = 0;
        private final int UPDATE_SEEKBAR = 1;
    
        //更新进度条的线程
        private Thread thread_update_view;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_music);
            //注册EventBus,以便Service通知Activity意播放完毕
            EventBus.getDefault().register(this);
    
            FindView();
            InitData();
            StartService();  //开启Service
            //初始化进度条以及设置监听器
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(mBinder == null) ;  //等待mBinder被赋值,表示Service成功开始
                    Message msg = new Message();
                    msg.what = 0;
                    mHandler.sendMessage(msg);
                }
            }).start();
        }
    
        private void FindView(){
            seekBar = findViewById(R.id.seekbar);
            button_play = findViewById(R.id.button_play);
        }
    
        private void InitData(){
            file_dir = Environment.getExternalStorageDirectory()+"/record/"+"a.mp3";  //这是提前放在手机路径“record”文件夹中的音频文件a.mp3
            isPlaying = false;
        }
    
        //开启Service
        private void StartService(){
            serviceConnection = new ServiceConnection() {
                //与Service建立连接
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    mBinder = iBinder;  //绑定Binder
                }
    
                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                    serviceConnection = null;
                }
            };
            Intent intent = new Intent(this, MusicService.class);
            Bundle bundle = new Bundle();
            bundle.putString("file_dir", file_dir);
            intent.putExtras(bundle);
            //startService(intent);
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
    
        private void SetListener(){
            button_play.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(!isPlaying){ //如果现在是暂停状态
                        StartMusic();
                    }
                    else{ //如果现在是播放状态
                        PauseMusic();
                    }
                    isPlaying = !isPlaying;
                }
            });
            //进度条的事件监听器,手指抬起时根据进度条改变音乐播放时间
            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
    
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
    
                }
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    int code = CODE_CHANGE_SEEKBAR;
                    Parcel data = Parcel.obtain();
                    Parcel reply = Parcel.obtain();
                    data.writeInt(seekBar.getProgress());  //写入进度条现在的数据
                    try {
                        mBinder.transact(code, data, reply, 0);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        private void StartMusic(){
            button_play.setText("暂停");
            int code = CODE_PLAY_MUSIC;
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                mBinder.transact(code,data, reply, 0);  //告诉Service进行播放
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            thread_update_view = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (isPlaying && mBinder!=null){
                        Message msg = new Message();
                        msg.what = UPDATE_SEEKBAR;
                        mHandler.sendMessage(msg);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            thread_update_view.start();
        }
    
        private void PauseMusic(){
            button_play.setText("播放");
            int code = CODE_PAUSE_MUSIC;
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                mBinder.transact(code,data, reply, 0);  //告诉Service进行暂停
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        //更新Activity中的进度条或TextView等
        private void UpdateView(){
            try {
                int code = CODE_UPDATE_SEEKBAR;
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                mBinder.transact(code, data, reply, 0);
                int read_data[] = new int[2];  //读取到的音乐播放时间以及总长度
                reply.readIntArray(read_data);
                seekBar.setProgress(read_data[0]);   //音乐正在播放的时间
                seekBar.setMax(read_data[1]);  //音乐总长度
                //这里还可以设置显示时间的TextView
                //......
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        private Handler mHandler = new Handler(){
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {      //判断标志位
                    case INIT_VIEW:   //初始化进度条以及监听器
                        UpdateView();
                        SetListener();
                        break;
                    case UPDATE_SEEKBAR:
                        UpdateView();
                        break;
                }
            }
        };
    
        @Override
        protected void onDestroy() {
            if(serviceConnection != null){
    unbindService(serviceConnection); serviceConnection
    = null;
    }
    if(EventBus.getDefault().isRegistered(this)) { EventBus.getDefault().unregister(this); } super.onDestroy(); } //已完成播放 @Subscribe(threadMode = ThreadMode.MAIN) public void Event(String str) { if(str.equals("finish_music")){ if(thread_update_view!=null){ thread_update_view.interrupt(); thread_update_view = null; } button_play.setText("播放"); seekBar.setProgress(0); isPlaying = false; } } }

    6、音频文件

    自备一个音频文件,这里我放在了Environment.getExternalStorageDirectory()+"/record/"+"a.mp3"。

  • 相关阅读:
    Java并发编程(六)——Callable接口
    java基础——反射
    java基础——序列化
    java基础——IO
    java基础——File类
    操作nginx时遇到的各种问题
    linux安装nginx 简单版
    linux 重命名文件和文件夹
    Linux 程序安装目录 /opt 目录和 /usr/local 目录
    Linux 各种安装包
  • 原文地址:https://www.cnblogs.com/zhaozilongcjiajia/p/10723838.html
Copyright © 2011-2022 走看看