安卓四大组件学习--音乐播放器,含有音乐列表显示当前播放歌曲,以及详细页面可以暂停、播放、切换歌曲(一)
源代码在文末给出~ 如有疑问可以qq我哟:517486222
一、前言
这两天复习安卓四大组件,想起上个星期做的音乐播放器并不是很好,所有又拿这个项目练起了手。上次的项目中,我是通过用static静态量来保存当前播放的歌曲状态的,这样子虽然可以实现功能,但是从设计上来说并不是很好,容易导致APP崩溃和手机发烫~(我不确定是不是static的锅,但是面向对象编程就得少用static ? )
那我这个版本的音乐播放器实现原理是啥呢,请听我细细道来~
二、Activity显示页面
首先说明一下,我的音乐列表页面叫做MainActivity,歌曲详情页面叫做DetailActivity,服务是MusicService。
1. 音乐列表 – MainActivity(APP首页)
我在MainActivity中要显示音乐列表,在刚进入APP时,只显示歌曲列表,但如果有歌曲正在播放,或者有歌曲播放后被暂停了,在列表中显示当前播放的歌曲是哪一首,也就是用不同的样式突显出正在后台被服务的歌曲。如下图所示(图一后台无正在被服务歌曲,图二是列表突显了正在播放的歌曲):


图一中显示音乐列表比较简单,过会直接贴代码。难点是图二中,如何获取当前正在播放的歌曲呢?
我的上一个版本是在MusicService中用static标记了当前歌曲,而这个版本我没用static了,而是通过绑定服务的方式来获取当前MusicService中是否有正在播放的歌曲的。

其中,与MusicService进行交互的Binder对象mBinder的值是在MyConnection类中得来的。
此处有个大坑???,不可以在
bindService(intent, mConnection, BIND_AUTO_CREATE);后面直接写成mBinder = mConnection.binder,因为绑定服务是异步的,如果在bindService方法调用后直接给mBinder赋值,则极有可能得到null,因为还没有完成绑定,此时IBinder的对象还没带过来。解决方法就是在ServiceConnection子类的成功绑定回调方法onServiceConnected中赋值,此时可以得到有效的IBinder值。
此外,还有一个需要注意的地方就是当我们打开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方法,如下所示:

- 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";@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(TAG, "onServiceConnected");mBinder = (MusicService.MyBinder) service;showListView();}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(TAG, "onServiceDisconnected");}}@Overrideprotected 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 finishbindMusicService();}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() {@Overridepublic 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);}@Overridepublic 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;}}@Overrideprotected void onDestroy() {Log.i(TAG, "MainActivity :: onDestroy()");super.onDestroy();}@Overrideprotected void onStart() {Log.i(TAG, "MainActivity :: onStart()");super.onStart();}@Overrideprotected void onStop() {Log.i(TAG, "MainActivity :: onStop()");super.onStop();}@Overrideprotected void onPause() {Log.i(TAG, "MainActivity :: onPause()");super.onPause();}@Overrideprotected void onRestart() {Log.i(TAG, "MainActivity :: onRestart()");super.onRestart();}@Overrideprotected void onResume() {Log.i(TAG, "MainActivity :: onResume()");super.onResume();initView();}
}
2. 歌曲详情页面 – DetailActivity
DetailActivity页面功能比较简单,后期还会继续完善的。目前只有三个按钮和文字显示歌曲名。

代码也比较简单,主要就是先通过启动方式启动MusicService服务(启动方式为的是用户离开APP的时候音乐还可以在后台播放不被关闭),再通过绑定方式绑定MusicService服务(绑定方式是为了可以在这个页面用户可以跟服务交互,如暂停、播放、切换歌曲。)

- 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 {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(TAG, "onServiceConnected");mBinder = (MusicService.MyBinder) service;}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(TAG, "onServiceDisconnected");}}@Overrideprotected 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);}@Overridepublic 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;}}@Overrideprotected void onDestroy() {Log.i(TAG, "DetailActivity :: onDestroy()");super.onDestroy();}@Overrideprotected void onStart() {Log.i(TAG, "DetailActivity :: onStart()");super.onStart();}@Overrideprotected void onStop() {Log.i(TAG, "DetailActivity :: onStop()");super.onStop();}@Overrideprotected void onPause() {Log.i(TAG, "DetailActivity :: onPause()");super.onPause();}@Overrideprotected void onRestart() {Log.i(TAG, "DetailActivity :: onRestart()");super.onRestart();}@Overrideprotected void onResume() {Log.i(TAG, "DetailActivity :: onResume()");super.onResume();}
}
三、MusicService处理音乐播放、切换服务
自定义了一个Binder类,用来作为onBinder方法的返回对象,给用户通过Binder对象来进行交互

- 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() {}@Overridepublic void onCreate() {Log.i(TAG, "MusicService :: onCreate()");super.onCreate();mMusicList = MusicList.getMusicList(getApplicationContext());}@Overridepublic 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 millisecondspublic int getDuration(){return mPlayer.getDuration();}//Return the name of the mMusicpublic String getName(){return mMusic.getName();}//Returns the current progress of the mMusic in millisecondspublic int getCurrenPostion(){return mPlayer.getCurrentPosition();}//Set the progress of mMusic playback in millisecondspublic 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();}@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "MusicService :: onBind()");return new MyBinder();}@Overridepublic void onDestroy() {Log.i(TAG, "MusicService :: onDestroy()");super.onDestroy();}@Overridepublic boolean onUnbind(Intent intent) {Log.i(TAG, "MusicService :: onUnbind()");return super.onUnbind(intent);}@Overridepublic 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

不要问我为什么用sublime打开Android代码,因为截图时色彩好看~??? 我编程的时候肯定用的是AndroidStudio啦
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

