增加缓存功能,优化用户体验;增加重试view

This commit is contained in:
litingting 2024-10-08 18:12:09 +08:00
parent 72bf148ad5
commit 5b390f4bff
17 changed files with 277 additions and 74 deletions

View File

@ -15,6 +15,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.hi.music.player.MusicApplication; import com.hi.music.player.MusicApplication;
import com.hi.music.player.R; import com.hi.music.player.R;
import com.hi.music.player.api.MediaControllerListener;
import com.hi.music.player.databinding.ItemPlayListBinding; import com.hi.music.player.databinding.ItemPlayListBinding;
import com.hi.music.player.helper.CommonUtils; import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayListInfo; import com.hi.music.player.javabean.response.ResponsePlayListInfo;
@ -26,7 +27,7 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
private String lastVideId; private String lastVideId;
private int curMusicPos = 0; // private int curMusicPos = 0;
private MyMediaControllerManager instance = MyMediaControllerManager.getInstance(); private MyMediaControllerManager instance = MyMediaControllerManager.getInstance();
@Override @Override
@ -48,8 +49,25 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
} }
} }
public void updateCurPlayStatus(String curVideId) {
lastVideId = mCurVideId;
this.mCurVideId = curVideId;
for (int i = 0; i < data.size(); i++) {
ResponsePlayListInfo listInfo = data.get(i);
if (listInfo.getVideoId().equals(curVideId)) {
notifyItemChanged(i);
}
if (listInfo.getVideoId().equals(lastVideId)) {
notifyItemChanged(i);
}
}
}
public void updateCurMusicAnimation(){ public void updateCurMusicAnimation(){
notifyItemChanged(curMusicPos); int curIndex = instance.getCurIndex();
CommonUtils.LogMsg("--curIndex="+curIndex);
notifyItemChanged(curIndex);
} }
@Override @Override
@ -69,18 +87,19 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
MediaItem currentMediaItem = instance.getMediaController().getCurrentMediaItem(); MediaItem currentMediaItem = instance.getMediaController().getCurrentMediaItem();
if (currentMediaItem != null && currentMediaItem.mediaId.equals(listInfo.getVideoId())) { if (currentMediaItem != null && currentMediaItem.mediaId.equals(listInfo.getVideoId())) {
vb.viewPlaying.setVisibility(View.VISIBLE); vb.viewPlaying.setVisibility(View.VISIBLE);
vb.getRoot().setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music)); vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music));
if (instance.getMediaController().isPlaying()) { if (instance.getMediaController().isPlaying()) {
vb.viewPlaying.startAnimating(); vb.viewPlaying.startAnimating();
lastVideId = listInfo.getVideoId(); lastVideId = listInfo.getVideoId();
CommonUtils.LogMsg("-------playAnimation"); CommonUtils.LogMsg("-------playAnimation "+itemHolder.getAbsoluteAdapterPosition());
} else { } else {
vb.viewPlaying.pauseAnimating(); vb.viewPlaying.pauseAnimating();
CommonUtils.LogMsg("-------pauseAnimation"); CommonUtils.LogMsg("-------pauseAnimation "+itemHolder.getAbsoluteAdapterPosition());
} }
} else { } else {
vb.viewPlaying.setVisibility(View.GONE); vb.viewPlaying.setVisibility(View.GONE);
// CommonUtils.LogMsg("-------GONE"); vb.itemLayout.setBackgroundColor(CommonUtils.getMyColor(R.color.color_transparent));
} }
vb.itemLayout.setOnClickListener(new View.OnClickListener() { vb.itemLayout.setOnClickListener(new View.OnClickListener() {

View File

@ -35,6 +35,13 @@ abstract public class BaseAdapter<K, T extends ViewBinding> extends RecyclerView
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void setData(List<K> data) {
this.data.clear();
this.data.addAll(data);
notifyDataSetChanged();
}
public void addLoadingFooter() { public void addLoadingFooter() {
isLoadingAdded = true; isLoadingAdded = true;
// notifyItemInserted(data.size()); // notifyItemInserted(data.size());

View File

@ -0,0 +1,5 @@
package com.hi.music.player.api;
public interface OnHasUrlAction {
void onHasUrl();
}

View File

@ -11,6 +11,8 @@ import android.view.animation.LinearInterpolator;
import com.hi.music.player.helper.CommonUtils; import com.hi.music.player.helper.CommonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
public class AnimaPlayingView extends View { public class AnimaPlayingView extends View {
@ -31,6 +33,7 @@ public class AnimaPlayingView extends View {
private Random random; private Random random;
private ValueAnimator animator; private ValueAnimator animator;
private List<ValueAnimator> valueAnimatorList;
public AnimaPlayingView(Context context) { public AnimaPlayingView(Context context) {
super(context); super(context);
@ -48,6 +51,7 @@ public class AnimaPlayingView extends View {
} }
private void init() { private void init() {
valueAnimatorList = new ArrayList<>();
paint = new Paint(); paint = new Paint();
paint.setColor(barColor); paint.setColor(barColor);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
@ -58,6 +62,7 @@ public class AnimaPlayingView extends View {
for (int i = 0; i < numBars; i++) { for (int i = 0; i < numBars; i++) {
barHeights[i] = 0; barHeights[i] = 0;
} }
initAnimating();
} }
@ -84,9 +89,14 @@ public class AnimaPlayingView extends View {
} }
} }
// 启动固定动画使音量条的高度变化
public void startAnimating() { public void startAnimating() {
for (ValueAnimator valueAnimator : valueAnimatorList) {
valueAnimator.start();
}
}
// 启动固定动画使音量条的高度变化
public void initAnimating() {
for (int i = 0; i < numBars; i++) { for (int i = 0; i < numBars; i++) {
animator = ValueAnimator.ofFloat(0, 1); animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000); // 每0.5秒更新一次 animator.setDuration(1000); // 每0.5秒更新一次
@ -104,16 +114,16 @@ public class AnimaPlayingView extends View {
invalidate(); // 重绘视图 invalidate(); // 重绘视图
} }
}); });
valueAnimatorList.add(animator);
animator.setStartDelay(i * 200L); animator.setStartDelay(i * 200L);
animator.start();
} }
} }
public void pauseAnimating() { public void pauseAnimating() {
if(animator!= null){ for (ValueAnimator valueAnimator : valueAnimatorList) {
animator.pause(); valueAnimator.pause();
} }
for (int i = 0; i < numBars; i++) { for (int i = 0; i < numBars; i++) {
barHeights[i] = minHeight; barHeights[i] = minHeight;
@ -122,7 +132,6 @@ public class AnimaPlayingView extends View {
} }
// 随机生成音量条的高度 // 随机生成音量条的高度
private float randomHeight() { private float randomHeight() {
return minHeight + random.nextFloat() * (maxHeight - minHeight); return minHeight + random.nextFloat() * (maxHeight - minHeight);

View File

@ -31,7 +31,7 @@ public class CommonUtils {
private static String TAG = "----MUSIC---------"; private static String TAG = "----MUSIC---------";
private static String TAG_ERROR = "----ERROR MUSIC---------"; private static String TAG_ERROR = "----MUSIC---------ERROR--";
public static void LogMsg(String msg) { public static void LogMsg(String msg) {
Log.d(TAG, msg); Log.d(TAG, msg);
@ -191,7 +191,6 @@ public class CommonUtils {
// 3. 检查 URI 是否具有正确的格式 // 3. 检查 URI 是否具有正确的格式
try { try {
if (parsedUri.getScheme() == null) { if (parsedUri.getScheme() == null) {
LogMsg( "URI has no valid scheme.");
return false; return false;
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -7,8 +7,17 @@ public class CustomerUrlInfo {
private ResponsePlayUrl playUrl; private ResponsePlayUrl playUrl;
private int playMusicIndex; private int playMusicIndex;
private boolean needPlayNow; private boolean needPlayNow;
private String videoId;
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public ResponsePlayUrl getPlayUrl() { public ResponsePlayUrl getPlayUrl() {
return playUrl; return playUrl;
} }

View File

@ -6,8 +6,12 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.DefaultDataSourceFactory;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.ResolvingDataSource; import androidx.media3.datasource.ResolvingDataSource;
import androidx.media3.datasource.cache.CacheDataSource; import androidx.media3.datasource.cache.CacheDataSource;
import androidx.media3.datasource.cache.SimpleCache;
import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider; import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
@ -16,6 +20,7 @@ import androidx.media3.exoplayer.source.MediaSourceFactory;
import androidx.media3.exoplayer.source.ProgressiveMediaSource; import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.api.RequestListener; import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils; import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayUrl; import com.hi.music.player.javabean.response.ResponsePlayUrl;
@ -32,16 +37,18 @@ import okhttp3.ResponseBody;
public class DynamicMediaSourceFactory implements MediaSource.Factory { public class DynamicMediaSourceFactory implements MediaSource.Factory {
// private final CacheDataSource.Factory cacheDataSourceFactory; // private final CacheDataSource.Factory cacheDataSourceFactory;
// private final ExoPlayer player; // private final ExoPlayer player;
private DefaultMediaSourceFactory mediaSourceFactory; private DataSource.Factory mediaSourceFactory;
private SimpleCache simpleCache;
// public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) { // public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) {
// this.cacheDataSourceFactory = cacheDataSourceFactory; // this.cacheDataSourceFactory = cacheDataSourceFactory;
// this.player = exoPlayer; // this.player = exoPlayer;
// this.mediaSourceFactory = factory; // this.mediaSourceFactory = factory;
// } // }
public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory) { public DynamicMediaSourceFactory(DataSource.Factory factory, SimpleCache cache) {
this.mediaSourceFactory = factory; this.mediaSourceFactory = factory;
this.simpleCache = cache;
} }
@ -105,22 +112,40 @@ public class DynamicMediaSourceFactory implements MediaSource.Factory {
// } // }
// }); // });
// DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
// 这里的DefaultDataSource同时支持本地和HTTP请求的资源自动实现检测 The DefaultDataSource supports both local and Http sources. It automatically detects which one to use.
//实现缓存
CacheDataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory()
.setCache(simpleCache)
.setUpstreamDataSourceFactory(mediaSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
MediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(mediaItem);
return mediaSource;
// return new ProgressiveMediaSource.Factory(cacheDataSourceFactory) // return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(responsePlayUrl.getAudioUrlMedium())); // .createMediaSource(MediaItem.fromUri(responsePlayUrl.getAudioUrlMedium()));
// 可以在这里根据 mediaItem ID 动态生成新的 URI // 可以在这里根据 mediaItem ID 动态生成新的 URI
String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D"; // String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D";
Uri updateUri = null; // Uri updateUri = null;
//
// MediaItem.Builder builder = mediaItem.buildUpon();
// if (mediaItem.localConfiguration != null) {
// updateUri = mediaItem.localConfiguration.uri;
// builder.setUri(updateUri);
// CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
// }else {
// CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
// }
MediaItem.Builder builder = mediaItem.buildUpon(); // return mediaSourceFactory.createMediaSource(builder.build());
if (mediaItem.localConfiguration != null) {
updateUri = mediaItem.localConfiguration.uri;
builder.setUri(updateUri);
CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
}else {
CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
}
return mediaSourceFactory.createMediaSource(builder.build());
} }

View File

@ -18,6 +18,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.hi.music.player.MusicApplication; import com.hi.music.player.MusicApplication;
import com.hi.music.player.api.MediaControllerListener; import com.hi.music.player.api.MediaControllerListener;
import com.hi.music.player.api.MediaControllerStatusListener; import com.hi.music.player.api.MediaControllerStatusListener;
import com.hi.music.player.api.OnHasUrlAction;
import com.hi.music.player.helper.CommonUtils; import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.helper.MyValue; import com.hi.music.player.helper.MyValue;
import com.hi.music.player.javabean.response.ResponsePlayListInfo; import com.hi.music.player.javabean.response.ResponsePlayListInfo;
@ -78,7 +79,6 @@ public class MyMediaControllerManager {
mediaController.addListener(new Player.Listener() { mediaController.addListener(new Player.Listener() {
@Override @Override
public void onPlayerError(PlaybackException error) { public void onPlayerError(PlaybackException error) {
CommonUtils.LogMsg("=-----PlaybackException+" + error.getMessage());
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_ERROR); mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_ERROR);
} }
@ -99,13 +99,22 @@ public class MyMediaControllerManager {
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING); mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING);
// TODO: 2024/9/26 自动播放完成切歌到下一首播放没有触发这里请求下一首 // TODO: 2024/9/26 自动播放完成切歌到下一首播放没有触发这里请求下一首
if (mediaController.hasNextMediaItem()) { if (mediaController.hasNextMediaItem()) {
MediaItem mediaItemAt = mediaController.getMediaItemAt(mediaController.getNextMediaItemIndex()); int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
if (!CommonUtils.hasValidUri(mediaItemAt)) { onCallRequestUrl(nextMediaItemIndex, false, new OnHasUrlAction() {
CommonUtils.LogMsg("----0000000000-请求下一首的uri" + mediaController.getMediaItemCount()); @Override
int nextIndex = mediaController.getNextMediaItemIndex(); public void onHasUrl() {
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextIndex).mediaId, nextIndex, false); CommonUtils.LogMsg("-------------有有效URl--播放检查下一首 位置="+nextMediaItemIndex );
} }
});
}
if (mediaController.hasPreviousMediaItem()) {
int previousMediaItemIndex = mediaController.getPreviousMediaItemIndex();
onCallRequestUrl(previousMediaItemIndex, false, new OnHasUrlAction() {
@Override
public void onHasUrl() {
CommonUtils.LogMsg("-------------有有效URl--播放检查上一首 位置="+previousMediaItemIndex );
}
});
} }
} else { } else {
@ -145,11 +154,15 @@ public class MyMediaControllerManager {
} }
// /** public int getCurIndex(){
// * 更新播放列表中的音频url return mediaController.getCurrentMediaItemIndex();
// * }
// * @param playUrl
// */ /**
* 更新播放列表中的音频url
*
* @param playUrl
*/
public void UpdateAudioUrl(ResponsePlayUrl playUrl, int index) { public void UpdateAudioUrl(ResponsePlayUrl playUrl, int index) {
CommonUtils.LogMsg("-------------更新播放列表中的音频url= index=" + index); CommonUtils.LogMsg("-------------更新播放列表中的音频url= index=" + index);
for (int i = 0; i < mediaController.getMediaItemCount(); i++) { for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
@ -285,15 +298,14 @@ public class MyMediaControllerManager {
public void playNext() { public void playNext() {
if (mediaController.hasNextMediaItem()) { if (mediaController.hasNextMediaItem()) {
int nextMediaItemIndex = mediaController.getNextMediaItemIndex(); int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
MediaItem mediaItemAt = mediaController.getMediaItemAt(nextMediaItemIndex);
boolean b = CommonUtils.hasValidUri(mediaItemAt);
if (b) {
mediaController.seekToNextMediaItem();
return;
}
CommonUtils.LogMsg("-------------有下一首但是歌曲url请求失败需要重新请求");
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextMediaItemIndex).mediaId, nextMediaItemIndex, true);
onCallRequestUrl(nextMediaItemIndex, true, new OnHasUrlAction() {
@Override
public void onHasUrl() {
mediaController.seekToNextMediaItem();
mediaController.play();
}
});
} else { } else {
// int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex(); // int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
// if (currentMediaItemIndex < playList.size() - 1) { // if (currentMediaItemIndex < playList.size() - 1) {
@ -307,7 +319,16 @@ public class MyMediaControllerManager {
public void playPrevious() { public void playPrevious() {
if (mediaController.hasPreviousMediaItem()) { if (mediaController.hasPreviousMediaItem()) {
int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
onCallRequestUrl(nextMediaItemIndex, true, new OnHasUrlAction() {
@Override
public void onHasUrl() {
mediaController.seekToPreviousMediaItem(); mediaController.seekToPreviousMediaItem();
mediaController.play();
}
});
} else { } else {
mediaController.seekTo(0); mediaController.seekTo(0);
mediaController.play(); mediaController.play();
@ -327,15 +348,25 @@ public class MyMediaControllerManager {
} }
stop(); stop();
mediaController.prepare(); mediaController.seekTo(index,0);
mediaController.seekTo(index); onCallRequestUrl(index, true, new OnHasUrlAction() {
@Override
public void onHasUrl() {
mediaController.play();
CommonUtils.LogMsg("-------------有有效URl--播放指定播放列表位置的歌曲" );
}
});
}
private void onCallRequestUrl(int index, boolean playNow, OnHasUrlAction action){
MediaItem mediaItemAt = mediaController.getMediaItemAt(index); MediaItem mediaItemAt = mediaController.getMediaItemAt(index);
boolean b = CommonUtils.hasValidUri(mediaItemAt); boolean b = CommonUtils.hasValidUri(mediaItemAt);
if (!b) { if (!b) {
CommonUtils.LogMsg("-------------播放指定播放列表位置的歌曲,需要重新请求"); CommonUtils.LogMsg("-------------请求URl index"+index+"---playNow="+playNow);
mListener.onRequestNextUri(mediaItemAt.mediaId, index, true); mListener.onRequestNextUri(mediaItemAt.mediaId, index, playNow);
} }else {
action.onHasUrl();
}
} }
} }

View File

@ -7,10 +7,13 @@ import androidx.annotation.Nullable;
import androidx.annotation.OptIn; import androidx.annotation.OptIn;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.database.DatabaseProvider;
import androidx.media3.database.ExoDatabaseProvider;
import androidx.media3.database.StandaloneDatabaseProvider; import androidx.media3.database.StandaloneDatabaseProvider;
import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.DefaultDataSource; import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.DefaultDataSourceFactory;
import androidx.media3.datasource.DefaultHttpDataSource; import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.ResolvingDataSource; import androidx.media3.datasource.ResolvingDataSource;
import androidx.media3.datasource.cache.CacheDataSource; import androidx.media3.datasource.cache.CacheDataSource;
@ -40,8 +43,21 @@ public class PlaybackService extends MediaSessionService {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// 1. 初始化缓存
File cacheDir = new File(getCacheDir(), "media_cache");
long maxCacheSize = 100 * 1024 * 1024; // 缓存大小 100MB
StandaloneDatabaseProvider databaseProvider = new StandaloneDatabaseProvider(this);
SimpleCache cache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(maxCacheSize), databaseProvider);
DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(new DefaultMediaSourceFactory(new DefaultHttpDataSource.Factory()));
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
// 这里的DefaultDataSource同时支持本地和HTTP请求的资源自动实现检测 The DefaultDataSource supports both local and Http sources. It automatically detects which one to use.
// DefaultDataSource.Factory defaultDataSourceFactory = new DefaultDataSourceFactory(MusicApplication.myApplication, httpDataSourceFactory);
DefaultDataSource.Factory upstreamFactory = new DefaultDataSource.Factory(this);
DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(upstreamFactory,cache);
// DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(new DefaultMediaSourceFactory(new DefaultHttpDataSource.Factory()),cache);
DefaultMediaSourceFactory defaultMediaSourceFactory1 = new DefaultMediaSourceFactory(getUrlFactory()); DefaultMediaSourceFactory defaultMediaSourceFactory1 = new DefaultMediaSourceFactory(getUrlFactory());
@ -53,8 +69,13 @@ public class PlaybackService extends MediaSessionService {
10000, // bufferForPlaybackMs: 播放时的目标缓冲时间毫秒 10000, // bufferForPlaybackMs: 播放时的目标缓冲时间毫秒
5000) // bufferForPlaybackAfterRebufferMs: 重新缓冲后的缓冲时间毫秒) 5000) // bufferForPlaybackAfterRebufferMs: 重新缓冲后的缓冲时间毫秒)
.build(); .build();
player = new ExoPlayer.Builder(this) player = new ExoPlayer.Builder(this)
// .setMediaSourceFactory(customMediaSourceFactory) .setMediaSourceFactory(customMediaSourceFactory)
// .setLoadControl(loadControl) // .setLoadControl(loadControl)
.build(); .build();
mediaSession = new MediaSession.Builder(this, player) mediaSession = new MediaSession.Builder(this, player)

View File

@ -26,7 +26,7 @@ public class ObserverWrapper<T> implements Observer<T> {
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
CommonUtils.LogMsg("----------onError"+e.getMessage()); CommonUtils.LogMsg("----------onError---"+e.getMessage());
requestListener.onFail(e.getMessage()); requestListener.onFail(e.getMessage());
} }

View File

@ -53,7 +53,7 @@ public class RetrofitManager {
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.addInterceptor(httpLoggingInterceptor) // .addInterceptor(httpLoggingInterceptor)
.build(); .build();
retrofit = new Retrofit.Builder() retrofit = new Retrofit.Builder()
.baseUrl(base_Host) .baseUrl(base_Host)

View File

@ -73,6 +73,12 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
private GradientDrawable gradientDrawable; private GradientDrawable gradientDrawable;
private int lighterColor, darkerColor; private int lighterColor, darkerColor;
// 0 播放列表请求失败 1 立即播放的歌曲请求失败
private int netError = 0;
// 请求失败的立即播放的歌曲信息
private CustomerUrlInfo mCustomerUrlInfo;
@Override @Override
protected ActivityPlayBinding getViewBinding() { protected ActivityPlayBinding getViewBinding() {
@ -94,7 +100,9 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@Override @Override
public void onChanged(List<ResponsePlayListInfo> playList) { public void onChanged(List<ResponsePlayListInfo> playList) {
if (playList == null) { if (playList == null) {
CommonUtils.LogMsg("---------playList = null"); CommonUtils.LogErrorMsg("---------playList = null");
netError = 0;
vb.linearRetry.setVisibility(View.VISIBLE);
return; return;
} }
if (playList.size() > 0) { if (playList.size() > 0) {
@ -111,7 +119,12 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
public void onChanged(CustomerUrlInfo customerUrlInfo) { public void onChanged(CustomerUrlInfo customerUrlInfo) {
if (customerUrlInfo.isNeedPlayNow() && customerUrlInfo.getPlayUrl() == null) { if (customerUrlInfo.isNeedPlayNow() && customerUrlInfo.getPlayUrl() == null) {
// TODO: 2024/9/26 需要马上播放这首歌曲但是此次网络请求失败 // TODO: 2024/9/26 需要马上播放这首歌曲但是此次网络请求失败
CommonUtils.LogMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败"); CommonUtils.LogErrorMsg("-------------需要马上播放这首歌曲,但是此次网络请求失败");
netError = 1;
mCustomerUrlInfo = customerUrlInfo;
initShowPlayList(false);
vb.linearRetry.setVisibility(View.VISIBLE);
mediaControllerManager.pause();
return; return;
} }
@ -119,13 +132,13 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
int second = customerUrlInfo.getPlayMusicIndex(); int second = customerUrlInfo.getPlayMusicIndex();
boolean needPlayNow = customerUrlInfo.isNeedPlayNow(); boolean needPlayNow = customerUrlInfo.isNeedPlayNow();
if (needPlayNow) { if (needPlayNow) {
mediaControllerManager.playPositionMusic(second);
mediaControllerManager.play(); mediaControllerManager.play();
} }
} }
}); });
} }
@OptIn(markerClass = UnstableApi.class) @OptIn(markerClass = UnstableApi.class)
private void initPlayerView() { private void initPlayerView() {
vb.playerView.setShowRewindButton(false); vb.playerView.setShowRewindButton(false);
@ -154,35 +167,45 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
switch (playStatus) { switch (playStatus) {
case Player.STATE_IDLE: case Player.STATE_IDLE:
CommonUtils.LogMsg("-------------playStatus=STATE_IDLE"); CommonUtils.LogMsg("-------------IDLE");
break; break;
case Player.STATE_BUFFERING: case Player.STATE_BUFFERING:
//快进没有缓冲的时候触发 //快进没有缓冲的时候触发
vb.btnPlay.setSelected(false); vb.btnPlay.setSelected(false);
vb.progressBarLoading.setVisibility(View.VISIBLE); vb.progressBarLoading.setVisibility(View.VISIBLE);
CommonUtils.LogMsg("-------------playStatus=STATE_BUFFERING"); CommonUtils.LogMsg("-------------缓冲");
break; break;
case Player.STATE_READY: case Player.STATE_READY:
vb.btnPlay.setSelected(true); vb.btnPlay.setSelected(true);
mHandler.post(mRunnable); mHandler.post(mRunnable);
vb.progressBarLoading.setVisibility(View.GONE); vb.progressBarLoading.setVisibility(View.GONE);
CommonUtils.LogMsg("-------------playStatus=STATE_READY"); CommonUtils.LogMsg("-------------准备");
break; break;
case Player.STATE_ENDED: case Player.STATE_ENDED:
//播放完成 //播放完成
vb.btnPlay.setSelected(false); vb.btnPlay.setSelected(false);
CommonUtils.LogMsg("-------------playStatus=STATE_ENDED 播放完成"); CommonUtils.LogMsg("------------- 播放完成");
mHandler.removeCallbacks(mRunnable); // 停止更新 mHandler.removeCallbacks(mRunnable); // 停止更新
updatePlayComplete(); updatePlayComplete();
mediaControllerManager.playNext(); mediaControllerManager.playNext();
break; break;
case MyValue.PLAY_STATUS_CODE_PAUSE: case MyValue.PLAY_STATUS_CODE_PAUSE:
CommonUtils.LogMsg("------------- 暂停");
vb.btnPlay.setSelected(false); vb.btnPlay.setSelected(false);
vb.layoutPlayList.imPlay.setSelected(false);
break; break;
case MyValue.PLAY_STATUS_CODE_PLAYING: case MyValue.PLAY_STATUS_CODE_PLAYING:
CommonUtils.LogMsg("------------- 播放ing getCurIndex=" + mediaControllerManager.getCurIndex());
vb.progressBarLoading.setVisibility(View.GONE);
vb.btnPlay.setSelected(true); vb.btnPlay.setSelected(true);
vb.layoutPlayList.imPlay.setSelected(true);
break;
case MyValue.PLAY_STATUS_CODE_ERROR:
vb.progressBarLoading.setVisibility(View.GONE);
int currentMediaItemIndex = mediaControllerManager.getMediaController().getCurrentMediaItemIndex();
CommonUtils.LogMsg("------------- 播放错误 currentMediaItemIndex=" + currentMediaItemIndex);
break; break;
} }
@ -190,7 +213,6 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@Override @Override
public void onRequestNextUri(String videoId, int playListIndex, boolean playNow) { public void onRequestNextUri(String videoId, int playListIndex, boolean playNow) {
CommonUtils.LogMsg("------------onRequestNextUri= videoId=" + videoId);
if (playNow) { if (playNow) {
vb.progressBarLoading.setVisibility(View.VISIBLE); vb.progressBarLoading.setVisibility(View.VISIBLE);
} }
@ -215,6 +237,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
vb.btnPrevious.setOnClickListener(this); vb.btnPrevious.setOnClickListener(this);
vb.imBack.setOnClickListener(this); vb.imBack.setOnClickListener(this);
vb.btnMusicList.setOnClickListener(this); vb.btnMusicList.setOnClickListener(this);
vb.tvRetry.setOnClickListener(this);
} }
@ -366,6 +389,16 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
if (adapterPlayList != null) { if (adapterPlayList != null) {
adapterPlayList.updateCurMusicAnimation(); adapterPlayList.updateCurMusicAnimation();
} }
} else if (v.equals(vb.tvRetry)) {
vb.linearRetry.setVisibility(View.GONE);
if (netError == 0) {
vmPlay.getPlayMusicList(responseSingle);
} else {
if (mCustomerUrlInfo != null) {
vmPlay.getPlayUrl(mCustomerUrlInfo.getVideoId(), mCustomerUrlInfo.getPlayMusicIndex(), true);
}
}
} }
} }
@ -387,7 +420,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
List<ResponsePlayListInfo> playList = mediaControllerManager.getPlayList(); List<ResponsePlayListInfo> playList = mediaControllerManager.getPlayList();
adapterPlayList = new AdapterPlayList(); adapterPlayList = new AdapterPlayList();
vb.layoutPlayList.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication)); vb.layoutPlayList.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication));
adapterPlayList.addData(playList); adapterPlayList.setData(playList);
vb.layoutPlayList.recyclerList.setAdapter(adapterPlayList); vb.layoutPlayList.recyclerList.setAdapter(adapterPlayList);
vb.layoutPlayList.imPlay.setOnClickListener(this); vb.layoutPlayList.imPlay.setOnClickListener(this);
initPlayList = true; initPlayList = true;

View File

@ -41,6 +41,7 @@ public class VMPlay extends ViewModel {
@Override @Override
public void onFail(String errorMsg) { public void onFail(String errorMsg) {
// TODO: 2024/10/8 播放列表拉取失败
_playList.setValue(null); _playList.setValue(null);
} }
@ -63,10 +64,12 @@ public class VMPlay extends ViewModel {
public void getPlayUrl(String videoId, int playListIndex,boolean playNow) { public void getPlayUrl(String videoId, int playListIndex,boolean playNow) {
CustomerUrlInfo customerUrlInfo = new CustomerUrlInfo(); CustomerUrlInfo customerUrlInfo = new CustomerUrlInfo();
customerUrlInfo.setNeedPlayNow(playNow); customerUrlInfo.setNeedPlayNow(playNow);
customerUrlInfo.setVideoId(videoId);
RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() { RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() {
@Override @Override
public void onFail(String errorMsg) { public void onFail(String errorMsg) {
CommonUtils.LogMsg("-------------此次网络请求失败 playNow="+playNow);
_playUrlMutableLiveData.setValue(customerUrlInfo); _playUrlMutableLiveData.setValue(customerUrlInfo);
} }

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"/>
<solid android:color="@color/cur_play_music"/>
</shape>

View File

@ -37,6 +37,39 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/im_back" /> app:layout_constraintTop_toBottomOf="@id/im_back" />
<LinearLayout
android:layout_width="140dp"
android:layout_height="100dp"
android:background="@drawable/bg_retry"
android:gravity="center"
android:orientation="vertical"
android:id="@+id/linear_retry"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/im_covert"
app:layout_constraintLeft_toLeftOf="@id/im_covert"
app:layout_constraintRight_toRightOf="@id/im_covert"
app:layout_constraintTop_toTopOf="@id/im_covert">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/An_error_occurred"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/tv_retry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@string/retry"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<androidx.media3.ui.PlayerView <androidx.media3.ui.PlayerView
android:id="@+id/player_view" android:id="@+id/player_view"
@ -183,6 +216,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone" android:indeterminateTint="@color/white"
android:indeterminateTint="@color/white" /> android:visibility="gone" />
</FrameLayout> </FrameLayout>

View File

@ -22,4 +22,6 @@
<string name="privacy_policy">Privacy Policy</string> <string name="privacy_policy">Privacy Policy</string>
<string name="terms_of_service">Terms of Service</string> <string name="terms_of_service">Terms of Service</string>
<string name="play_next">Play next</string> <string name="play_next">Play next</string>
<string name="retry">Retry</string>
<string name="An_error_occurred">An error occurred</string>
</resources> </resources>