欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android播放音频

程序员文章站 2022-06-18 20:18:29
...

最近需要项目涉及到了音频播放功能,有可能需要在后台播放,并且在通知栏显示状态。通过查阅资料,自己模拟了一个demo。样式如下:

android播放音频

android播放音频

在运行中,界面内的控件更新使用的是eventBus发送的,在Notification中,是通过广播发送操作,目前只加入了播放和暂停的功能。
首先是在MainActivity注册的广播和Notification,全部设置为静态的,广播是写在了播放界面

    public BPlayActivity.MusicServiceReceiver mr;
    public static NotificationManager manager;
    public static RemoteViews remoteViews;
    public static Notification notify;
    // 广播监听的动作
    public static final String ACTION_BF = "ACTION_BF";

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        showStartNotification();
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public void showStartNotification() {
        // 注册receiver
        mr = new BPlayActivity().getReceiver();
        IntentFilter intentfilter = new IntentFilter();
        intentfilter.addAction(ACTION_BF);
        registerReceiver(mr, intentfilter);
        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        notify = builder.build();
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.layout_view_title);
        notify.contentView = remoteViews; // 设置下拉图标
        notify.bigContentView = remoteViews; // 防止显示不完全,需要添加apisupport
        notify.flags = Notification.FLAG_ONGOING_EVENT;
        notify.icon = R.drawable.img_reward_bg_down;
        Intent intent1 = new Intent();
        intent1.setAction(ACTION_BF);
        PendingIntent contentIntent = PendingIntent.getBroadcast(this, 0,
                intent1, 0);
        remoteViews.setOnClickPendingIntent(R.id.iv_notify_play, contentIntent);
    }

在BPlayActivity.class 播放界面,使用了SeekBar和自定义的MediaPlayer。

public class MyPlay implements MediaPlayer.OnBufferingUpdateListener,
        MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener,
        MediaPlayer.OnErrorListener {
    public static SeekBar skbProgress;
    private Timer mTimer = new Timer();
    public static MediaPlayer mediaPlayer;
    private static onShowTimeListener listener;
    /**
     * 0未播放
     * 1播放
     * 2暂停
     */
    public static int playState = 0;

    public interface onShowTimeListener {
        public void showTime(long time, long endtime);
    }

    public MyPlay(SeekBar skbProgress, onShowTimeListener showTimeListener) {
        skbProgress.setClickable(false);
        listener = showTimeListener;
        setSkbProgress(skbProgress);
        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
        }
        mTimer.schedule(mTimerTask, 0, 300);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setOnBufferingUpdateListener(this);
        mediaPlayer.setOnPreparedListener(this);
//        skbProgress.setOnSeekBarChangeListener(this);
    }

    public void play() {
        playState = 1;
        mediaPlayer.start();
    }

    public static void setSkbProgress(SeekBar skbProgre) {
        skbProgress = skbProgre;
        skbProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (mediaPlayer != null) {
                    progress = i * mediaPlayer.getDuration()
                            / seekBar.getMax();
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                Log.i("msg", "onStartTrackingTouch");
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (mediaPlayer != null) {
                    mediaPlayer.seekTo(progress);
                    Log.i("msg", "progress==" + progress);
                    if (progress == mediaPlayer.getDuration()) {
                        playState = 0;
                        if (listener != null)
                            listener.showTime(progress, progress);
                    }
                }
            }
        });
    }

    public void playUrl(String videoUrl) {
        try {
            mediaPlayer.reset();
            mediaPlayer.setDataSource(videoUrl);
            mediaPlayer.prepare();//prepare之后自动播放
            play();
            skbProgress.setClickable(true);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            ToastBean bean = new ToastBean();
            bean.setMsg("音频地址错误");
            EventBus.getDefault().post(bean);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void pause() {
        playState = 2;
        mediaPlayer.pause();
    }

    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            playState = 0;
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    /*******************************************************
     * 通过定时器和Handler来更新进度条
     ******************************************************/
    TimerTask mTimerTask = new TimerTask() {
        @Override
        public void run() {
            if (mediaPlayer == null)
                return;
            try {
                if (mediaPlayer.isPlaying() && skbProgress.isPressed() == false) {
                    handleProgress.sendEmptyMessage(0);
                }
            } catch (Exception e) {
            }

        }
    };
    Handler handleProgress = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            if (mediaPlayer != null) {
                int position = mediaPlayer.getCurrentPosition();
                int duration = mediaPlayer.getDuration();

                if (duration > 0) {
                    long pos = skbProgress.getMax() * position / duration;
                    TimeBean bean = new TimeBean();
                    bean.setPos(pos);
                    EventBus.getDefault().post(bean);
                }
                if (listener != null)
                    listener.showTime(position, duration);
            }
        }
    };

    @Override
    public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
        skbProgress.setSecondaryProgress(i);
        if (mediaPlayer.getDuration() != 0) {
            int currentProgress = skbProgress.getMax() * mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration();
        }
    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {

    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        mediaPlayer.start();
        playState = 1;
    }

    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
        return false;
    }

    //返回播放状态
    public int isPalyState() {
        return playState;
    }


    static int progress;

}

