MediaPlayer实现边播放边缓存

为了节省流量,安卓移动设备有需求:实现边播放边缓存.对于音乐重复播放的几率很大.有必要缓存下来.

现状是:MediaPlayer类只实现播放,和临时缓存数据的进度通知,但没有实现本地保存.

考虑了一下应该有几种方案;

  1. 重写MediaPlayer类,实现缓存自动通知或者上报通知给调用者保存. 需要修改底层源码
  2. 开2个线程,先下载一部分,立即通知mediaPlayer.setDataSource(path)播放; 同步继续下载,mediaPlayer继续播放,可能要重新定位播放位置
  3. 实现本地HTTP PROXY代理. 熟悉proxy和nginx原理,比较容易理解. 通过url生成本地代理后的url. 访问proxy,proxy模拟了http服务器提供数据流.内部又从外部请求数据,并保存到本地.后续请求可以直接从本地放回数据.

			if(path.startsWith("http")){
				path=proxy.getProxyUrl(path);
			}
			mediaPlayer.setDataSource(path);

目前来看,对MediaPlayer侵入最少的.还是PROXY的方式.  本来打算自己实现的.发现网上已经有几个实现的方案了

其中金山云Android HTTPCache SDK 提供的方案使用非常简单省事.

https://segmentfault.com/a/1190000007273843

https://github.com/ksvc/ksyhttpcache_android/releases

使用前,项目加载其提供的jar,可以在Application启动时初始化

private void initHttpProxy(){
    	proxy=new KSYProxyService(getApplicationContext()) ;
    	proxy.setCacheRoot(getExternalCacheDir());
    	proxy.setMaxCacheSize(2048*1024*1024l);
    	proxy.registerLogEventListener(new OnLogEventListener() {
			
			@Override
			public void onLogEvent(String var1) {
				MyLog.d(TAG, var1);
			}
		});
    	proxy.startServer();
    }

 

金山云提供封装更全的播放器,也可以考虑直接使用:KSYMediaPlayer

金山云Android播放SDK

https://gitee.com/ksvc/KSYMediaPlayer_Android

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的实现: 首先,在 AndroidManifest.xml 文件中添加外部存储权限: ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ``` 然后,在 Activity 的布局文件中添加一个按钮和一个 SurfaceView: ```xml <Button android:id="@+id/select_file_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="选择文件" /> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 接下来,在 Activity 的 Java 文件中实现按钮点击和视频播放功能: ```java import android.Manifest; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.MediaController; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.io.IOException; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, MediaController.MediaPlayerControl, MediaPlayer.OnBufferingUpdateListener { private static final int REQUEST_EXTERNAL_STORAGE = 1; private static final String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private MediaPlayer mediaPlayer; private MediaController mediaController; private SurfaceHolder surfaceHolder; private String videoPath; private boolean bufferUpdating; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查外部存储权限 int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE); } // 获取 SurfaceView 和按钮 SurfaceView surfaceView = findViewById(R.id.surface_view); Button selectFileButton = findViewById(R.id.select_file_button); // 设置 SurfaceView 的 Holder 和 Callback surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); // 设置按钮的点击事件 selectFileButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 打开文件选择框 // 选择相应的音频文件,返回文件路径 videoPath = Environment.getExternalStorageDirectory() + "/video.mp4"; } }); } @Override protected void onPause() { super.onPause(); if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } } @Override protected void onDestroy() { super.onDestroy(); if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } } @Override public void surfaceCreated(SurfaceHolder holder) { // 初始化 MediaPlayer 和 MediaController mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnBufferingUpdateListener(this); mediaController = new MediaController(this); mediaController.setMediaPlayer(this); mediaController.setAnchorView(findViewById(R.id.surface_view)); try { mediaPlayer.setDataSource(videoPath); } catch (IOException e) { e.printStackTrace(); } // 设置 MediaPlayer 的 Surface 和 PrepareAsync mediaPlayer.setDisplay(holder); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mediaController.setEnabled(true); mediaPlayer.start(); } }); mediaPlayer.prepareAsync(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Do nothing } @Override public void surfaceDestroyed(SurfaceHolder holder) { // Do nothing } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { // 更新缓存状态 bufferUpdating = true; mediaController.show(); mediaController.setSecondaryProgress(percent * mediaPlayer.getDuration() / 100); bufferUpdating = false; } @Override public boolean onTouchEvent(MotionEvent event) { // 显示或隐藏 Controller mediaController.show(); return false; } @Override public void start() { mediaPlayer.start(); } @Override public void pause() { mediaPlayer.pause(); } @Override public int getDuration() { return mediaPlayer.getDuration(); } @Override public int getCurrentPosition() { return mediaPlayer.getCurrentPosition(); } @Override public void seekTo(int pos) { mediaPlayer.seekTo(pos); } @Override public boolean isPlaying() { return mediaPlayer.isPlaying(); } @Override public int getBufferPercentage() { return bufferUpdating ? 0 : 100; } @Override public boolean canPause() { return true; } @Override public boolean canSeekBackward() { return true; } @Override public boolean canSeekForward() { return true; } @Override public int getAudioSessionId() { return mediaPlayer.getAudioSessionId(); } } ``` 注意,在点击播放按钮时,应该先检查视频路径是否为空,以及 MediaPlayer 是否已经准备好了。如果没有准备好,应该等待 OnPreparedListener 回调再开始播放。同时,应该在 onPause() 和 onDestroy() 中分别暂停和释放 MediaPlayer,以免造成资源浪费和内存泄漏。此外,实现 MediaController.MediaPlayerControl 接口可以让 Controller 自动显示和隐藏,并且可以通过 Controller 控制视频的播放和暂停,以及拖动进度条跳转到指定时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值