安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)

源代码在文末给出~ 如有疑问可以qq我哟:517486222

一、前言

这两天复习安卓四大组件,想起上个星期做的音乐播放器并不是很好,所有又拿这个项目练起了手。上次的项目中,我是通过用static静态量来保存当前播放的歌曲状态的,这样子虽然可以实现功能,但是从设计上来说并不是很好,容易导致APP崩溃和手机发烫~(我不确定是不是static的锅,但是面向对象编程就得少用static ? )
那我这个版本的音乐播放器实现原理是啥呢,请听我细细道来~

二、Activity显示页面

首先说明一下,我的音乐列表页面叫做MainActivity,歌曲详情页面叫做DetailActivity,服务是MusicService。

1. 音乐列表 – MainActivity(APP首页)

我在MainActivity中要显示音乐列表,在刚进入APP时,只显示歌曲列表,但如果有歌曲正在播放,或者有歌曲播放后被暂停了,在列表中显示当前播放的歌曲是哪一首,也就是用不同的样式突显出正在后台被服务的歌曲。如下图所示(图一后台无正在被服务歌曲,图二是列表突显了正在播放的歌曲):
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第1张图片安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第2张图片

图一中显示音乐列表比较简单,过会直接贴代码。难点是图二中,如何获取当前正在播放的歌曲呢?
我的上一个版本是在MusicService中用static标记了当前歌曲,而这个版本我没用static了,而是通过绑定服务的方式来获取当前MusicService中是否有正在播放的歌曲的。
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第3张图片

其中,与MusicService进行交互的Binder对象mBinder的值是在MyConnection类中得来的。

此处有个大坑???,不可以在bindService(intent, mConnection, BIND_AUTO_CREATE);后面直接写成mBinder = mConnection.binder,因为绑定服务是异步的,如果在bindService方法调用后直接给mBinder赋值,则极有可能得到null,因为还没有完成绑定,此时IBinder的对象还没带过来。解决方法就是在ServiceConnection子类的成功绑定回调方法onServiceConnected中赋值,此时可以得到有效的IBinder值。
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第4张图片

此外,还有一个需要注意的地方就是当我们打开APP从MainActivity进入DetailActivity播放歌曲,然后再从DetailActivity返回到MainActivity的时候,根据Activity的生命周期可知,此时应该是:
MainActivity::onCreate() -> MainActivity::onStart() -> MainActivity::onResume() ->MainActivity::onPause() -> DetailActivity::onCreate() -> DetailActivity::onStart() -> DetailActivity::onResume() -> MainActivity::onStop() -> DetailActivity::onPause() -> MainActivity::onRestart() -> MainActivity:: onStart() -> MainActivity::onResume() -> DetailActivity::onStop() -> DetailActivity::onDestory()
写得我手好酸,如有手误请见谅??
嘿嘿,我想表达的重点是第二次返回到MainActivity的时候,没有调用MainActivity::onCreate()方法了,如果我们的initView()只在MainActivity::onCreate()中调用,那从DetailActivity返回到MainActivity的时候,音乐列表将得不到更新,所以我们应该在MainActivity::onResume()也要调用initView方法,如下所示:
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第5张图片

  • MainActivity.java代码如下:
package com.jal.www.cathy;