在播放界面首相注册EventBus

    private static ItemData intentBean;
    private ImageView iv_play;
    private TextView tv_starttime;
    private TextView tv_endtime;
    private static SeekBar sb_play;
    private ImageView iv_roat;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bplay);
        EventBus.getDefault().register(this);
        intentBean = (ItemData) getIntent().getSerializableExtra("selList");
        initView();
        initData();
    }
//因为是用过列表点击进入的,这里判断了进入的时候是否在播放,如果是相同的就同步时间,如果不同,就将seekBar设置为0,
//音频继续播,知道点击了播放按钮,在请求另一个地址的音频进行播放,时间重置
   private void initData() {
        if (PlayService.mPlay != null) {
            if (PlayService.playId == intentBean.getId()) {
                MyPlay.setSkbProgress(sb_play);
                //初始进入,判断是不是当前播放的Id
                if (PlayService.mPlay.mediaPlayer.isPlaying()) {
                    iv_play.setSelected(true);
                    if (notify != null) {
                        notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
                        manager.notify(0, notify);
                    }
                    setAnim(true);
                }
            }
        }
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.iv_play:
                //显示通知栏Notification
                manager.notify(0, notify);
                startService();
                break;
        }
    }


//这里的context使用的是Application的
 public void startService() {
        PlayService.sb_play = sb_play;
        Intent intent = null;
        intent = new Intent(MyApplication.getContext(), PlayService.class);
        intent.putExtra("intentBean", intentBean);
        MyApplication.getContext().startService(intent);
    }


//设置中间的原图播放旋转效果
    private boolean isAnimStart = false;
    ObjectAnimator rotate = null;
    Float currentValue = 0f;

    public void setAnim(boolean state) {
        if (state) {
            if (!isAnimStart) {
                isAnimStart = true;
                rotate = ObjectAnimator.ofFloat(iv_roat, "Rotation",
                        currentValue - 360, currentValue);
                // 设置持续时间
                rotate.setDuration(3000);
                // 设置循环播放
                rotate.setRepeatCount(ObjectAnimator.INFINITE);
                rotate.setInterpolator(new LinearInterpolator());//not stop
                rotate.setRepeatCount(-1);//set repeat time forever
                rotate.start();
                rotate.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        // 监听动画执行的位置,以便下次开始时,从当前位置开始
                        currentValue = (Float) animation.getAnimatedValue();

                    }
                });
            }
        } else {
            isAnimStart = false;
            if (rotate != null)
                rotate.cancel();
        }
    }
   /**
     * 在主线程中显示当前的控件状态
     */
    @Subscribe(threadMode = ThreadMode.MainThread)
    public void EventUpdateTime(UpdateTimeBean bean) {
        if (tv_starttime == null || tv_endtime == null) {
            return;
        }
        if (PlayService.playId == intentBean.getId()) {
            tv_starttime.setText(DateUtil.setHmsQian(bean.getStartTime()));
            tv_endtime.setText(DateUtil.setHmsQian(bean.getEndTime()));
            if (DateUtil.setHmsQian(bean.getStartTime()).equals(DateUtil.setHmsQian(bean.getEndTime()))) {
                //说明是播放完成了
                iv_play.setSelected(false);
                if (notify != null) {
                    notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
                    manager.notify(0, notify);
                }
                setAnim(false);
            }
        }
    }

    /**
     * 在主线程中显示当前的控件状态
     */
    @Subscribe(threadMode = ThreadMode.MainThread)
    public void EventPlayState(BPlayStateBean bean) {
        if (iv_play == null) {
            return;
        }
        if (bean.isSelect()) {
            iv_play.setSelected(true);
            if (notify != null) {
                notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
                manager.notify(0, notify);
            }
            setAnim(true);
        } else {
            iv_play.setSelected(false);
            if (notify != null) {
                notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
                manager.notify(0, notify);
            }
            setAnim(false);
        }
    }

    /**
     * 设置seekVar的显示状态
     *
     * @param bean
     */
    @Subscribe(threadMode = ThreadMode.MainThread)
    public void EventPlayState(TimeBean bean) {
        if (PlayService.playId == intentBean.getId() && sb_play != null) {
            sb_play.setProgress((int) bean.getPos());
        }
    }

    /**
     * 返回错误提示
     *
     * @param bean
     */
    @Subscribe(threadMode = ThreadMode.MainThread)
    public void EventMsg(ToastBean bean) {
        Toast.makeText(this, bean.getMsg(), Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }


    public MusicServiceReceiver getReceiver() {
        return new MusicServiceReceiver();
    }

    class MusicServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            manager.notify(0, notify);
            startService();
        }
    }

再通过一个Service播放音频

public class PlayService extends IntentService {
    public static MyPlay mPlay;
    public static SeekBar sb_play;
    private ItemData intentBean;
    public static int playId = -1;
    public static int intentId = -1;

