基于ExoPlayer的音频播放器以及进度悬浮窗

简述

最近在使用EXOplayer做关于音频的开发,目标如下

  1. 通过service绑定activity,能在后台播放,同时,在退出activity之后,显示一个悬浮窗,悬浮窗能同步播放器的进度,点击则进入播放界面。

关于ExoPlayer

这个库是goole官方推出的,十分强大,根据项目需要我主要使用它来进行音频播放。

列举几篇有参考意义的参考文献

  1. 官方介绍

先来一张图片镇楼

使用步骤

  1. 需要创建DefaultDataSourceFactory

进度条监听

在常见的music软件中进度条的监听是必不可少的,在本项目中,使用的进度条是defaultTimeBar,UI的更新在android使用handler是必不可少的,在配上接口回调,相当的nice。

  public Runnable loadStatusRunable = new Runnable() {
        @Override
        public void run() {
            long durationUs = 0;
            int adGroupCount = 0;
            long currentWindowTimeBarOffsetMs = 0;
            Timeline currentTimeline = mSimpleExoPlayer.getCurrentTimeline();
            if (!currentTimeline.isEmpty()) {
                int currentWindowIndex = mSimpleExoPlayer.getCurrentWindowIndex();


                int firstWindowIndex = currentWindowIndex;
                int lastWindowIndex = currentWindowIndex;
                for (int i = firstWindowIndex; i <= lastWindowIndex; i++) {
                    if (i == currentWindowIndex) {
                        currentWindowTimeBarOffsetMs = C.usToMs(durationUs);
                    }
                    currentTimeline.getWindow(i, window);
                    if (window.durationUs == C.TIME_UNSET) {
                        break;
                    }
                    for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) {
                        currentTimeline.getPeriod(j, period);
                        int periodAdGroupCount = period.getAdGroupCount();
                        for (int adGroupIndex = 0; adGroupIndex < periodAdGroupCount; adGroupIndex++) {
                            long adGroupTimeInPeriodUs = period.getAdGroupTimeUs(adGroupIndex);
                            if (adGroupTimeInPeriodUs == C.TIME_END_OF_SOURCE) {
                                if (period.durationUs == C.TIME_UNSET) {
                                    continue;
                                }
                                adGroupTimeInPeriodUs = period.durationUs;
                            }
                            long adGroupTimeInWindowUs = adGroupTimeInPeriodUs + period.getPositionInWindowUs();
                            if (adGroupTimeInWindowUs >= 0 && adGroupTimeInWindowUs <= window.durationUs) {
                                if (adGroupCount == adGroupTimesMs.length) {
                                    int newLength = adGroupTimesMs.length == 0 ? 1 : adGroupTimesMs.length * 2;
                                    adGroupTimesMs = Arrays.copyOf(adGroupTimesMs, newLength);
                                    playedAdGroups = Arrays.copyOf(playedAdGroups, newLength);
                                }
                                adGroupTimesMs[adGroupCount] = C.usToMs(durationUs + adGroupTimeInWindowUs);
                                playedAdGroups[adGroupCount] = period.hasPlayedAdGroup(adGroupIndex);
                                adGroupCount++;
                            }
                        }
                    }
                    durationUs += window.durationUs;
                }
            }

            durationUs = C.usToMs(window.durationUs);
            long curtime = currentWindowTimeBarOffsetMs + mSimpleExoPlayer.getContentPosition();
            long bufferedPosition = currentWindowTimeBarOffsetMs + mSimpleExoPlayer.getContentBufferedPosition();

            if (mediaControlListener != null) {
                mediaControlListener.setCurTimeString("" + Util.getStringForTime(formatBuilder, formatter, curtime));
                //  > 1000 ? durationUs - 1000 : durationUs
                mediaControlListener.setDurationTimeString("" + Util.getStringForTime(formatBuilder, formatter, durationUs));
                mediaControlListener.setBufferedPositionTime(bufferedPosition);
                mediaControlListener.setCurPositionTime(curtime);
                mediaControlListener.setDurationTime(durationUs);
            }

            mHandler.removeCallbacks(loadStatusRunable);
            int playbackState = mSimpleExoPlayer == null ? Player.STATE_IDLE : mSimpleExoPlayer.getPlaybackState();

            // 播放器未开始播放后者播放器播放结束
            if (playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED) {
                long delayMs = 0;
                // 当正在播放状态时
                if (mSimpleExoPlayer.getPlayWhenReady() && playbackState == Player.STATE_READY) {
                    float playBackSpeed = mSimpleExoPlayer.getPlaybackParameters().speed;
                    if (playBackSpeed <= 0.1f) {
                        delayMs = 1000;
                    } else if (playBackSpeed <= 5f) {
                        // 中间更新周期时间
                        long mediaTimeUpdatePeriodMs = 1000 / Math.max(1, Math.round(1 / playBackSpeed));
                        // 当前进度时间与中间更新周期之间的多出的不足一个中间更新周期时长的时间
                        long surplusTimeMs = curtime % mediaTimeUpdatePeriodMs;
                        // 播放延迟时间
                        long mediaTimeDelayMs = mediaTimeUpdatePeriodMs - surplusTimeMs;
                        if (mediaTimeDelayMs < (mediaTimeUpdatePeriodMs / 5)) {
                            mediaTimeDelayMs += mediaTimeUpdatePeriodMs;
                        }
                        delayMs = playBackSpeed == 1 ? mediaTimeDelayMs : (long) (mediaTimeDelayMs / playBackSpeed);
                    } else {
                        delayMs = 200;
                    }
                } else {
                    // 当暂停状态时
                    delayMs = 1000;
                }
                mHandler.postDelayed(this, delayMs);
            }
        }
    };

