基于MediaPlayer技术实现在线音乐播放器,播放在线音乐,后端使用SpringBoot将音乐存放在Tomcat服务器。app通过网络请求获取音乐,从而实现在线音乐播放。该项目分为用户端和管理员端
Service它可以在后台执行长时间运行操作而没有用户界面的应用组件,不依赖任何用户界面,例如后台播放音乐,后台下载文件等。
虽然服务是在后台运行的,但是Service和Activity都是运行在当前APP所在的main thread(UI主线程)中的,而耗时操作(如网络请求、拷贝数据、大文件)会阻塞主线程,给用户带来不好的体验。如果需要在服务中进行耗时操作,可以选择 IntentService,IntentService是Service的子类,用来处理异步请求。
创建Service直接在Android Studio中右键选择Service创建即可。默认我们所创建的Service是继承Service的。同时我们为了实现后台音乐播放的功能,所以我们还要实MediaPlayer.OnCompletionListener的接口。
同时还要在AndroidManifest.xml文件中对已经创建的Service进行注册,这一点Android Studio已经帮我们自动创建好了
(1)在Acitivity界面通过显式意图(或隐式意图)的方式来启动服务和关闭服务。
Intent intentService = new Intent(MainActivity.this, AudioService.class);
startService(intentService);
(2)bindService()绑定服务
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。




