Android mediaplayer的正确用法

公司主要跟音频视频杠上了,视频还好说,有完美的第三方框架,傻瓜式的拉过来修改一下就能用,视频用的gsyVideoPlayer,这个框架非常不错,链接:https://blog.csdn.net/qq_24531461/article/details/73456794,音频我也没经验啊,逼得紧,拿着mediaplayer就开整。中间的曲折就不说了,直接上图:

QQ截图20180911084730.png

技术点:

1:单例封装,这个单例是当前页面和服务持有的,主要是mediaplayer的持有方法。

2:service,主要播放要放在服务里,在当前页面绑定回调服务

3:定时播放采用的是线程1秒发一次,这个主要是要回调数据显示,所以只能这么搞

4:倍速,神坑,不同手机不一样

5:关闭播放器,就是后台正在播放,游客再看其他页面,这个时候退出了或者更换账号

一:单例

public class Voice_Utils {
    private Voice_Utils() {
    }

    private static Voice_Utils lei = new Voice_Utils();

    //单例模式
    public synchronized static Voice_Utils getInstance() {
        return lei;
    }

    //播放方法
    public void play(MediaPlayer mediaplayer) {
        mediaplayer.start();

    }


    //暂停
    public void pause(MediaPlayer mediaplayer) {
        mediaplayer.pause();
    }


    //判断是否正在播放中
    public boolean isplay(MediaPlayer mediaplayer) {
        return mediaplayer.isPlaying();
    }

    //获取播放时长
    public long getduring(MediaPlayer mediaplayer) {
        // TODO Auto-generated method stub
        return mediaplayer.getDuration();
    }

    //获取当前的播放进度
    public long getcurrentduring(MediaPlayer mediaplayer) {
        // TODO Auto-generated method stub
        return mediaplayer.getCurrentPosition();
    }

    //获取位置
    public int position(int current) {
        // TODO Auto-generated method stub
        return current;
    }

    //更上进度,设置进度..
    public void curento(int position, MediaPlayer mediaplayer) {
        mediaplayer.seekTo(position);
    }


    /**
     * 关闭播放器
     */
    public void closeMedia(MediaPlayer mediaplayer) {
        if (mediaplayer != null) {
            if (mediaplayer.isPlaying()) {
                mediaplayer.stop();
            }
            mediaplayer.release();
        }

    }
}