其次是悬浮窗的设置,悬浮窗是属于window层的,所以是需要权限的,在这里感谢前辈们的开源代码,这是传送门

我将music的管理以及悬浮窗的管理都放在了service中,这里就涉及到了service的启动。

service

service的启动有两种方式,一种是startService ,另外一种是bundService .

  1. 绑定开启bindService,使用unbindService解绑关闭。
    bindServic和unbindService一一对应,一个绑定开启,一个解绑结束。

这是一篇关于service声明周期的详细介绍

关于service,要说的就很多了,这里就不岔开话题了。

package com.haodong.musicplayer.myplayer;

import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ui.TimeBar;
import com.haodong.musicplayer.R;
import com.haodong.musicplayer.permission.AVCallFloatView;
import com.haodong.musicplayer.permission.FloatWindowManager;


import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import androidx.annotation.Nullable;


/**
 * created by linghaoDo on 2019-08-25
 * <p>
 * description:
 */
public class ExoPlayerService extends Service implements FloatWindowManager.OnWindowLis {
    private static final String TAG = "lhl-->ExoPlayerService";
    private ImageView ivCover;
    private CircleTimeBar timeBar;
    private ImageView ivClose;
    private boolean isWindowDismiss = true;
    private WindowManager windowManager = null;
    private WindowManager.LayoutParams mParams = null;
    private AVCallFloatView floatView = null;
    private OnProgressLis onProgressLis;
    private String mCoverUrl;
    private boolean isListenerIniteed = false;

    public boolean isWindowDismiss() {
        return isWindowDismiss;
    }

    private static final String ACTIVITY_NAME="com.maxwon.mobile.module.business.activities.knowledge.KnowledgeMusicActivity";

    public OnProgressLis getOnProgressLis() {
        return onProgressLis;
    }

    @Subscribe(threadMode = ThreadMode.ASYNC, sticky = true, priority = 8)
    public ExoPlayerService setOnProgressLis(OnProgressLis onProgressLis) {
        this.onProgressLis = onProgressLis;
        LogUtil.i("setOnProgressLis()");
        return this;
    }
    public class MusicBinder extends Binder {
        public ExoPlayerService getService() {
            return ExoPlayerService.this;
        }
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.i("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onCreate() {
        EventBus.getDefault().register(this);
        LogUtil.i("onCreate");
        super.onCreate();
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MusicBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    public void initListener() {
        ExoPlayerManager.getDefault().addMediaListener(new ExoPlayerManager.MediaControlListener() {
            @Override
            public void setCurPositionTime(long curPositionTime) {
                if (timeBar != null)
                    timeBar.setPosition(curPositionTime);
                if (onProgressLis != null) {
                    onProgressLis.onPositionChanged(curPositionTime);
                }
            }

            @Override
            public void setDurationTime(long durationTime) {
                if (timeBar != null)
                    timeBar.setDuration(durationTime);
                if (onProgressLis != null)
                    onProgressLis.onDurationChanged(durationTime);
            }

            @Override
            public void setBufferedPositionTime(long bufferedPosition) {
                if (onProgressLis != null) {
                    onProgressLis.onBufferedPositionChanged(bufferedPosition);
                }

            }

            @Override
            public void setCurTimeString(String curTimeString) {
                if (onProgressLis != null) {
                    onProgressLis.onCurTimeStringChanged(curTimeString);
                }
            }

            @Override
            public void setDurationTimeString(String durationTimeString) {
                if (onProgressLis != null) {
                    onProgressLis.onDurationTimeStringChanged(durationTimeString);
                }
            }
        });
        ExoPlayerManager.getDefault().addListener(new Player.EventListener() {
            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                Log.i(TAG,  "playWhenReady-->" + playWhenReady + "playbackState-->" + playbackState);
                if (onProgressLis != null) {
                    onProgressLis.onPlayerStateChanged(playWhenReady, playbackState);
                }
            }

            @Override
            public void onLoadingChanged(boolean isLoading) {
                if (isLoading) {
                    ExoPlayerManager.getDefault().startListenProgress();
                }
            }
        });
    }


