Android程序设计之音乐播放器实现

news2024/11/25 20:21:46

Android毕设音乐播放器实现

基于MediaPlayer技术实现在线音乐播放器,播放在线音乐,后端使用SpringBoot将音乐存放在Tomcat服务器。app通过网络请求获取音乐,从而实现在线音乐播放。该项目分为用户端和管理员端


一、核心技术Service组件介绍

Service它可以在后台执行长时间运行操作而没有用户界面的应用组件,不依赖任何用户界面,例如后台播放音乐,后台下载文件等。

虽然服务是在后台运行的,但是Service和Activity都是运行在当前APP所在的main thread(UI主线程)中的,而耗时操作(如网络请求、拷贝数据、大文件)会阻塞主线程,给用户带来不好的体验。如果需要在服务中进行耗时操作,可以选择 IntentService,IntentService是Service的子类,用来处理异步请求。

二、如何使用SerVice

创建Service直接在Android Studio中右键选择Service创建即可。默认我们所创建的Service是继承Service的。同时我们为了实现后台音乐播放的功能,所以我们还要实MediaPlayer.OnCompletionListener的接口。

同时还要在AndroidManifest.xml文件中对已经创建的Service进行注册,这一点Android Studio已经帮我们自动创建好了

三、Service启动的两种方式

(1)在Acitivity界面通过显式意图(或隐式意图)的方式来启动服务和关闭服务。

Intent intentService = new Intent(MainActivity.this, AudioService.class);
 startService(intentService);

(2)bindService()绑定服务

当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。


1. 用户端功能模块:登录,注册,音乐推荐,音乐分类,个人中心,音乐浏览记录,我的下载,上一曲,下一曲,音乐下载

在这里插入图片描述
在这里插入图片描述


2.管理员模块:登录,注册,用户管理,音乐分类管理(添加分类,删除分类,编辑分类),音乐管理(修改歌名,移动音乐分类,删除音乐)

在这里插入图片描述
在这里插入图片描述


3.部分核心代码实现

欢迎页

/**
 * 欢迎页
 */
@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();

        }
    }

}

详情可私信哦~~~~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/13733.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

远程教育:低代码重塑教育技术

新冠肺炎大流行对世界各地的行业产生了影响&#xff0c;其中一些行业的影响远远超过其他行业。食品、零售、供应链、娱乐和航空业是受影响最大的行业&#xff0c;为确保不间断运营&#xff0c;这引发了一场数字革命。相信&#xff0c;这种数字化的采用将长期保持下去&#xff0…

​电脑上的回收站怎么隐藏 ,怎么隐藏桌面回收站图标

回收站是电脑上不可分割的一部分&#xff0c;往往被放在电脑的桌面上&#xff0c;我们是不能够删除桌面上的回收站的&#xff0c;但是如果想要一个很干净的桌面&#xff0c;不想让回收站出现在电脑桌面上&#xff0c;​电脑上的回收站怎么隐藏&#xff1f; 通过本文&#xff0c…

List接口

集合框架图 list接口的常用实现类 list接口的常用方法 ■void add(int index, E element);向指定位置上添加元素&#xff0c;原始数据后移■E remove(int index);删除指定位置上的元素&#xff0c;并返回被删除的元素&#xff0c;原始位置上的元素前移■E get(int index);按照索…

Maven 【ERROR】 不再支持源选项 5。请使用 7或更高版本解决方案:修改Maven默认JDK(含完整代码及详细教程)

文章目录一、前言二、项目场景三、问题描述四、原因分析五、解决方案1. 查看本机安装的jdk版本2. 找到settings.xml文件3. 编辑settings.xml文件4.找到pom.xml文件5.修改pom.xml文件六、验证七、结语一、前言 博主在遇到这个问题时&#xff0c;找遍了百度的方法也没办法解决&a…

[附源码]Python计算机毕业设计java视频点播系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

AI+教育的结合

1、AI 目前有哪些应用方向 我们所说的 AI 到底是什么&#xff1f;先定义清楚&#xff0c;我们先不要被这个被过度包装的概念迷惑说 AI 能代替人类&#xff0c;无所不能。 实际上&#xff0c;人工智能&#xff0c;是属于计算机科学的一个分支&#xff0c;我们常听到的 机器学习…

java读取局域网共享文件夹中文件并保存到本地文件夹

在磁盘新建一个文件夹&#xff0c;右击文件夹属性&#xff0c;点击共享 点击网络和共享中心 设置文件夹可访问权限 到此就可以用本地ip加文件夹名称访问了&#xff0c;同局域网也可以通过改地址访问 文件夹的名称来自这张图的 网络路径 如果需要通过java的jcifs包访问请继…

负载均衡-动静分离