import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "JalLog::MainActivity";
    private MyConnection mConnection;
    private MusicService.MyBinder mBinder;
    private List<Music> mMusicList;
    private ListView mListView;
    private Context mContext;
    private int mPpositionOfPlaying;
    class MyConnection implements ServiceConnection {

        private static final String TAG = "LogMyConnection";
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "onServiceConnected");
            mBinder = (MusicService.MyBinder) service;
            showListView();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected");
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "MainActivity :: onCreate()");
        mContext = this;
        requestPermission();
    }

    private void requestPermission() {
        List<String> permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }

        if (ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }

        if (!permissionList.isEmpty()){
            ActivityCompat.requestPermissions(this,permissionList.toArray(new String[permissionList.size()]),1);
        }else {
            initView();
        }
    }

    private void initView() {
        mListView = findViewById(R.id.listView);
        mMusicList = MusicList.getMusicList(this);
        if (mBinder == null){//mBinder is null that is what bindService is not finish
            bindMusicService();
        }else{
            showListView();
        }
    }

    private void showListView() {
        if ( mBinder == null || mBinder.isNullOfPlayer()){
            mPpositionOfPlaying = -1;
        } else {
            mPpositionOfPlaying = mBinder.getPosition();
        }
        MusicAdapter adapter = new MusicAdapter(this, mMusicList, mPpositionOfPlaying);
        mListView.setAdapter(adapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent();
                Bundle bundle = new Bundle();
                bundle.putInt("position", position);
                intent.putExtras(bundle);
                intent.setClass(MainActivity.this, DetailActivity.class);
                startActivity(intent);
            }
        });
    }

    private void bindMusicService() {
        Intent intent = new Intent();
        intent.setClass(this, MusicService.class);
        mConnection = new MyConnection();
        bindService(intent, mConnection, BIND_AUTO_CREATE);
        Log.i(TAG, "mBinder:"+ mBinder);
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0){
                    for (int i = 0; i < grantResults.length; i++) {

                        int grantResult = grantResults[i];
                        if (grantResult == PackageManager.PERMISSION_DENIED){
                            String s = permissions[i];
                            Toast.makeText(this,s+getResources().getString(R.string.rejectPermission),Toast.LENGTH_SHORT).show();
                        }else{
                            initView();
                        }
                    }
                }
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        Log.i(TAG, "MainActivity :: onDestroy()");
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        Log.i(TAG, "MainActivity :: onStart()");
        super.onStart();
    }

    @Override
    protected void onStop() {
        Log.i(TAG, "MainActivity :: onStop()");
        super.onStop();
    }

    @Override
    protected void onPause() {
        Log.i(TAG, "MainActivity :: onPause()");
        super.onPause();
    }

    @Override
    protected void onRestart() {
        Log.i(TAG, "MainActivity :: onRestart()");
        super.onRestart();
    }

    @Override
    protected void onResume() {
        Log.i(TAG, "MainActivity :: onResume()");
        super.onResume();
        initView();
    }
}


2. 歌曲详情页面 – DetailActivity

DetailActivity页面功能比较简单,后期还会继续完善的。目前只有三个按钮和文字显示歌曲名。
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第6张图片
代码也比较简单,主要就是先通过启动方式启动MusicService服务(启动方式为的是用户离开APP的时候音乐还可以在后台播放不被关闭),再通过绑定方式绑定MusicService服务(绑定方式是为了可以在这个页面用户可以跟服务交互,如暂停、播放、切换歌曲。)
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第7张图片

  • DetailActivity.java完整代码如下:
package com.jal.www.cathy;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.List;

public class DetailActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "JalLog::DetailActivity";
    private MyConnection mConnection;
    private TextView mMusicTitle;
    private List<Music> mMusicList;
    private Button mBtnPre, mBtnPlayPause, mBtnNext;
    private MusicService.MyBinder mBinder;

    class MyConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "onServiceConnected");
            mBinder = (MusicService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected");
        }
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "DetailActivity :: onCreate()");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        bindMusicService();
        bindView();
    }
    private void bindMusicService() {
        Intent intent = new Intent();
        intent.setClass(this, MusicService.class);
        intent.putExtras(getIntent().getExtras());
        startService(intent);
        mConnection = new MyConnection();
        bindService(intent, mConnection, BIND_AUTO_CREATE);
        Log.i(TAG, "mBinder:"+ mBinder);
    }
    private void bindView() {
        mMusicList = MusicList.getMusicList(this);
        mMusicTitle = findViewById(R.id.music_title);
        mBtnPre = findViewById(R.id.btn_pre);
        mBtnPlayPause = findViewById(R.id.btn_play_pause);
        mBtnNext = findViewById(R.id.btn_next);
        Bundle bundle = getIntent().getExtras();
        String title = mMusicList.get(bundle.getInt("position")).getTitle();
        mMusicTitle.setText(title);
        mBtnPre.setOnClickListener(this);
        mBtnPlayPause.setOnClickListener(this);
        mBtnNext.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_pre:
                mBinder.pre();
                break;
            case R.id.btn_play_pause:
                mBinder.play_pause();
                break;
            case R.id.btn_next:
                mBinder.next();
                break;
        }
    }

    @Override
    protected void onDestroy() {
        Log.i(TAG, "DetailActivity :: onDestroy()");
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        Log.i(TAG, "DetailActivity :: onStart()");
        super.onStart();
    }

    @Override
    protected void onStop() {
        Log.i(TAG, "DetailActivity :: onStop()");
        super.onStop();
    }

    @Override
    protected void onPause() {
        Log.i(TAG, "DetailActivity :: onPause()");
        super.onPause();
    }

    @Override
    protected void onRestart() {
        Log.i(TAG, "DetailActivity :: onRestart()");
        super.onRestart();
    }
    @Override
    protected void onResume() {
        Log.i(TAG, "DetailActivity :: onResume()");
        super.onResume();
    }
}

