Implementation of music player in Android program design

Realization of Android music player

Realize an online music player based on MediaPlayer technology, play online music, and use SpringBoot to store music in the Tomcat server at the back end. The app obtains music through network requests, thereby realizing online music playback. The project is divided into user side and administrator side


1. Introduction to core technology Service components

Service is an application component that can perform long-running operations in the background without a user interface, and does not rely on any user interface, such as playing music in the background, downloading files in the background, and so on.

Although the service runs in the background, both the Service and the Activity run in the main thread (UI main thread) where the current APP is located, and time-consuming operations (such as network requests, copying data, and large files) will block the main thread. Bring bad experience to users. If you need to perform time-consuming operations in the service, you can choose IntentService, which is a subclass of Service and is used to process asynchronous requests.

2. How to use SerVice

To create a Service, right-click in Android Studio and select Service to create. By default, the Service we created inherits from Service. At the same time, in order to realize the function of background music playback, we also need to implement the interface of MediaPlayer.OnCompletionListener.

At the same time, we need to register the created Service in the AndroidManifest.xml file, which Android Studio has automatically created for us.

3. Two ways to start Service

(1) On the Acitivity interface, start and close the service through explicit intent (or implicit intent).

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

(2) bindService () binding service

A service is in the "bound" state when an application component binds to it by calling bindService(). Bound services provide a client-server interface that allows components to interact with the service, send requests, and get results. Bound services only run when bound to another application component. Multiple components can bind to the service at the same time, but when they are all unbound, the service is destroyed.


1. Client function modules: login, registration, music recommendation, music classification, personal center, music browsing record, my download, previous song, next song, music download

insert image description here
insert image description here


2. Administrator module: login, registration, user management, music category management (add category, delete category, edit category), music management (modify song name, move music category, delete music)

insert image description here
insert image description here


3. Part of the core code implementation

welcome page

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

    }
}

User home page

/**
 * 用户主页面
 */
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();
        }
    }
}

music player interface

/**
 * 音乐播放界面
 */
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();

        }
    }

}

For details, please private message~~~~~~

Guess you like

Origin blog.csdn.net/jky_yihuangxing/article/details/127898359