Android Mp3播放器,支持Service后台播放
来源:互联网 发布:手机好莱坞特效软件 编辑:程序博客网 时间:2024/05/16 07:31
大家好,作为小白,这是我第一篇文章,也是我的一个学习记录。废话就不多说了,开始进入正题吧。
这篇文章是我做Mp3播放器的一个过程,首先整理一下思路,我们做播放器会需要什么东西呢?
一:获取本地的音频文件。并且保存到一个List里面方便我们来读取。
二:需要一个界面来展示出我们所获取到的音频文件。
三:播放界面,需要有播放,下一首,等一系列操作。
四:Service,我们需要运用Service来达到后台播放目的,当然使用它的前提就是我们能够先了解到Service的基本使用方法,这里我就不多做说明了.等以后我会写一点点关于这个的东西。
好了,大概的流程就是这样,下面我们来用代码实现。
一:获取本地音频文件
public class Audio { private String mTitle, mTitleKey,//标题 mArtist,//艺术家名 mArtistKey, mComposer, mAlbum, mAlbumKey, mDisplayName,//歌曲名 mMimeType, mPath;//路径 private int mId, mArtistId, mAlbumId, mYear, mTrack; private int mDuration = 0,//时长 mSize = 0;//大小 private boolean isRingtone = false, isPodcast = false, isAlarm = false, isMusic = false, isNotification = false; public Audio(Bundle bundle) { mId = bundle.getInt(MediaStore.Audio.Media._ID); mTitle = bundle.getString(MediaStore.Audio.Media.TITLE); mTitleKey = bundle.getString(MediaStore.Audio.Media.TITLE_KEY); mArtist = bundle.getString(MediaStore.Audio.Media.ARTIST); mArtistKey = bundle.getString(MediaStore.Audio.Media.ARTIST_KEY); mComposer = bundle.getString(MediaStore.Audio.Media.COMPOSER); mAlbum = bundle.getString(MediaStore.Audio.Media.ALBUM); mAlbumKey = bundle.getString(MediaStore.Audio.Media.ALBUM_KEY); mDisplayName = bundle.getString(MediaStore.Audio.Media.DISPLAY_NAME); mYear = bundle.getInt(MediaStore.Audio.Media.YEAR); mMimeType = bundle.getString(MediaStore.Audio.Media.MIME_TYPE); mPath = bundle.getString(MediaStore.Audio.Media.DATA); mArtistId = bundle.getInt(MediaStore.Audio.Media.ARTIST_ID); mAlbumId = bundle.getInt(MediaStore.Audio.Media.ALBUM_ID); mTrack = bundle.getInt(MediaStore.Audio.Media.TRACK); mDuration = bundle.getInt(MediaStore.Audio.Media.DURATION); mSize = bundle.getInt(MediaStore.Audio.Media.SIZE); isRingtone = bundle.getInt(MediaStore.Audio.Media.IS_RINGTONE) == 1; isPodcast = bundle.getInt(MediaStore.Audio.Media.IS_PODCAST) == 1; isAlarm = bundle.getInt(MediaStore.Audio.Media.IS_ALARM) == 1; isMusic = bundle.getInt(MediaStore.Audio.Media.IS_MUSIC) == 1; isNotification = bundle.getInt(MediaStore.Audio.Media.IS_NOTIFICATION) == 1; } public int getId() { return mId; } public String getMimeType () { return mMimeType; } public int getDuration () { return mDuration; } public int getSize () { return mSize; } public boolean isRingtone () { return isRingtone; } public boolean isPodcast () { return isPodcast; } public boolean isAlarm () { return isAlarm; } public boolean isMusic () { return isMusic; } public boolean isNotification () { return isNotification; } public String getTitle () { return mTitle; } public String getTitleKey () { return mTitleKey; } public String getArtist () { return mArtist; } public int getArtistId () { return mArtistId; } public String getArtistKey () { return mArtistKey; } public String getComposer () { return mComposer; } public String getAlbum () { return mAlbum; } public int getAlbumId () { return mAlbumId; } public String getAlbumKey () { return mAlbumKey; } public String getDisplayName () { return mDisplayName; } public int getYear () { return mYear; } public int getTrack () { return mTrack; } public String getPath () { return mPath; } }
这里我们写了一个Audio的音频文件类,它所有的属性有mTitle,
mTitleKey,
mArtist,
mArtistKey,
mComposer,
mAlbum,
mAlbumKey,
mDisplayName,
mMimeType,
mPath;
我们从名字中就可以看到其作用,歌曲标题,艺术家名等.
public Audio(Bundle bundle) 这个构造的函数的作用呢,也就相当于我们取到本地音频的一系列值,然后存储到这个类中来。
然后就是实现这个类的属性的get方法,从类中获取到这些属性。
接下来,我们需要去浏览本地文件,选出音频文件进行保存:
public class MediaUtils { public static final String[] AUDIO_KEYS = new String[]{ MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.TITLE_KEY, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ARTIST_ID, MediaStore.Audio.Media.ARTIST_KEY, MediaStore.Audio.Media.COMPOSER, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.ALBUM_KEY, MediaStore.Audio.Media.DISPLAY_NAME, MediaStore.Audio.Media.DURATION, MediaStore.Audio.Media.SIZE, MediaStore.Audio.Media.YEAR, MediaStore.Audio.Media.TRACK, MediaStore.Audio.Media.IS_RINGTONE, MediaStore.Audio.Media.IS_PODCAST, MediaStore.Audio.Media.IS_ALARM, MediaStore.Audio.Media.IS_MUSIC, MediaStore.Audio.Media.IS_NOTIFICATION, MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.DATA }; //读取本地音频文件,保存到list里并返回 public static List<Audio> getAudioList(Context context){ List<Audio> audioList = new ArrayList<Audio>(); ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, AUDIO_KEYS, null, null, null); for(cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){ Bundle bundle = new Bundle(); for(int i=0;i<AUDIO_KEYS.length;i++){ final String key = AUDIO_KEYS[i]; final int columnIndex = cursor.getColumnIndex(key); final int type = cursor.getType(columnIndex); switch (type) { case Cursor.FIELD_TYPE_BLOB: break; case Cursor.FIELD_TYPE_FLOAT: float floatValue = cursor.getFloat(columnIndex); bundle.putFloat(key, floatValue); break; case Cursor.FIELD_TYPE_INTEGER: int intValue = cursor.getInt(columnIndex); bundle.putInt(key, intValue); break; case Cursor.FIELD_TYPE_NULL: break; case Cursor.FIELD_TYPE_STRING: String strValue = cursor.getString(columnIndex); bundle.putString(key, strValue); break; } } Audio audio = new Audio (bundle); audioList.add(audio); } cursor.close(); return audioList; }}
这一个类的主要目的就是去通过Cusor索引,然后去获取到本地的音频文件,然后保存到List里面,在返回一个List
这里我们已经拿到了步骤一种所说的,保存有本地所有音频文件的List
然后如何将这个List显示在手机界面上,并且可以点击的呢。
MainActivity
@ContentView(R.layout.activity_main)public class MainActivity extends Activity { @ViewInject(R.id.mp3_ListView) ListView mp3ListView; private Context mContext; static List<Audio> mp3Infos = new ArrayList<Audio>(); private Mp3Adapter adapter; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); x.view().inject(this); init(); //给MusicPlayActivity这个类的mp3Infos赋值 MusicPlayActivity.mp3Infos=mp3Infos; } public void init(){ this.mContext=this; mp3Infos = (ArrayList<Audio>) MediaUtils.getAudioList(this.mContext); adapter = new Mp3Adapter(this.mContext,mp3Infos); mp3ListView.setAdapter(adapter); //设置ListView Item点击监听器 mp3ListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent1 = new Intent(); //启动音乐播放界面 intent1 .setClass(MainActivity.this,MusicPlayActivity.class); //带值跳转,传入点击的歌曲位置 intent1.putExtra("position", position); startActivity(intent1); } }); }}
Mp3Adapter
public class Mp3Adapter extends BaseAdapter{ private Context context=null; private List<Audio> mp3Infos ; public Mp3Adapter(Context context){ this.context=context; } public Mp3Adapter(Context context, List<Audio> mp3Infos){ this.context=context; this.mp3Infos=mp3Infos; } public int getCount() { return mp3Infos.size(); } @Override public Object getItem(int position) { return mp3Infos.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if(mp3Infos==null){ return null; } if(convertView==null){ AppView view = new AppView(this.context); view.update(mp3Infos.get(position)); convertView=view; convertView.setTag(view); }else{ convertView= (View) convertView.getTag(); } return convertView; }}
AppView
public class AppView extends RelativeLayout{ ImageView mp3ImageView; TextView mp3ArtistName; TextView mp3Name; TextView mp3Duration; private Context context; public AppView(Context context){ super(context); this.context=context; init(context); } public void init(Context context){ this.context=context; View view = LayoutInflater.from(this.context).inflate(R.layout.listview_item_activity,null); mp3ImageView = (ImageView) view.findViewById(R.id.mpe3_imageView); mp3ArtistName = (TextView) view.findViewById(R.id.mp3_ArttistName); mp3Duration = (TextView) view.findViewById(R.id.mp3_dration); mp3Name = (TextView) view.findViewById(R.id.Mp3_name); addView(view); } public void update(Audio info){ mp3ArtistName.setText(info.getArtist()); mp3ImageView.setImageResource(R.mipmap.xxx); mp3Duration.setText(TimeTransform.secToTime(info.getDuration()/1000)); String x = info.getDisplayName(); if(x.length()>=15){ mp3Name.setText(x.subSequence(0,15)+"..."); }else mp3Name.setText(x); }}
上述代码是三个类,一个是Activity,一个是Adapter,一个是View
这三个类就是比较标准的实现显示一个ListView的操作吧,相信大家不会陌生,就是拿着我们返回的所有音频的List并且给他写好适配器,然后进行显示操作。当然代码中也有建立ListView的Item监听器。
现在我们实现了获取所有音频文件,然后将其显示在界面上,并且可以点击,现在点击后会需要跳转到播放界面。
上面代码中我已经写了跳转部分,现在我们实现以下需要跳转的类:
MusicPlayActivity
public class MusicPlayActivity extends Activity { @ViewInject(R.id.play_music) ImageView playMusicIv; @ViewInject(R.id.next_music) ImageView nextMusicIv; @ViewInject(R.id.pre_music) ImageView preMusicIv; @ViewInject(R.id.play_mode) ImageView playMusicModeIv; @ViewInject(R.id.music_name) TextView musicName; @ViewInject(R.id.artist_name) TextView artistName; @ViewInject(R.id.seekBar) SeekBar seekBar; @ViewInject(R.id.MusicCurrentTime) TextView MusicCurrentTime; @ViewInject(R.id.MusicTime) TextView MusicTime; int array[]; public static List<Audio> mp3Infos; boolean mBound=false;//控制绑定和开启Service boolean flag = false;//判断播放状态 Thread myThread; int position; private int playFlag = 0;//0 顺序,1 单曲, 2,随机 boolean playStatus=true;//控制线程的开启和关闭 Mp3Service mService; static String TAG="MusicPlayerActivity"; //Handler用于时时更新进度条 Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 0: double progress = msg.getData().getDouble("progress"); int currentTime = msg.getData().getInt("currentTime"); int max = seekBar.getMax(); int position = (int) (max * progress); //设置seekbar的实际位置 seekBar.setProgress(position); MusicCurrentTime.setText(TimeTransform.secToTime(currentTime/1000));//设置总时间显示 break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); x.view().inject(this); //获取到从MainActivity传过来的歌曲的位置 Intent intent = getIntent(); position = intent.getIntExtra("position",-1); playMusicModeIv.setImageDrawable(getResources().getDrawable(R.drawable.xunhuan_play_selector)); init(); } public void init(){ //通过Mp3Service类中的isRunning来判断服务是否处于开启状态,如果是的话,则需要关闭辅助在进行别的操作 if(Mp3Service.isRunning==true){ // unbindService(mConnection); stopService(new Intent(MusicPlayActivity.this, Mp3Service.class)); } //获取到Mp3的信息 mp3Infos = MainActivity.mp3Infos; //设置界面 musicName.setText(mp3Infos.get(position).getDisplayName()); MusicTime.setText(TimeTransform.secToTime(mp3Infos.get(position).getDuration()/1000)); artistName.setText(mp3Infos.get(position).getArtist()); Intent serviceIntent = new Intent(MusicPlayActivity.this,Mp3Service.class); serviceIntent.putExtra("position",position); if(mBound==false){ //开启和绑定服务 startService(serviceIntent); bindService(serviceIntent,mConnection,BIND_AUTO_CREATE); } myThread = new Thread(new UpdateProgress()); //拖动seekBar调整播放进度 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { int dest = seekBar.getProgress(); //seekbar的最大值 int max = seekBar.getMax(); //调用service调节播放进度 mService.setProgress(max, dest); } }); }//播放界面按钮的监听器 @Event(value={R.id.play_music,R.id.pre_music,R.id.next_music,R.id.play_mode}) private void setOnClickListener(View view){ switch (view.getId()) { case R.id.play_music: if (mBound && flag) { playMusicIv.setImageDrawable(getResources().getDrawable(R.drawable.playmusic_selector)); mService.pause(); flag = false; } else { playMusicIv.setImageDrawable(getResources().getDrawable(R.drawable.pause_music_selector)); mService.play(); flag = true; } break; case R.id.pre_music: position=position-1; if(position<0) position = mp3Infos.size() - 1; playStatus = false; mBound=false; unbindService(mConnection); init(); break; case R.id.next_music: switch (playFlag){ case 0: position = position+1; if(position>=mp3Infos.size()){ position=0; } break; case 1: position = position; Toast.makeText(MusicPlayActivity.this, "单曲播放中", Toast.LENGTH_SHORT).show(); break; case 2: int x=(int)(Math.random()*mp3Infos.size()-1); position=x; break; } playStatus = false; mBound=false; unbindService(mConnection); init(); break; case R.id.play_mode: switch (playFlag){ case 0: playFlag++; playMusicModeIv.setImageDrawable(getResources().getDrawable(R.drawable.danqu_play_selector)); break; case 1: playFlag++; playMusicModeIv.setImageDrawable(getResources().getDrawable(R.drawable.suiji_play_selector)); break; case 2: playFlag=0; playMusicModeIv.setImageDrawable(getResources().getDrawable(R.drawable.xunhuan_play_selector)); break; } break; } } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Mp3Service.MyBinder myBinder = (Mp3Service.MyBinder) service; mService = (Mp3Service) myBinder.getService(); mBound=true; playStatus=true; playMusicIv.setImageDrawable(getResources().getDrawable(R.drawable.pause_music_selector)); // mService.play(); myThread.start(); flag=true; } @Override public void onServiceDisconnected(ComponentName name) { mBound=false; } }; public void onDestroy() { //销毁activity时,要记得销毁线程 playStatus = false; super.onDestroy(); } //更新进度线程,单位时间获取Service返回的 private class UpdateProgress implements Runnable { Bundle data = new Bundle(); int millisecond=100; double progress; int currentTime; @Override public void run() { while(playStatus){ try { if(mBound){ Message msg = new Message(); data.clear(); progress = mService.getProgress(); currentTime=mService.getDurationTime(); msg.what=0; data.putDouble("progress",progress); data.putInt("currentTime",currentTime); msg.setData(data); mHandler.sendMessage(msg); } Thread.sleep(millisecond); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static boolean isServiceRunning(Context mContext, String className) { boolean isRunning = false; ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> serviceList= activityManager.getRunningServices(50); if (!(serviceList.size()>0)) { return false; } for (int i=0; i<serviceList.size(); i++) { String a =serviceList.get(i).service.getClassName(); if (serviceList.get(i).service.getClassName().equals(className) == true) { isRunning = true; break; } } return isRunning; }}
这个类就是音乐的播放界面,在这里我们首先获取到了由ListView点击后传过来的位置参数,然后在开启服务之前首要去判断这个服务是否处于正在运行状态,如果是的话,就首先关闭服务,然后在进行别的操作,因为我们听歌的时候,切歌,必须先关闭前一首歌,再去播放另一首,然后isRunning就是Service里的属性,从这里判断是否服务正在进行。
当然更新进度也的放在这个类里,具体思想就是我们通过Thread和Handler去实现,线程不断从Service中获取歌曲播放进度,然后send给Handler,在由Handler去进行seekBar的更新。
下面就说一下我们比较核心的Service类,这个类就是后台播放歌曲的实现。
public class Mp3Service extends Service { int position; MediaPlayer mediaPlayer;//MediaPlayer 对象 //判断是否服务开启了 public static boolean isRunning=false; //构建Binder,通过binder和Activity进行交互 MyBinder myBinder= new MyBinder(); public static List<Audio> mp3Infos; public IBinder onBind(Intent intent) { return myBinder; } public class MyBinder extends Binder{ //获取到Service public Service getService(){ return Mp3Service.this; } } @Override public void onCreate() { super.onCreate(); } @Override //开启服务 public int onStartCommand(Intent intent, int flags, int startId) { isRunning=true; //取到带值跳转传过来的歌曲位置参数 position=intent.getIntExtra("position",-1); mp3Infos= MusicPlayActivity.mp3Infos; init(); return Service.START_NOT_STICKY; } //初始化音乐MediaPlayer public void init(){ mediaPlayer = new MediaPlayer(); //拿到歌曲位置进行初始化 try { mediaPlayer.reset(); mediaPlayer.setDataSource(mp3Infos.get(position).getPath()); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.prepare(); mediaPlayer.start(); } catch (IOException e) { e.printStackTrace(); } } //获取进度 public double getProgress(){ int position = mediaPlayer.getCurrentPosition(); int time = mediaPlayer.getDuration(); double progress = (double)position / (double)time; return progress; } //获取播放时间 public int getDurationTime(){ return mediaPlayer.getCurrentPosition(); } //设置进度 public void setProgress(int max , int dest){ int time = mediaPlayer.getDuration(); mediaPlayer.seekTo(time*dest/max); } public void play(){ if(mediaPlayer!=null){ mediaPlayer.start(); } } public void pause(){ if(mediaPlayer!=null&&mediaPlayer.isPlaying()){ mediaPlayer.pause(); } } @Override //服务销毁 public void onDestroy() { if(mediaPlayer!=null&&mediaPlayer.isPlaying()){ mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; isRunning=false; } super.onDestroy(); }}
这个Mp3Service类就是实现歌曲后台播放的类,在这里我们首先接收到了由MusicPlayActivity传入的歌曲位置,当MusicPlayActivity类开启和绑定服务之后,我服务类进行了初始化,首先实例化了mediaPlayer并且设置好了他的播放设置。然后这个类里写了歌曲的暂停和播放功能。
在这里要知道什么时候服务开启,什么时候服务关闭,然后在合适的地方设置好isRunning的值。
这个类中我们也写了获取进度和设置进度的方法,获取进度就是为了方便MusicPlayActivity去时时获取进度进行界面进度条更新,设置进度就是为了拖动bar的时候能够调节进度。
当然要完善销毁方法,释放Mediaplayer的一系列操作。
好了差不多就是这样,然后还有用到的工具类,我也复制在底下:
public class TimeTransform { public static String secToTime(int time) { String timeStr = null; int hour = 0; int minute = 0; int second = 0; if (time <= 0) return "00:00"; else { minute = time / 60; if (minute < 60) { second = time % 60; timeStr = unitFormat(minute) + ":" + unitFormat(second); } else { hour = minute / 60; if (hour > 99) return "99:59:59"; minute = minute % 60; second = time - hour * 3600 - minute * 60; timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second); } } return timeStr; } public static String unitFormat(int i) { String retStr = null; if (i >= 0 && i < 10) retStr = "0" + Integer.toString(i); else retStr = "" + i; return retStr; }}
这个类的作用就是传入一个秒数,然后他会返回一个标准化时间输出的字符串,例如传入xxxxxx,他会 返回xx:xx。因为音乐播放界面的时间应该是标准化的。
要记住Mediaplayer获取到的时间都是以毫秒为单位的,所以传入的时候必须转化成秒在传入。xxxx/1000
主要进行传值我们都用的是带值跳转和static 声明存放歌曲的List,这样他们就可以共享这些数据了。布局文件我就不贴出来了,这个可以自己实现,大体的思路就是这样,可能有些太长了,因为我把所有的代码基本上都放出来了。
好了,基本就这么多,这篇文章主要为了记录学习过程,还有很多不完善和不规范的地方,请大神勿喷。作为新手仅仅是想做一点学习记录而已。
- Android Mp3播放器,支持Service后台播放
- android的service学习案例------自己做的音乐播放器,让service后台播放mp3文件(是burning.mp3哦!)
- android mp3播放器
- Android 音乐播放器,Service后台管理播放
- android Service后台播放音乐
- android开发之MediaPlayer+Service MP3播放器
- Android应用开发--MP3音乐播放器Service实现
- Android应用开发--MP3音乐播放器Service实现
- Android应用开发--MP3音乐播放器Service实现
- android开发之MediaPlayer+Service MP3播放器
- Service播放MP3
- android MP3播放器(支持歌词滚动等功能)
- Android学习-MP3播放器
- Android MP3播放器MediaPlayer
- android实现MP3播放器
- android音乐后台播放需要用到Service
- Android通过Service实现音乐后台播放
- android组件Service控制后台音乐播放
- 郝斌的C语言基础 109 break和return的区别
- Volley 图片加载相关源码解析
- 快速排序的递归和非递归实现
- linux内核札记
- yii2使用时间插件
- Android Mp3播放器,支持Service后台播放
- 【转】Spring静态资源访问
- 【微信小程序】:小程序,新场景
- MySql创建数据库
- 机器学习资源大全中文版
- 前端easyUI时间控件datebox小bug问题修复
- 求两圆相交的面积
- Android DiskLruCache 源码解析 硬盘缓存的绝佳方案
- HDU1039 Easier Done Than Said?