    @Subscribe(sticky = true)
    public void doStart(DoStartEvent startEvent) {
        mCoverUrl = startEvent.getImgUrl();
        if (!isListenerIniteed)
            initListener();
        ExoPlayerManager.getDefault().startRadio(startEvent.getMusicUrl());
    }

    public void show() {
        LogUtil.i("show");
        initFloatingWindow();
    }

    public interface OnProgressLis {
        void onPositionChanged(long curPositionTime);

        void onDurationChanged(long durationTime);

        void onBufferedPositionChanged(long bufferedPosition);

        void onCurTimeStringChanged(String curTimeString);

        void onDurationTimeStringChanged(String durationTimeString);

        void onPlayerStateChanged(boolean playWhenReady, int playbackState);
    }


    @Override
    public void onDestroy() {
        LogUtil.i("music service destoryed");
        EventBus.getDefault().unregister(this);
        ExoPlayerManager.getDefault().releasePlayer();
        super.onDestroy();
    }

    private void initFloatingWindow() {
        if (!isWindowDismiss) {
            Log.e(TAG, "view is already added here");
            return;
        }
        isWindowDismiss = false;
        if (windowManager == null) {
            windowManager = (WindowManager) this.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        }

        Point size = new Point();
        windowManager.getDefaultDisplay().getSize(size);
        int screenWidth = size.x;
        int screenHeight = size.y;

        mParams = new WindowManager.LayoutParams();
        mParams.packageName = this.getPackageName();
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        int mType;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mType = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            mType = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
        }
        mParams.type = mType;
        mParams.format = PixelFormat.RGBA_8888;
        mParams.gravity = Gravity.LEFT | Gravity.TOP;
        mParams.x = screenWidth - dp2px(this, 100);
        mParams.y = screenHeight - dp2px(this, 171);
        floatView = new AVCallFloatView(this.getApplicationContext());
        floatView.setParams(mParams);
        floatView.setIsShowing(true);
        floatView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = null;
                try {
                    intent = new Intent(ExoPlayerService.this.getApplicationContext(),Class.forName(ACTIVITY_NAME));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }

                PendingIntent pendingIntent =
                        PendingIntent.getActivity(ExoPlayerService.this.getApplicationContext(), 0, intent, 0);
                try {
                    pendingIntent.send();
                } catch (PendingIntent.CanceledException e) {
                    e.printStackTrace();
                }
            }
        });
        /*init*/
        ivClose = floatView.findViewById(R.id.iv_stop);
        ivClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ExoPlayerManager.getDefault().pauseRadio();
                dismissWindow();
            }
        });
        ivCover = floatView.findViewById(R.id.iv_cover);
        timeBar = floatView.findViewById(R.id.circle_time_bar);
        timeBar.addListener(new TimeBar.OnScrubListener() {
            @Override
            public void onScrubStart(TimeBar timeBar, long position) {
            }

            @Override
            public void onScrubMove(TimeBar timeBar, long position) {
                ExoPlayerManager.getDefault().seekToTimeBarPosition(position);
            }

            @Override
            public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {

            }
        });
        windowManager.addView(floatView, mParams);
        initListener();
    }

    @Override
    public void showWindow() {
        show();
    }

    @Override
    public void dismissWindow() {
        if (isWindowDismiss) {
            Log.e(TAG, "window can not be dismiss cause it has not been added");
            return;
        }
        isWindowDismiss = true;
        floatView.setIsShowing(false);
        if (windowManager != null && floatView != null) {
            windowManager.removeViewImmediate(floatView);
        }
    }


    private int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

}

这里面提供了两个思路:

  1. 静态绑定service,可以通过EventBus来与activity进行交互。
  2. 动态绑定service,在activity中拿到service的引用,以此来与activity进行交互,当然,还有诸如广播之类的方法等鞥,在这里我就不一一列举啦