文章目录一.Nginx负载均衡实现原理1、反向代理原理2、反向代理的概念3、反向代理的优势4、Nginx四层反向代理和七层反向代理二、Nginx动静分离实现原理1、动静分离的概念2、动静分离的原理3、Nginx 静态处理优势三、Nginx负载均衡调度算法&#xff08;6种&#xff09;1、轮询&a…

Sentinel源码剖析之执行流程

1、说明 Sentinel主要用来流控&#xff0c;熔断降级保护目标资源用的&#xff0c;常用集成SCG&#xff0c;SpringBoot&#xff0c;SprinMVC这些&#xff0c;但底层本质没变&#xff0c;但是体现形式上会有差别。例如SCG底层是Netty 和 SpringWebFlux 采用Reactor Stream处理&a…

策略验证_买入口诀_三杆通底反弹在即

写在前面&#xff1a; 1. 本文中提到的“股票策略校验工具”的具体使用操作请查看该博文&#xff1b; 2. 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 3. 本系列文章是用来学习技法&#xff0c;文中所得内容都仅仅只是作为演示功能使用 目录 解说 策略代码 结果 解…

【POJ No. 1988】 方块栈 Cube Stacking

【POJ No. 1988】 方块栈 Cube Stacking POJ 题目地址 【题意】 贝西正在玩方块游戏&#xff0c;方块编号为1&#xff5e;N&#xff08;1≤N ≤30,000&#xff09;&#xff0c;开始时每个方块都相当于一个栈。 贝西执行P 个&#xff08;1≤P ≤100,000&#xff09;操作&…

深圳市数字经济指数发布:数字经济蓬勃发展,数字用户深度渗透

2022年1月&#xff0c;国务院印发了《“十四五”数字经济发展规划》。规划提出&#xff0c;到2025年&#xff0c;数字经济核心产业增加值占国内生产总值比重达到10%&#xff0c;数据要素市场体系初步建立&#xff0c;产业数字化转型迈上新台阶&#xff0c;数字产业化水平显著提…

【论文精读4】MVSNet系列论文详解-CVP-MVSNet

CVP-MVSNet全文名为“Cost Volume Pyramid Based Depth Inference for Multi-View Stereo”&#xff0c;主要创新点在于使用由粗到细&#xff08;coarse-to-fine&#xff09;模式来构建代价体金字塔&#xff08;cost volume pyramid&#xff09;&#xff0c;流程如下&#xff1…

Java虚拟机之运行时数据区(一)

Java虚拟机之运行时数据区 简述 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的区域随着虚拟机进行启动而一直存在&#xff0c;有些区域则是依赖用户线程的启动和结…

房子装修有哪些注意事项要注意?

房子装修有哪些注意事项要注意&#xff1f;这里的东西太多了&#xff0c;可能三天三夜都搞不清楚&#xff0c;而且对于初次装修的业主来说&#xff0c;很多专业术语都是一头雾水&#xff0c;所以本文就简单介绍一下&#xff0c;从装修到装修&#xff0c;以及需要注意的几点。 房…

【深度学习】实验4答案:脑部 MRI 图像分割

DL_class 学堂在线《深度学习》实验课代码报告&#xff08;其中实验1和实验6有配套PPT&#xff09;&#xff0c;授课老师为胡晓林老师。课程链接&#xff1a;https://www.xuetangx.com/training/DP080910033751/619488?channeli.area.manual_search。 持续更新中。 所有代码…

数据之道读书笔记-05面向“联接共享”的数据底座建设

数据之道读书笔记-05面向“联接共享”的数据底座建设 在从信息化向数字化转型的过程中&#xff0c;企业积累了海量的数据&#xff0c;并且还在爆发式地增长。数据很多&#xff0c;但真正能产生价值的数据却很少。数据普遍存在分散、不拉通的问题&#xff0c;缺乏统一的定义和架…

[发送AT指令配置a7670C模块上网]

发送AT指令配置a7670C模块上网内核配置调试验证应用的编写AT的指令的使用内核配置 概要&#xff1a;基于rv1126平台调试SIMCOM 7670C 4G模块。 1&#xff0c;内核配置及内核代码修改 1.1按照芯片data sheet修改kernel代码&#xff0c;添加PID和VID。 此处根据不同的芯片按照其…

Mybatis plus注解@TableField详解

Mybatis plus注解TableField详解 目录1. 前言2. 应用场景2.1 更新null值2.2 查询时排除字段2.3 非数据库字段属性1. 前言 TableField 字段注解&#xff0c;该注解用于标识非主键的字段。将数据库列与 JavaBean 中的属性进行映射。 2. 应用场景 2.1 更新null值 想要在更新字…

auth模块方法的使用

文章目录1、创建超级用户(管理员)2、获取表&#xff0c;检验密码3、保存用户状态4、获取用户对象&#xff0c;校验用户是否登录5、验证用户是否登录6、修改密码7、注销8、注册9、方法总结10、如何扩展auth_user表1、创建超级用户(管理员) """ 在创建好一个djan…