二:服务封装,因人而异,我写的也不一定对,很多地方待优化

 //TODO  开启了一个异步任务
    class MyTask extends AsyncTask {

        @Override //操作之前-- MainThread
        protected void onPreExecute() {
            super.onPreExecute();
            instance = Voice_Utils.getInstance();
        }

        @Override //耗时操作在这里
        protected Void doInBackground(Void... voids) {
            //缓存
            if (!ClassPathResource.isEmptyOrNull(urls)) {
                HttpProxyCacheServer proxy = MyApplication.getProxy(Services.this);
                proxyUrl = proxy.getProxyUrl(urls);


                Share.d("proxyUrl" + proxyUrl);


                if (urls != null && mMediaPlayer == null) {//判断执行如下操作-创建媒体播放类
                    mMediaPlayer = MediaPlayer.create(Services.this, Uri.parse(proxyUrl));
                } else if (urls != null && mMediaPlayer != null) {
                    String mp3 = (String) SharePrenfencesUtil.get(Services.this, "mp3", urls);
                    if (mp3.equals(urls)) {
                        Log.e("The", "Same");//同步播放进度
                    } else {
                        try {
                            mMediaPlayer.reset();//重置
                            mMediaPlayer.setDataSource(Services.this, Uri.parse(proxyUrl));
                            try {
                                mMediaPlayer.prepare();
                            } catch (IOException e) {
                                Log.e("media_player", "-prepare-");
                            } catch (IllegalStateException e) {
                                e.printStackTrace();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return null;
        }


        //此方法为随时更新函数,使用需在上面调用 publicProgress();
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override //操作完成-- MainThread
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);


            if (mMediaPlayer != null) {
                //如果他ture说明没有创建播放器  让手动点击创建播放
                if (type.equals("auto")) {
                    instance.play(mMediaPlayer);//播放响应1
                    sendBroadcast(new Intent("playing"));
                }


            }
            if (urls != null) {
                SharePrenfencesUtil.put(Services.this, "mp3", urls);
            }
            try { //回调
                Share_utils.getInstance().getMedia_playerCallBack().ComingPlayer(mMediaPlayer);
            } catch (Exception e) {
                Log.e("TAG", "ZhiXin ComingPlayer is null");
            }
        }
    }

三:定时关闭回显

package club.modernedu.lovebook.ui;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.view.KeyEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.bigkoo.pickerview.builder.TimePickerBuilder;
import com.bigkoo.pickerview.listener.OnTimeSelectListener;
import com.bigkoo.pickerview.view.TimePickerView;

import org.greenrobot.eventbus.EventBus;

import java.text.SimpleDateFormat;
import java.util.Date;


import club.modernedu.lovebook.R;
import club.modernedu.lovebook.base.BaseActivity;
import club.modernedu.lovebook.contants.CountdownTimerEvent;
import club.modernedu.lovebook.contants.PlayerEvent;
import club.modernedu.lovebook.receivers.AlarmReceiver;
import club.modernedu.lovebook.utils.SPUtils;
import club.modernedu.lovebook.utils.Share;
import club.modernedu.lovebook.utils.StatusBarUtil;

import static android.view.KeyEvent.KEYCODE_BACK;

/**
 * Created by jiangbin on 2018/7/21 17:51
 * 定时关闭
 */
public class TimeOffActivity extends BaseActivity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener {

    private LinearLayout back;
    private TextView title;
    private RadioGroup speed_rg;
    private RadioButton speed_rb1;
    private RadioButton speed_rb2;
    private RadioButton speed_rb3;
    private RadioButton speed_rb4;
    private RadioButton speed_rb5;
    private RadioButton speed_rb6;
    private RadioButton speed_rb7;
    private RadioButton speed_rb8;
    private TextView time_tv;
    private Context context;
    private int triggerAtTime;
    private int countdownTime;
    private String eventType = "ok";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置状态栏字体颜色
        StatusBarUtil.setColor(TimeOffActivity.this, getResources().getColor(R.color.main_color), 0);
        //底部虚拟按键的颜色
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().setNavigationBarColor(getResources().getColor(R.color.virtual_buttons));

        }
        setContentView(R.layout.activity_timeoff);
        context = TimeOffActivity.this;
        initView();
    }

    private void initView() {
        back = (LinearLayout) findViewById(R.id.back);
        title = (TextView) findViewById(R.id.title);
        speed_rg = (RadioGroup) findViewById(R.id.speed_rg);
        speed_rb1 = (RadioButton) findViewById(R.id.speed_rb1);
        speed_rb2 = (RadioButton) findViewById(R.id.speed_rb2);
        speed_rb3 = (RadioButton) findViewById(R.id.speed_rb3);
        speed_rb4 = (RadioButton) findViewById(R.id.speed_rb4);
        speed_rb5 = (RadioButton) findViewById(R.id.speed_rb5);
        speed_rb6 = (RadioButton) findViewById(R.id.speed_rb6);
        speed_rb7 = (RadioButton) findViewById(R.id.speed_rb7);
        speed_rb8 = (RadioButton) findViewById(R.id.speed_rb8);
        //time_tv = (TextView)findViewById(R.id.time_tv);


        speed_rg.setOnCheckedChangeListener(this);
        speed_rg.check(R.id.speed_rb1);


        title.setText("定时关闭");
        back.setOnClickListener(this);


        //判断选择哪个
        String offtime = (String) SPUtils.get(TimeOffActivity.this, "offtime", "");
        if (offtime.equals("10")) {
            speed_rb2.setChecked(true);
        } else if (offtime.equals("20")) {
            speed_rb3.setChecked(true);
        } else if (offtime.equals("30")) {
            speed_rb4.setChecked(true);
        } else if (offtime.equals("60")) {
            speed_rb5.setChecked(true);
        } else if (offtime.equals("90")) {
            speed_rb6.setChecked(true);
        } else if (offtime.length() > 0) {
            speed_rb7.setChecked(true);
            speed_rb7.setText(offtime);
        } else {
            speed_rb8.setChecked(true);
        }


    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.back:
                TimeOff();

                finish();
                break;
            default:
                break;
        }

    }

    /**
     * @time 2018/7/19 15:33
     * @Description 切换标签
     */
    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int i) {

        switch (i) {
            case R.id.speed_rb1:

                //播放完当前默认关闭
                //ToastUtils.showToast(SettingSpeedActivity.this, speed_rb1.getText().toString());

                break;
            case R.id.speed_rb2:
                //10分钟
                SPUtils.put(TimeOffActivity.this, "offtime", "10");
                triggerAtTime = (int) (SystemClock.elapsedRealtime() + 10 * 60 * 1000);
                countdownTime = 10 * 60 * 1000;
                eventType = "ok";
                //ToastUtils.showToast(SettingSpeedActivity.this, speed_rb2.getText().toString());
                break;
            case R.id.speed_rb3:
                //20分钟
                SPUtils.put(TimeOffActivity.this, "offtime", "20");
                triggerAtTime = (int) (SystemClock.elapsedRealtime() + 20 * 60 * 1000);
                countdownTime = 20 * 60 * 1000;
                eventType = "ok";
                // ToastUtils.showToast(SettingSpeedActivity.this, speed_rb3.getText().toString());
                break;
            case R.id.speed_rb4:
                //30分钟
                SPUtils.put(TimeOffActivity.this, "offtime", "30");
                triggerAtTime = (int) (SystemClock.elapsedRealtime() + 30 * 60 * 1000);
                countdownTime = 30 * 60 * 1000;
                eventType = "ok";
                //ToastUtils.showToast(SettingSpeedActivity.this, speed_rb4.getText().toString());
                break;
            case R.id.speed_rb5:
                //60分钟
                SPUtils.put(TimeOffActivity.this, "offtime", "60");
                triggerAtTime = (int) (SystemClock.elapsedRealtime() + 60 * 60 * 1000);
                countdownTime = 60 * 60 * 1000;
                eventType = "ok";
                // ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
                break;
            case R.id.speed_rb6:
                //90分钟
                SPUtils.put(TimeOffActivity.this, "offtime", "90");
                triggerAtTime = (int) (SystemClock.elapsedRealtime() + 90 * 60 * 1000);
                countdownTime = 90 * 60 * 1000;
                eventType = "ok";
                // ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
                break;
            case R.id.speed_rb7:
                //自定义
                //时间选择器

                // ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
                break;
            case R.id.speed_rb8:
                //不开启 直接取消现在定时的请求
                AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
                Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
                int requestCode = 0;
                PendingIntent pendIntent = PendingIntent.getBroadcast(getApplicationContext(),
                        requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

                //先取消一下之前没执行的定时
                alarmMgr.cancel(pendIntent);
                // ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
                SPUtils.put(TimeOffActivity.this, "offtime", "");
                eventType = "no";


                break;
        }
        speed_rb7.setOnClickListener(new View.OnClickListener() {
            //时间选择器 ,在rudiogroup 中只能单击一次,重新这个点击事件让日期控件出来

            @Override
            public void onClick(View view) {
                TimePickerView pvTime = new TimePickerBuilder(TimeOffActivity.this, new OnTimeSelectListener() {
                    @Override
                    public void onTimeSelect(Date date, View v) {
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
                        String format = simpleDateFormat.format(date);
                        speed_rb7.setText(format);

                        SimpleDateFormat simpleDateFormatHours = new SimpleDateFormat("HH");
                        int hours = Integer.valueOf(simpleDateFormatHours.format(date));

                        SimpleDateFormat simpleDateFormatMinutes = new SimpleDateFormat("mm");
                        int minutes = Integer.valueOf(simpleDateFormatMinutes.format(date));

                        countdownTime = ((hours * 60 + minutes)) * 60 * 1000;
                        eventType = "ok";
                        triggerAtTime = (int) (SystemClock.elapsedRealtime() + ((hours * 60 + minutes)) * 60 * 1000);
                        SPUtils.put(TimeOffActivity.this, "offtime", "自定义");

                    }
                }).setType(new boolean[]{false, false, false, true, true, false}).setLabel("年", "月", "日", "时", "分", "秒").build();

                pvTime.show();


            }
        });

    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KEYCODE_BACK)) {
            TimeOff();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    private void TimeOff() {

        //发送通知到baseactivity里开始倒计时发通知给播放界面显示
        CountdownTimerEvent timerEvent = new CountdownTimerEvent();
        timerEvent.setEventType(eventType);
        timerEvent.setObject(countdownTime);
        EventBus.getDefault().post(timerEvent);
        Share.d("秒数" + countdownTime);



        //广播有差异
//        AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//        Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
//        int requestCode = 0;
//        PendingIntent pendIntent = PendingIntent.getBroadcast(getApplicationContext(),
//                requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//
//        //先取消一下之前没执行的定时
//        alarmMgr.cancel(pendIntent);
//        // 5秒后发送广播,只发送一次
//        //triggerAtTime = (int) (SystemClock.elapsedRealtime() + 10 * 1000);
//        alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendIntent);



    }
}

