【Android】Service+Seek Bar实现后台音乐播放器小呆萌

时间:2021-08-22 10:14:32

1. Android四大组件之Service

相信大家对于身为四大组件之一的Service一定很熟悉。Service可以运行在后台来处理一些耗时操作,也可以用来维持一些需要长期运行的任务,甚至可以在用户退出程序是仍然运行在后台中。作为一个初学者,我可以联想到很多关于Service的应用场景。比如:闹钟、音乐播放器等等。
今天我就来给大家展示一个关于音乐播放器退出时后台播放的小Demo。

2.Android控件SeekBar

SeekBar相当与ProgressBar的一个扩展,SeekBar的主要应用场景是在多媒体播放器当中,用来控制播放的进度;也用作一些范围型参数的选择器,比如我们常见的音量调节和屏幕亮度调节。
【Android】Service+Seek Bar实现后台音乐播放器小呆萌

3.Demo的具体实现

1.创建一个Service

首先进入到我们的工程,创建一个新的package为Service,再到其中创建一个MusicBindService类。
为了实现音乐播放的一些功能我们需要在这个类中新建一个MusicController方法,继承Binder类实现对内部组件的绑定。
下面就是MusicBindService的具体实现。

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

import com.jinni.servicedemo.R;

public class MusicBindService extends Service {

private MediaPlayer mPlayer;

public MusicBindService() {
}

/*
* 绑定服务的实现流程:
* 1.服务 onCreate, onBind, onDestroy 方法
* 2.onBind 方法需要返回一个 IBinder 对象
* 3.如果 Activity 绑定,Activity 就可以取到 IBinder 对象,可以直接调用对象的方法
*/


// 相同应用内部不同组件绑定,可以使用内部类以及Binder对象来返回。
public class MusicController extends Binder {
public void play() {
mPlayer.start();//开启音乐
}

public void pause() {
mPlayer.pause();//暂停音乐
}

public long getMusicDuration() {
return mPlayer.getDuration();//获取文件的总长度
}

public long getPosition() {
return mPlayer.getCurrentPosition();//获取当前播放进度
}

public void setPosition (int position) {
mPlayer.seekTo(position);//重新设定播放进度
}
}

/**
* 当绑定服务的时候,自动回调这个方法
* 返回的对象可以直接操作Service内部的内容
* @param intent
* @return
*/

@Override
public IBinder onBind(Intent intent) {
return new MusicController();
}

@Override
public void onCreate() {
super.onCreate();
mPlayer = MediaPlayer.create(this, R.raw.nobody);
}

/**
* 任意一次unbindService()方法,都会触发这个方法
* 用于释放一些绑定时使用的资源
* @param intent
* @return
*/

@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}

@Override
public void onDestroy() {
if (mPlayer.isPlaying()) {
mPlayer.stop();
}
mPlayer.release();
mPlayer = null;
super.onDestroy();
}
}

2. MusicActivity的实现

首先进入我们的布局文件当中,编辑一个简单的音乐播放器界面。当中不仅加入了SeekBar同时还加入了一个ProgressBar作为对比。STOP按钮是用来停止服务的,点击之后退出程序将不会继续在后台播放音乐。在编写代码之前,还需要在res文件夹中新建一个raw文件夹,并且将我们的音乐文件拷贝到里面。
【Android】Service+Seek Bar实现后台音乐播放器小呆萌【Android】Service+Seek Bar实现后台音乐播放器小呆萌

1.activity_music.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.jinni.servicedemo.MusicActivity">


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnMusicPlay"
android:text="Play"/>


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnMusicPause"
android:text="Pause"/>


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnStopService"
android:text="Stop"/>


<TextSwitcher
android:id="@+id/text_switcher"
android:layout_width="match_parent"
android:layout_height="50dp">


<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>


</TextSwitcher>

<ProgressBar
android:id="@+id/music_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>


<SeekBar
android:id="@+id/music_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

</LinearLayout>

2.MusicActivity.java

package com.jinni.servicedemo;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextSwitcher;
import com.jinni.servicedemo.services.MusicBindService;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MusicActivity extends AppCompatActivity implements Runnable, ServiceConnection, SeekBar.OnSeekBarChangeListener {

private static final String TAG = MusicActivity.class.getSimpleName();
private MusicBindService.MusicController mMusicController;
private ProgressBar mProgressBar;
private boolean running;
private TextSwitcher mSwitcher;
private SeekBar mSeekBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music);
mSeekBar = (SeekBar) findViewById(R.id.music_seek_bar);
mSeekBar.setOnSeekBarChangeListener(this);
mProgressBar = ((ProgressBar) findViewById(R.id.music_progress));
mSwitcher = (TextSwitcher) findViewById(R.id.text_switcher);
mSwitcher.setInAnimation(this, android.R.anim.fade_in);
mSwitcher.setOutAnimation(this, android.R.anim.fade_out);
Intent intent = new Intent(this, MusicBindService.class);
//增加StartService,来增加后台播放功能
startService(intent);
// 绑定服务,使用context来绑定
// 那个界面需要绑定 就用哪个 Activity
// 参数1:Intent 代表需要绑定哪一个Service
// 参数2:ServiceConnection 回调接口,可以接收到Service连接成功和断开的回调,成功就可以取到对象。
// 绑定服务 参数2就是服务和指定的对象绑定在一起
bindService(intent, this, BIND_AUTO_CREATE);

}

@Override
protected void onStart() {
super.onStart();
Thread thread = new Thread(this);
thread.start();
}

@Override
protected void onStop() {
running = false;
super.onStop();
}

@Override
protected void onDestroy() {
// 解除绑定
unbindService(this);
super.onDestroy();
}

//------------------------

@Override
public void run() {
running = true;
try {
while (running) {
if (mMusicController != null) {
long musicDuration = mMusicController.getMusicDuration();
final long position = mMusicController.getPosition();
final Date dateTotal = new Date(musicDuration);
final SimpleDateFormat sb = new SimpleDateFormat("mm:ss");
mProgressBar.setMax(((int) musicDuration));
mProgressBar.setProgress(((int) position));
mSeekBar.setMax((int) musicDuration);
mSeekBar.setProgress((int) position);
mSwitcher.post(
new Runnable() {
@Override
public void run() {
Date date = new Date(position);
String time = sb.format(date) + "/" + sb.format(dateTotal);
mSwitcher.setCurrentText(time);
}
}
);
}

Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//------------------------
public void btnMusicPlay(View view) {
if (mMusicController != null) {
mMusicController.play();
}
}

public void btnMusicPause(View view) {
if (mMusicController != null) {
mMusicController.pause();
}
}

//-----------------------------
//服务绑定与解除绑定的回调

/**
* 当服务与当前绑定对象,绑定成功,服务onBind方法调用并且返回之后
* 回调给这个方法
*
* @param name
* @param service IBinder 就是服务 onBind 返回的对象
*/

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMusicController = ((MusicBindService.MusicController) service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
mMusicController = null;
}

public void btnStopService(View view) {
Intent intent = new Intent(this, MusicBindService.class);
stopService(intent);
}

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mMusicController.setPosition(seekBar.getProgress());
}
}

第一次写博客,写得好希望大家可以点个赞,如果不好也希望大家多多指点,谢谢大家!