    public PlayService() {
        super("PlayService");
    }


    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (intent != null) {
            intentBean = (ItemData) intent.getSerializableExtra("intentBean");
            intentId = intentBean.getId();

            if (intentId != playId && mPlay != null) {
                //说明是不同的列表,将当前的清空,再进行播放,相同的话,就设置当前的Post
                mPlay.stop();
                mPlay = null;
            }
            if (mPlay == null) {
                mPlay = new MyPlay(sb_play, new MyPlay.onShowTimeListener() {
                    @Override
                    public void showTime(long time, long endtime) {
                        if (DateUtil.setHmsQian(time).equals(DateUtil.setHmsQian(endtime))) {
                            //说明是播放完成了
                            mPlay.playState = 0;
                        }
                        UpdateTimeBean updateTimeBean = new UpdateTimeBean();
                        updateTimeBean.setStartTime(time);
                        updateTimeBean.setEndTime(endtime);
                        EventBus.getDefault().post(updateTimeBean);
                    }

                });
            }
            playId = intentBean.getId();
            if (mPlay.isPalyState() == 0) {
                thread.start();
            } else if (mPlay.isPalyState() == 1) {
                mPlay.pause();
                setState(false);
                if (notify != null) {
                    notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
                    manager.notify(0, notify);
                }
            } else {
                mPlay.play();
                setState(true);
                if (notify != null) {
                    notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
                    manager.notify(0, notify);
                }
            }
        }
    }

    public void setState(boolean state) {
        BPlayStateBean bean = new BPlayStateBean();
        bean.setSelect(state);
        EventBus.getDefault().post(bean);
    }

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            if (!TextUtils.isEmpty(intentBean.getUrl()) && mPlay != null) {
                mPlay.playUrl(intentBean.getUrl());
                if (mPlay.isPalyState() == 1) {
                    setState(true);
                } else {
                    setState(false);
                }
            } else {
                showMessage("该音频为空");
            }
        }
    });

    private void showMessage(String msg) {
        ToastBean bean = new ToastBean();
        bean.setMsg(msg);
        EventBus.getDefault().post(bean);
    }
}

//在DateUtil中转换时间

 /**
     * 给一个时间,然后算出总长度hms/1000
     **/
    public static String setHmsQian(long time) {
        long hour = (time / (60 * 60 * 1000));
        long min = ((time / (60 * 1000)) - hour * 60);
        long s = (time / 1000 - hour * 60 * 60 - min * 60);
        String str_s = s + "";
        String str_m = min + "";
        String str_h = hour + "";
        if (min < 10) {
            str_m = "0" + min;
        }
        if (s < 10) {
            str_s = "0" + s;
        }
        if (hour < 10) {
            str_h = "0" + hour;
        }
        if (hour <= 0) {
            return str_m + ":" + str_s;
        } else {
            return str_h + ":" + str_m + ":" + str_s;
        }
    }

播放界面的布局文件

<?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:background="#008888"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <SeekBar
        android:id="@+id/sb_play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:maxHeight="1dp"
        android:progressDrawable="@color/white"
        android:thumbTint="@color/c_ff7a7a" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:orientation="horizontal"
        android:paddingLeft="12dp"
        android:paddingRight="12dp">

        <TextView
            android:id="@+id/tv_starttime"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="00:00"
            android:textColor="@color/white"
            android:textSize="13sp" />

        <TextView
            android:id="@+id/tv_endtime"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:text="00:00"
            android:textColor="@color/white"
            android:textSize="13sp" />
    </LinearLayout>

    <ImageView
        android:id="@+id/iv_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:src="@drawable/select_playvideo" />

    <ImageView
        android:id="@+id/iv_roat"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:scaleType="centerCrop"
        android:layout_marginTop="20dp"
        android:src="@drawable/img_share_friend" />
</LinearLayout>

通知栏的布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/iv_notify_head"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/img_default_head" />

    <TextView
        android:id="@+id/tv_notify_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/iv_notify_head"
        android:layout_toEndOf="@+id/iv_notify_head"
        android:layout_toRightOf="@+id/iv_notify_head"
        android:paddingBottom="10dp"
        android:paddingRight="20dp"
        android:text="标题1"
        android:textSize="15sp" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/iv_notify_head"
        android:layout_below="@id/tv_notify_title"
        android:layout_toRightOf="@id/iv_notify_head">

        <ImageView
            android:id="@+id/tv_notify_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:paddingRight="20dp"
            android:src="@drawable/img_left" />

        <ImageView
            android:id="@+id/iv_notify_play"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/tv_notify_left"
            android:src="@drawable/img_play" />

        <ImageView
            android:id="@+id/tv_notify_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/iv_notify_play"
            android:paddingLeft="20dp"
            android:paddingRight="10dp"
            android:src="@drawable/img_right" />
    </RelativeLayout>

</RelativeLayout>

在就是在AndroidManifest中启动服务和网络请求的权限

    <activity android:name=".BPlayActivity" />
      <uses-permission android:name="android.permission.INTERNET" />
public class MyApplication extends Application {

    public static Context mContext = null;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
    }

    public static Context getContext() {
        return mContext;
    }


}

在build.gradle中添加引用,里面有些其他引用不用管

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    //compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
    compile 'de.greenrobot:eventbus:3.0.0-beta1'
    testCompile 'junit:junit:4.12'
}

一个demo就基本完成了。