我在这里就用bindService 举例

package com.haodong.musicplayer;

import androidx.appcompat.app.AppCompatActivity;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.exoplayer2.ui.DefaultTimeBar;
import com.google.android.exoplayer2.ui.TimeBar;
import com.google.android.exoplayer2.util.Util;
import com.haodong.musicplayer.event.MusicShowWindowEvent;
import com.haodong.musicplayer.myplayer.Chapter;
import com.haodong.musicplayer.myplayer.ExoPlayerManager;
import com.haodong.musicplayer.myplayer.ExoPlayerService;
import com.haodong.musicplayer.myplayer.LogUtil;
import com.haodong.musicplayer.permission.FloatWindowManager;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements ExoPlayerService.OnProgressLis{
    @BindView(R.id.iv_music_previous)
    ImageView ivPrevious;
    @BindView(R.id.iv_music_next)
    ImageView ivNext;
    @BindView(R.id.tv_start_time)
    TextView tvStartTime;
    @BindView(R.id.tv_end_time)
    TextView tvEndTime;
    @BindView(R.id.iv_cover)
    ImageView ivCover;
    @BindView(R.id.iv_audio_switch)
    ImageView ivStop;
    @BindView(R.id.exo_progress)
    DefaultTimeBar timeBar;
    private List<Chapter> chapterList = new ArrayList<>();
    @BindView(R.id.btn_show_floating)
    Button btnFloating;
    Unbinder unbinder;
    private ExoPlayerService.MusicBinder myBinder;
    private ExoPlayerService mService;

    private Intent serviceIntent;
    private ServiceConnection serviceConnection;

    @OnClick({R.id.iv_music_previous, R.id.iv_music_next, R.id.iv_audio_switch, R.id.btn_show_floating})
    void onBtnClick(View v) {
        final int id = v.getId();
        if (id == R.id.iv_music_next) {

        } else if (id == R.id.iv_music_previous) {


        } else if (id == R.id.iv_audio_switch) {
            if (!ExoPlayerManager.getDefault().isPaused()){
                ivStop.setImageResource(R.mipmap.ic_knowledge_audio_suspended);
                ExoPlayerManager.getDefault().pauseRadio();
            }else {
                ivStop.setImageResource(R.mipmap.ic_knowledge_audio_play);
                ExoPlayerManager.getDefault().resumeRadio();
            }
        } else if (id == R.id.btn_show_floating) {
            LogUtil.i();
            if (FloatWindowManager.getInstance().applyOrShowFloatWindow(this)) {
                EventBus.getDefault().postSticky(new MusicShowWindowEvent());
            }
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        unbinder= ButterKnife.bind(this);
        initWidget();
    }
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }
    @Override
    public void onBackPressed() {
        /*这是重点*/
        moveTaskToBack(true);
    }

    private void initWidget() {
        bindService();

        timeBar.addListener(new TimeBar.OnScrubListener() {
            @Override
            public void onScrubStart(TimeBar timeBar, long position) {
                tvStartTime.setText(Util.getStringForTime(ExoPlayerManager.getDefault().getFormatBuilder()
                        , ExoPlayerManager.getDefault().getFormatter(), position));
            }

            @Override
            public void onScrubMove(TimeBar timeBar, long position) {
                ExoPlayerManager.getDefault().seekToTimeBarPosition(position);
            }

            @Override
            public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {

            }
        });


    }
    private void bindService() {
        LogUtil.i();
        serviceIntent = new Intent(MainActivity.this, ExoPlayerService.class);
        if(serviceConnection == null) {
            serviceConnection = new ServiceConnection() {

                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    mService= ((ExoPlayerService.MusicBinder)service).getService();
                    String uri = "https://storage.googleapis.com/exoplayer-test-media-0/play.mp3";
                    mService.setOnProgressLis(MainActivity.this).initListener();
                    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            /*模仿网络请求*/
                            String uri = "https://storage.googleapis.com/exoplayer-test-media-0/play.mp3";
                            ExoPlayerManager.getDefault().startRadio(uri);
                        }
                    },1000);
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {

                }
            };
            bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE);
        }
    }
    private void unbindService() {
        if(null != serviceConnection) {
            unbindService(serviceConnection);
            serviceConnection = null;
        }
    }

    @Override
    protected void onDestroy() {
        unbindService();
        unbinder.unbind();
        super.onDestroy();
    }

    @Override
    public void onPositionChanged(long curPositionTime) {
        if (timeBar != null)
            timeBar.setPosition(curPositionTime);
    }

    @Override
    public void onDurationChanged(long durationTime) {
        if (timeBar != null)
            timeBar.setDuration(durationTime);
    }

    @Override
    public void onBufferedPositionChanged(long bufferedPosition) {
        if (timeBar != null)
            timeBar.setBufferedPosition(bufferedPosition);
    }

    @Override
    public void onCurTimeStringChanged(String curTimeString) {
        if (tvStartTime != null) {
            tvStartTime.setText(curTimeString);
        }

    }

    @Override
    public void onDurationTimeStringChanged(String durationTimeString) {
        if (tvEndTime != null) {
            tvEndTime.setText(durationTimeString);
        }

    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

    }
}