三、MusicService处理音乐播放、切换服务

自定义了一个Binder类,用来作为onBinder方法的返回对象,给用户通过Binder对象来进行交互
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第8张图片

  • MusicService.java
package com.jal.www.cathy;

import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import java.io.IOException;
import java.util.List;

public class MusicService extends Service {
    private static final String TAG = "JalLog::MusicService";
    private MediaPlayer mPlayer;
    private Music mMusic;
    private List<Music> mMusicList;
    private int mPosition;
    public MusicService() {
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "MusicService :: onCreate()");
        super.onCreate();
        mMusicList = MusicList.getMusicList(getApplicationContext());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "MusicService :: onStartCommand()");
        mPosition = intent.getExtras().getInt("mPosition");
        playIndex(mPosition);
        return super.onStartCommand(intent, flags, startId);
    }

    public class MyBinder extends Binder {

        public boolean isPlaying(){
            return mPlayer.isPlaying();
        }
        public boolean isNullOfPlayer(){
            return mPlayer == null;
        }
        public void play_pause() {
            if (mPlayer.isPlaying()) {
                mPlayer.pause();
                Log.i(TAG, "Play stop");
            } else {
                mPlayer.start();
                Log.i(TAG, "Play start");
            }
        }

        public void pre(){
            mPosition = (mPosition - 1 + mMusicList.size()) % mMusicList.size();
            playIndex(mPosition);
        }

        public void next(){
            mPosition = (mPosition + 1) % mMusicList.size();
            playIndex(mPosition);
        }

        public int getPosition(){
            return mPosition;
        }
        //Returns the length of the mMusic in milliseconds
        public int getDuration(){
            return mPlayer.getDuration();
        }

        //Return the name of the mMusic
        public String getName(){
            return mMusic.getName();
        }

        //Returns the current progress of the mMusic in milliseconds
        public int getCurrenPostion(){
            return mPlayer.getCurrentPosition();
        }

        //Set the progress of mMusic playback in milliseconds
        public void seekTo(int mesc){
            mPlayer.seekTo(mesc);
        }
    }

    private void playIndex(int position) {
        if (null == mPlayer){
            mPlayer = new MediaPlayer();
        }
        if (mMusic == mMusicList.get(position)){
            return;// continue play this mMusic.
        }

        mPlayer.reset();
        mMusic = mMusicList.get(position);
        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mPlayer.setDataSource(mMusic.getUrl());
            mPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mPlayer.start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "MusicService :: onBind()");
        return new MyBinder();
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "MusicService :: onDestroy()");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "MusicService :: onUnbind()");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, "MusicService :: onUnbind()");
        super.onRebind(intent);
    }

}

四、全部源代码

获取源代码途径一:

https://www.github.com/2604150210/Cathy

获取源代码途径二:

https://download.csdn.net/download/jal517486222/11110188
安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)_第9张图片

不要问我为什么用sublime打开Android代码,因为截图时色彩好看~??? 我编程的时候肯定用的是AndroidStudio啦

你可能感兴趣的:(Android开发)