四:倍速,我一定要说一说
你以为这样

  mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(1.5f));

一开始我也是这样,好多手机都没问题,前提得是6.0以后得啊,但是华为手机就不行,我想了很久,考虑到音频焦点,第二天来公司,打印发现跟音频焦点关系不大,前天晚上查阅了资料有两个细节:

MediaPlayer.setPlaybackParams(PlaybackParams params) throws IllegalStateException, IllegalArgumentException

接口说明:
(1) 使用这个接口可以进行播放速率的设置。
(2) 播放器prepared状态之前调用这个方法不会更改播放器的状态。
(3) prepared状态之后设置速率0等同于调用pause(),当调用start恢复播放以后,将以原来的速率进行播放。
(4) prepared状态之后设置非0的速率等同于调用start()。
(5) 当播放器还未初始化或者已经被释放的时候设置会抛IllegalStateException的异常。
(6) 当参数不支持的时候会抛IllegalArgumentException的异常。

设置时机要求:
合法的时机:Initialized, Prepared, Started, Paused, PlaybackCompleted, Error
非法的时机:Idle, Stopped

啥意思呢:主要是设置倍速相当于先pause,在start,抱着试试的心态,重新了这两个方法结果是OK的,如果直接设置大部分手机可以,但是华为手机会没音,这样设置相当于暂停又开始一下就可以了。具体写法:

          if (mediaplayer != null && instance.isplay(mediaplayer)) {

                    //倍速设置,必须在23以上
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        Share.d("aaaaa"+mediaplayer.isPlaying());
                        mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(1.5f));
                        mediaplayer.pause();
                        mediaplayer.start();
                    } else {
                        ToastUtils.showToast(getActivity(), "对不起请升级手机系统至Android6.0及以上");

                    }
                }

五:关闭播放器,这个两个方面,一个是服务,一个是单例持有,服务绑定两种方法,用户退出你是不是要销毁,销毁了单例还持有就报错,这个地方也是朋友商量的在退出的时候发通知到常驻服务里,stopmyself,在ondstory方法调用持有单例销毁播放器吗,这样就不内存溢出了

我说的不一定对,有指正的地方请留言,我及时修改,这个mediaplayer也不是长久之计

你可能感兴趣的:(Android mediaplayer的正确用法)