欢迎页
/**
* 欢迎页
*/
@SuppressLint("CustomSplashScreen")
public class SplashActivity extends BaseActivity<ActivitySplashBinding> {
@Override
protected ActivitySplashBinding getViewBinding() {
return ActivitySplashBinding.inflate(getLayoutInflater());
}
@Override
protected void setListener() {
}
@Override
protected void initData() {
new Handler(Looper.myLooper()).postDelayed(new Runnable() {
@Override
public void run() {
startActivity(new Intent(mContext,LoginActivity.class));
finish();
}
},800);
}
}
用户主页面
/**
* 用户主页面
*/
public class MainActivity extends BaseActivity<ActivityMainBinding> {
private String[] titles = {"音乐推荐", "音乐分类"};
private List<Fragment> fragmentList = new ArrayList<>();
@Override
protected ActivityMainBinding getViewBinding() {
return ActivityMainBinding.inflate(getLayoutInflater());
}
@Override
protected void setListener() {
mBinding.avatar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(new Intent(MainActivity.this, MineActivity.class), 20002);
}
});
}
@Override
protected void initData() {
if (ApiConstants.getUserInfo() != null) {
mBinding.username.setText(ApiConstants.getUserInfo().getUsername());
}
//造数据
fragmentList.add(new HomeFragment());
fragmentList.add(new TypeFragment());
// fragmentList.add(new RecordFragment());
//如果处理成懒加载的话,其实很简单,只要是这个方法setOffscreenPageLimit不去设置,就可以了。
// mBinding.viewPager.setOffscreenPageLimit(fragmentList.size());
mBinding.viewPager.setUserInputEnabled(false);
mBinding.viewPager.setAdapter(new FragmentStateAdapter(this) {
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
});
mBinding.tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mBinding.viewPager.setCurrentItem(tab.getPosition(), false);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(mBinding.tabs, mBinding.viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
tab.setText(titles[position]);
}
});
//这句话很重要
tabLayoutMediator.attach();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 20002) {
finish();
}
}
}
音乐播放界面
/**
* 音乐播放界面
*/
public class PlayMusicActivity extends BaseActivity<ActivityPlayMusicBinding> implements OnPlayerEventListener {
private static final String TAG = "============";
private MusicInfo musicInfo;
private int mLastProgress;
@Override
protected ActivityPlayMusicBinding getViewBinding() {
return ActivityPlayMusicBinding.inflate(getLayoutInflater());
}
@Override
protected void setListener() {
Aria.download(this).register();
mBinding.sbProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
if (Math.abs(progress - mLastProgress) >= DateUtils.SECOND_IN_MILLIS) {
mBinding.tvCurrentTime.setText(formatTime("mm:ss", progress));
mLastProgress = progress;
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (AudioPlayer.get().isPlaying() || AudioPlayer.get().isPausing()) {
int progress = seekBar.getProgress();
AudioPlayer.get().seekTo(progress);
} else {
seekBar.setProgress(0);
}
}
});
mBinding.ivMusicPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioPlayer.get().playPause();
}
});
mBinding.ivMusicPrevious.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioPlayer.get().prev();
}
});
mBinding.ivMusicNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioPlayer.get().next();
}
});
//下载
mBinding.download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (XXPermissions.isGranted(mContext, Permission.Group.STORAGE)) {
download();
} else {
checkPermission();
}
}
});
}
@Override
protected void initData() {
initSystemBar();
musicInfo = (MusicInfo) getIntent().getSerializableExtra("musicInfo");
//监听
AudioPlayer.get().addOnPlayEventListener(this);
if (null != musicInfo) {
AudioPlayer.get().addAndPlay(musicInfo);
//添加到浏览记录
addRecord(musicInfo);
//获取单个任务实体
DownloadEntity entity = Aria.download(this).getFirstDownloadEntity(musicInfo.getMusic_url());
if (null != entity) {
mBinding.download.setClickable(false);
mBinding.download.setImageResource(R.mipmap.ic_download_complete);
} else {
mBinding.download.setClickable(true);
mBinding.download.setImageResource(R.mipmap.iv_download);
}
}
}
/**
* 沉浸式状态栏
*/
private void initSystemBar() {
ImmersionBar.with(this).init();
}
public String formatTime(String pattern, long milli) {
int m = (int) (milli / DateUtils.MINUTE_IN_MILLIS);
int s = (int) ((milli / DateUtils.SECOND_IN_MILLIS) % 60);
String mm = String.format(Locale.getDefault(), "%02d", m);
String ss = String.format(Locale.getDefault(), "%02d", s);
return pattern.replace("mm", mm).replace("ss", ss);
}
@SuppressLint("SetTextI18n")
private void onChangeImpl(MusicInfo music) {
if (music == null) {
return;
}
mBinding.sbProgress.setProgress((int) AudioPlayer.get().getAudioPosition());
mBinding.sbProgress.setSecondaryProgress(0);
mLastProgress = 0;
mBinding.tvCurrentTime.setText("00:00");
if (AudioPlayer.get().isPlaying() || AudioPlayer.get().isPreparing()) {
mBinding.ivMusicPlay.setSelected(true);
} else {
mBinding.ivMusicPlay.setSelected(false);
}
mBinding.toolbar.setTitle(music.getMusic_title());
mBinding.tvMusicTitle.setText(music.getMusic_title());
mBinding.tvMusicSongType.setText(music.getMusic_type());
startAnim();
}
@Override
public void onChange(MusicInfo music) {
onChangeImpl(music);
}
@Override
public void onPlayerStart(long duration) {
//一定要设置最大值
mBinding.sbProgress.setMax((int) duration);
mBinding.tvTotalTime.setText(formatTime("mm:ss", duration));
mBinding.ivMusicPlay.setSelected(true);
startAnim();
}
@Override
public void onPlayerPause() {
mBinding.ivMusicPlay.setSelected(false);
stopAnim();
}
@Override
public void onPublish(int progress) {
mBinding.sbProgress.setProgress(progress);
}
@Override
public void onBufferingUpdate(int percent) {
mBinding.sbProgress.setSecondaryProgress(mBinding.sbProgress.getMax() * 100 / percent);
}
private Animation animation;
private void startAnim() {
animation = AnimationUtils.loadAnimation(this, R.anim.rotation_animation);
LinearInterpolator lin = new LinearInterpolator();//设置动画匀速运动
animation.setInterpolator(lin);
mBinding.imgCd.startAnimation(animation);
}
private void stopAnim() {
if (mBinding.imgCd.getAnimation() != null) {
mBinding.imgCd.clearAnimation();
}
}
private void addRecord(MusicInfo musicInfo) {
OkGo.<String>get(ApiConstants.ADD_RECORD_MUSIC_URL)
.params("username", ApiConstants.getUserInfo().getUsername())
.params("music_title", musicInfo.getMusic_title())
.params("music_url", musicInfo.getMusic_url())
.params("music_type", musicInfo.getMusic_type())
.execute(new HttpStringCallback(null) {
@Override
protected void onSuccess(String msg, String response) {
}
@Override
protected void onError(String response) {
}
});
}
private void checkPermission() {
XXPermissions.with(this)
// 申请单个权限
// 申请多个权限
.permission(Permission.Group.STORAGE)
// 设置权限请求拦截器(局部设置)
//.interceptor(new PermissionInterceptor())
// 设置不触发错误检测机制(局部设置)
//.unchecked()
.request(new OnPermissionCallback() {
@Override
public void onGranted(List<String> permissions, boolean all) {
if (!all) {
showToast("获取部分权限成功,但部分权限未正常授予");
return;
}
//这里做操作
}
@Override
public void onDenied(List<String> permissions, boolean never) {
if (never) {
showToast("被永久拒绝授权,请手动授予录音和日历权限");
// 如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.startPermissionActivity(mContext, permissions);
} else {
showToast("获取录音和日历权限失败");
}
}
});
}
@Download.onWait
public void onWait(DownloadTask task) {
Log.d(TAG, "onWait: ");
}
@Download.onPre
public void onPre(DownloadTask task) {
Log.d(TAG, "onPre: ");
}
@Download.onTaskStart
public void onTaskStart(DownloadTask task) {
Log.d(TAG, "onTaskStart: ");
showToast("开始下载~~~~~");
}
@Download.onTaskRunning
public void onTaskRunning(DownloadTask task) {
Log.d(TAG, "onTaskRunning: ");
}
@Download.onTaskResume
public void onTaskResume(DownloadTask task) {
Log.d(TAG, "onTaskResume: ");
}
@Download.onTaskStop
public void onTaskStop(DownloadTask task) {
Log.d(TAG, "onTaskStop: ");
}
@Download.onTaskCancel
public void onTaskCancel(DownloadTask task) {
Log.d(TAG, "onTaskCancel: ");
}
@Download.onTaskFail
public void onTaskFail(DownloadTask task, Exception e) {
Log.d(TAG, "onTaskFail: ");
}
@Download.onTaskComplete
public void onTaskComplete(DownloadTask task) {
Log.d(TAG, "onTaskComplete: ");
mBinding.download.setClickable(false);
mBinding.download.setImageResource(R.mipmap.ic_download_complete);
showToast("下载完成~~~~~");
}
private void download() {
if (null != musicInfo) {
Aria.download(PlayMusicActivity.this)
.load(musicInfo.getMusic_url()) // 下载地址
.setFilePath(getExternalCacheDir().getPath() + musicInfo.getMusic_title() + ".mp3") // 设置文件保存路径
.setExtendField(GsonUtils.toJson(musicInfo))
.create();
}
}
}
详情可私信哦~~~~~~
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden