Android mediaplayer的正确用法

作者: jiangbin1992 | 来源:发表于2018-09-11 09:04 被阅读109次

    公司主要跟音频视频杠上了,视频还好说,有完美的第三方框架,傻瓜式的拉过来修改一下就能用,视频用的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<Void, Void, Void> {
    
            @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也不是长久之计

    相关文章

      网友评论

      • 痴迷_d6b5:那就是mediaplayer自带的倍速方法华为手机不能用呗
        jiangbin1992:@痴迷_d6b5 嗯 很好用 跟mediaplayer一样 具体逻辑自己封装 主要是他们底层做得好 有硬件加速什么的
        痴迷_d6b5:@jiangbin1992 那博主用过那个ijkPlayer试过倍速播放吗,这个我没了解过,求指教
        jiangbin1992:@痴迷_d6b5 是的 不能用 魅族也是 具体的是在想用话记录播放在那个位置然后更换倍速重置播放器直接定位到当前播放位置 当然 这个不太好 别的没办法
      • jiangbin1992:能用ijkplayer 还是用ijkplayer 这个问题还是有的 设置倍速有的手机还是不行 必须重置播放器
        case R.id.play_speed:
        //倍速
        // bottomIn();
        // intent = new Intent(getActivity(), SetSpeedActivity.class);
        // startActivity(intent);
        //获取本地设置的倍速
        if (mediaplayer != null && instance.isplay(mediaplayer)) {

        //倍速设置,必须在23以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2f));
        try {
        mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2.0f));
        } catch (Exception e) {
        e.printStackTrace();
        Log.e("Tag", "该设备暂不支持设置速率");
        HttpProxyCacheServer proxy = MyApplication.getProxy(getActivity());
        String proxyUrl = proxy.getProxyUrl(source);
        // initPlayer("noauto");
        int position = mediaplayer.getCurrentPosition();
        mediaplayer.reset();
        try {
        mediaplayer.setDataSource(getActivity(), Uri.parse(proxyUrl));
        mediaplayer.prepare();
        mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2.0f));
        mediaplayer.seekTo(position);
        mediaplayer.start();
        } catch (IOException e1) {
        e1.printStackTrace();
        }
        }
        } else {
        ToastUtils.showToast(getActivity(), "对不起请升级手机系统至Android6.0及以上");
        }
        }
        break;

      本文标题:Android mediaplayer的正确用法

      本文链接:https://www.haomeiwen.com/subject/mceygftx.html