为了方便activity的管理,我将activity的启动模式设为singleInstance

设置为singleInstance之后,还是有很多坑的,首先,在退出activity的时候,为了不退扎,我们需要屏蔽掉onBackPresee;



    @Override
    public void onBackPressed() {
        if (FloatWindowManager.getInstance().applyOrShowFloatWindow(this) && !ExoPlayerManager.getDefault().isStoped()) {
            EventBus.getDefault().postSticky(new ShowWindowEvent());
        } else if (!FloatWindowManager.getInstance().getService().isWindowDismiss()) {
            FloatWindowManager.getInstance().getService().dismissWindow();
        }
        moveTaskToBack(true);
    }

其次,再进入activity的时候,如果是使用startActivity等等,因为activity没有退栈,所以要走onNewIntent,不会再走onCreate()啦,如果你不想你的getIntent调用的东西的一样的,请务必要使用setIntent(intent);重置一下intent.

 @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        checkComeFrom();
    }

最后,我把所有资源都放在github啦,有需要的可以看看,欢迎star,当然,在我的资源里还有很多不完善的地方,需要大家去踩,(代码资源是足够大家去扩展的)毕竟没有踩过坑不知道坑有多深,只有大家动手写写,印象才够深刻。

写在最后

发现要写好一篇博客还是很难的,真的是字数不够代码来凑,感谢那些乐于分享的大佬,才有了我们这些低级码农的快速成长。也希望自己以后能分享出更多的干货。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个灵活的视频播放器。 MediaPlayer与VideoView完全分开,可以替换为其他播放器内核,如ExoPlayer和ijkPlayer。 可以完全自定义播放器视图,我们称之为控制面板。 此外,可以使用MediaPlayerManager来控制播放行为,例如全屏模式,小屏幕模式以及RecyclerView中的智能匹配模式。Features全屏,小屏播放内部支持RecyclerView中播放自定义UIAPP内全局播放静音循环播放手势操作(小窗:单指拖动,双指缩放;全屏:音量,亮度,快进)ijkPlayer支持ExoPlayer支持重力感应支持PreviewDownloadDemo DownloadGetting startedbuild.gradledependencies {     // required     implementation 'org.salient.artvideoplayer:artplayer-java:0.6.0'     // Default control panel: optional     implementation 'org.salient.artvideoplayer:artplayer-ui:0.6.0'      //ijkPlayer: optional      implementation 'org.salient.artvideoplayer:artplayer-ijk:0.6.0'      implementation "org.salient.artvideoplayer:artplayer-armv7a:0.6.0"       //Other ABIs: optional      implementation "org.salient.artvideoplayer:artplayer-armv5:0.6.0"      implementation "org.salient.artvideoplayer:artplayer-x86:0.6.0"      // Other ABIs: optional (minSdk version >= 21)      implementation "org.salient.artvideoplayer:artplayer-arm64:0.6.0"      implementation "org.salient.artvideoplayer:artplayer-x86_64:0.6.0"      //ExoPlayer2 : optional      implementation "org.salient.artvideoplayer:artplayer-exo:0.6.0" }Usagejavaimport org.salient.artplayer.VideoView;VideoView videoView = new VideoView(this); videoView.setUp("http://vfx.mtime.cn/Video/2018/06/27/mp4/180627094726195356.mp4"); videoView.setControlPanel(new ControlPanel(this)); videoView.start();xmlAndroidManifest.xml <!-- required -->Activity@Overridepublic void onBackPressed() {  if (MediaPlayerManager.instance().backPress(this)) {      return;   }  super.onBackPressed(); }@Overrideprotected void onPause() {  super.onPause();  MediaPlayerManager.instance().pause(); }@Overrideprotected void onDestroy() {  super.onDestroy();  MediaPlayerMa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值