android 随机播放代码,Android | 一个随机播放网络音乐的小 Demo
前言
是这样,前几天接触到一个可以随机获取网络音乐及其热评的 API(关于该API:github.com/isecret/yun… ),于是乎就想着要做一个小 demo 来练练手吧!
目前的效果就是上面那个样子。
我目前有打算把这个 demo 长期维护下去,后面会加入更多功能,例如收藏、下载等。
需求
需求很简单,就是通过 API 随机获取一首在线音乐及其某一条热评,实现音乐的后台播放、暂停、随机切换,显示热评及其点赞数。
API 返回的数据示例如下:
{
"song_id": 400162138,
"title": "海阔天空",
"images": "https://p1.music.126.net/a9oLdcFPhqQyuouJzG2mAQ==/3273246124149810.jpg",
"author": "Beyond",
"album": "华纳23周年纪念精选系列",
"description": "歌手:Beyond。所属专辑:华纳23周年纪念精选系列。",
"mp3_url": "https://api.comments.hk/music/400162138",
"pub_date": "2001-08-31 16:00:00",
"comment_id": 168923809,
"comment_user_id": 6942157,
"comment_nickname": "斑马斑斑",
"comment_avatar_url": "https://p1.music.126.net/O-z-71Ffl1VimPDElVDKcQ==/6057209557649645.jpg",
"comment_liked_count": 105599,
"comment_content": "如果家驹没走,现在是什么样的存在?",
"comment_pub_date": "2016-06-14 12:26:33"
}
复制代码
参数释义:
用到的库
背景虚化:Glide-transformations - github.com/wasabeef/gl…
OK,那接下来就看看是如何一点一点做出来的吧!
代码实现
音乐播放 - Service + MediaPlayer
public class MusicPlayService extends Service {
private static String TAG = "MusicPlayService";
MediaPlayer mediaPlayer;
boolean firstTimePlay;
public MusicPlayService(){}
@Override
public void onCreate() {
super.onCreate();
if (mediaPlayer == null){
mediaPlayer = new MediaPlayer();
firstTimePlay = true;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return new MyMusicPlayBinder();
}
public class MyMusicPlayBinder extends Binder {
public void playMusic(){
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
}
public void pauseMusic(){
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
}
public void playRandomMusic(String url, final OnNetworkMusicPreparedListener onNetworkMusicPreparedListener) {
try {
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync();
mediaPlayer.setLooping(true); // 循环播放
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
onNetworkMusicPreparedListener.onPrepared();
firstTimePlay = false;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isPlaying(){
return mediaPlayer.isPlaying();
}
public int getMusicDuration(){
return mediaPlayer.getDuration();
}
// 获取当前播放进度
public int getCurPosition(){
return mediaPlayer.getCurrentPosition();
}
public boolean isFirstTimePlay(){
return firstTimePlay;
}
}
@Override
public void onDestroy() {
mediaPlayer.release();
super.onDestroy();
}
}
复制代码
分析一下主要代码。
首先声明一个了 MediaPlayer 对象(第 4 行),用于音乐的播放。随后在 Service 被创建的时候对其进行初始化(第 14 行)。
定义了一个内部类 MyMusicPlayBinder(29 - 76 行),用以和 Activity 通信。在该类中,定义了一系列方法(播放、暂停、获取进度等等)供 Activity 调用。并且在 onBind() 方法中返回了一个 MyMusicPlayBinder 实例(26 行)。
这其中注意一下 playRandomMusic(40 - 58 行) 方法,由于我们播放的是网络音乐,所以要调用的是 mediaPlayer.prepareAsync(),即异步准备,然后必须要设置回调,即 mediaPlayer.setOnPreparedListener(),该回调会在 mediaPlayer 准备好之后被调用,我们应该在该回调中开始播放(调用 mediaPlayer.start())。
界面实现
接下来看看主界面的实现。
还是先看代码吧:
(为了避免代码看起来过长,省略了一些不重要的代码)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static String TAG = "MainActivity";
private int IMAGE_SOURCE_PLAY = R.drawable.play_white;
private int IMAGE_SOURCE_PAUSE = R.drawable.pause_white;
/*
控件声明 省略
*/
private MusicPlayService.MyMusicPlayBinder musicController;
ObjectAnimator objectAnimator; // 图片旋转动画
SeekBar seekBar; // 进度条
private ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicController = (MusicPlayService.MyMusicPlayBinder) service;
if (musicController.isPlaying())
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PAUSE);
else
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PLAY);
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initService();
if (savedInstanceState != null)
tv_music_title.setText(savedInstanceState.getString("musicTitle"));
}
private void initView(){
/*
各个控件的初始化 省略
*/
seekBar = (SeekBar) findViewById(R.id.music_progress_seek_bar);
// 禁止拖动 点击
seekBar.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
objectAnimator = ObjectAnimator.ofFloat(image_view_music, "rotation", 0, 360);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setDuration(20 * 1000);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);//Animation.INFINITE 表示重复多次
objectAnimator.setRepeatMode(ValueAnimator.RESTART);//RESTART表示从头开始,REVERSE表示从末尾倒播
btn_play_or_pause.setOnClickListener(this);
btn_next_random.setOnClickListener(this);
}
private void initService(){
Intent intent = new Intent(this, MusicPlayService.class);
startService(intent);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_play_or_pause:
playOrPauseMusic();
break;
case R.id.next_random:
nextRandomMusic();
default:
break;
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void playMusic(){
musicController.playMusic();
objectAnimator.resume();
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void pauseMusic(){
musicController.pauseMusic();
objectAnimator.pause();
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void playOrPauseMusic(){
if (musicController.isFirstTimePlay()){
Toast.makeText(MainActivity.this, "没有正在播放的音乐!", Toast.LENGTH_SHORT).show();
return;
}
if (musicController.isPlaying()){
pauseMusic();
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PLAY);
}else {
playMusic();
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PAUSE);
}
}
private void nextRandomMusic(){
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PLAY);
MusicModel.getRandomMusic(new GetRandomMusicListener() {
@Override
public void onSuccess(final Music music) {
LogUtil.e(TAG, music.getDescription());
musicController.playRandomMusic(music.getMp3_url(), new OnNetworkMusicPreparedListener() {
@Override
public void onPrepared() {
btn_play_or_pause.setImageResource(IMAGE_SOURCE_PAUSE);
mHandler.post(new Runnable() {
@Override
public void run() {
tv_music_title.setText(music.getTitle());
tv_music_desc.setText(music.getDescription().replaceAll("。", " "));
tv_comment.setText(music.getComment_content());
tv_comment_time.setText(music.getComment_pub_date());
tv_comment_username.setText(music.getComment_nickname());
tv_liked_count.setText(music.getComment_liked_count() + "");
seekBar.setMax(musicController.getMusicDuration());
new UpdateProgressThread().start();
tv_music_total_time.setText(TimeTool.format(musicController.getMusicDuration()));
Glide.with(MainActivity.this).load(music.getImages()).into(image_view_music);
Glide.with(MainActivity.this).load(music.getImages()).
apply(RequestOptions.bitmapTransform(new BlurTransformation(50,10))).into(image_view_bg);
Glide.with(MainActivity.this).load(music.getComment_avatar_url()).placeholder(R.drawable.placeholder).into(image_view_user_avatar);
objectAnimator.start();
}
});
}
});
}
@Override
public void onFailed() {
LogUtil.e(TAG, "获取音乐失败!");
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outState.putString("musicTitle", tv_music_title.getText().toString());
}
class UpdateProgressThread extends Thread {
@Override
public void run() {
super.run();
while (seekBar.getProgress() < seekBar.getMax()){
final int curPosition = musicController.getCurPosition();
seekBar.setProgress(curPosition);
SystemClock.sleep(1000);
mHandler.post(new Runnable() {
@Override
public void run() {
tv_music_cur_time.setText(TimeTool.format(curPosition));
}
});
}
}
}
}
复制代码
简单分析下代码。首先第 14 - 25 行,定义了一个 serviceConnection,用于 Activity 和 Service 建立连接。27 行定义一个 Handler 用于界面的更新(切换线程更新界面)。第 56 - 60 行初始化 objectAnimator,并将其与 image_view_music 绑定,该动画用于歌曲图片的旋转。169 - 185 行,定义了一个内部类 UpdateProgressThread,这个线程类是用来 SeekBar 的更新的,也就是实时更新播放进度条,由于不能在子线程更新 UI,所以必须要是用 mHandler.post() 切换到主线程去更新。
主要的代码差不多就这些了。
琐碎
在使用 Okhttp 的时候,连续多次调用 response.body().string() 会导致 java.lang.IllegalStateException: closed 错误。
Service 的绑定(bindService())实际上是一个异步的过程。
使用 bindService() 绑定服务就一定要调用 unbindService() 解绑,要记住了啊!
android 随机播放代码,Android | 一个随机播放网络音乐的小 Demo相关推荐
- Java黑皮书课后题第3章:**3.4(随机月份)编写一个随机产生1和12之间整数的程序,并根据数组1,2,3...显示对应的月份
**3.4(随机月份)编写一个随机产生1和12之间整数的程序,并根据数组1,2,3...显示对应的月份 题目 题目描述 破题 代码 如何理解产生随机数 题目 题目描述 **3.4(随机月份)编写一个随 ...
- android小球移动代码,Android中如何绘制一个跟随手指移动的小球
Android中如何绘制一个跟随手指移动的小球 发布时间:2020-11-07 16:22:43 来源:亿速云 阅读:82 作者:Leah 本篇文章为大家展示了Android中如何绘制一个跟随手指移动 ...
- Android钢琴滑动代码,Android实现简易版弹钢琴效果
本文实例为大家分享了Android实现弹钢琴效果展示的具体代码,供大家参考,具体内容如下 目标效果: 1.drawable下新建button_selector.xml页面: 2.drawable下新建 ...
- android小球移动代码,Android自定义圆形View实现小球跟随手指移动效果
本文实例为大家分享了Android实现小球跟随手指移动效果的具体代码,供大家参考,具体内容如下 一. 需求功能 手指在屏幕上滑动,红色的小球始终跟随手指移动. 实现的思路: 1)自定义View,在on ...
- Android钢琴滑动代码,android 钢琴界面实现
近在做一个钢琴的东西,关于这个界面如何设计画了很长时间,主要是考虑到针对不同的分辨率,如果只针对一种分辨率的话用绝对布局可以实现,实现的基本思想是每个白色的键的位置是可以计算出来的,屏幕的宽度可以获得 ...
- android确认密码代码,Android自定义View实现验证码or密码输入框
前言 最近项目中有支付功能,用户输入密码时要类似微信支付密码输入框的样式,本想直接copy网上的,但设计姐姐总是对样式挑三拣四,抽空自己自定义了一个,无奈之下抽空自定义了个,并把它贴到GitHub上供 ...
- android相对布局代码,Android基础_3 Activity相对布局(示例代码)
相对布局要比前面讲的线性布局和表格布局要灵活一些,所以平常用得也是比较多的.相对布局控件的位置是与其周围控件的位置相关的,从名字可以看出来,这些位置都是相对的,确定出了其中一个控件的位置就可以确定另一 ...
- android确认密码代码,Android手机卫士之确认密码对话框
本文接着实现"确认密码"功能,也即是用户以前设置过密码,现在只需要输入确认密码 布局文件和<Android 手机卫士--设置密码对话框>中的布局基本类似,所有copy一 ...
- android 进度条 代码,Android 进度条使用详解及示例代码
在这里,总结一下loading进度条的使用简单总结一下. 一.说起进度条,必须说说条形进度条,经常都会使用到嘛,特别是下载文件进度等等,还有像腾讯QQ安装进度条一样,有个进度总给人良好的用户体验. 先 ...
最新文章
- 贪心 ---- Educational Codeforces Round 90 (Rated for Div. 2)E. Sum of Digits[数位贡献+思维题+贪心]
- 基础环境搭建--原始mavenWeb项目
- Webservice 的设计和模式
- seo模拟点击软件_网站用软件刷排名好不好?
- 网上商城—管理员修改商品
- STL——萃取机制(Traits)
- Linux 中断处理浅析
- lisp java_从Java调用的LISP代码
- 树莓派vnc用法 linux,怎样使用VNC在树莓派上运行远程桌面
- oracle或mysql分组查询并且获取前3条排序后的数据
- Eclipse CDT 编译wxWidgets
- java递归删除文件夹_如何使用递归删除Java中的目录/文件夹
- 互联网+医疗考验无线部署 飞塔SAA同频部署模式巧妙化解
- android iptables清空,android中的iptables错误:iptables-save和iptables-restore无法正常工作...
- SVPWM调制的simulink仿真
- 哪个软件能做英语测试,英语考试软件哪个好用 英语考试软件神器推荐
- 解决Markdown在线编辑器不能换行的问题
- 【车间调度】改进的帝国企鹅算法求解车间调度问题【含Matlab源码 2041期】
- 190道Python3基础题以及解答
- 计算机科学引论试题,内工大计算机科学引论在线测试题.pdf
热门文章
- 安卓备忘录便签怎么转移到苹果手机
- MySql数据库函数集
- Android开发技术总结!如何才能更容易拿到大厂Offer?值得收藏!
- 海外直播带货系统源码,海外的直播带货究竟是什么样?
- BUAA-OO 第三单元作业 JML 总结与思考
- 如何恢复删除的照片和视频?可以试试看
- 撤销前进快捷键_ps怎么撤销的三种方法和ps撤销快捷键以及连续撤销多步快捷键...
- 【photoshop Action Manager】设置矩形选框工具样式固定比例
- js中两个对象数组如何比对合并
- 锐龙4000系列运行matlab,性能提升60%!AMD锐龙运行Matlab恢复正常