实验题目:后台服务
- 实验目的
1)了解什么是Service;
2)掌握Service 中启动服务于绑定服务的区别;
3)通过Service 实现音乐播放的实时更新。
- 实验内容
1)制作一个简单的音乐播放器,拥有播放,暂停,停止功能,播放res/raw文件夹中的mp3 资源文件。
2)、实现两个简单的按钮功能,一个是启动一个自定义的自动计数的Service,另一个是关闭该Service。
- 程序设计思想(流程图,或算法思想或设计方案等)
这是一个Android应用程序,包含两个服务:音乐服务和计数服务。音乐服务允许用户播放,暂停和停止音乐,计数服务可以启动并停止计数器并在活动中显示当前计数。
音乐服务(MusicService)继承自Service类。它包含一个MediaPlayer对象,该对象用于播放音乐。该类重写了onCreate(),onStartCommand()和onDestroy()方法。onCreate()方法初始化MediaPlayer对象。onStartCommand()方法接收Intent对象,该对象包含指示要执行的操作的参数,例如播放,停止或暂停音乐。根据Intent中的参数,音乐服务执行相应的操作。onDestroy()方法停止MediaPlayer对象并释放相关资源。
计数服务(CountService)继承自Service类。它包含一个计数器和一个CountCallback接口。该类重写了onBind(),onStartCommand()和onDestroy()方法。onBind()方法返回一个Binder对象,该对象允许与服务进行通信。onStartCommand()方法接收Intent对象,该对象包含指示要执行的操作的参数,例如启动或停止计数器。根据Intent中的参数,计数服务执行相应的操作。onDestroy()方法停止计数器并释放相关资源。计数器使用一个线程不断增加计数器变量,同时在CountCallback接口上通知调用者新的计数器值。该计数器变量也可以在CountService类中通过调用getCount()方法获取。
MainActivity类是应用程序的主要活动。它显示了四个按钮,其中三个用于控制音乐服务,另一个用于控制计数服务。它还包括一个TextView,用于显示当前计数。在onCreate()方法中,MainActivity类创建了一个Intent对象,用于启动CountService。该类还使用bindService()方法绑定CountService,并在CountService的onServiceConnected()方法中设置CountCallback接口。每当计数器增加时,CountService通过该接口通知MainActivity类并更新计数器的文本视图。此外,MainActivity类还处理音乐服务的按钮点击事件。
- 源代码(主要源代码)
CountService.java
package com.example.musicservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
public class CountService extends Service {
private int count = 0;
private CountCallback countCallback;
private final IBinder mBinder = new LocalBinder();
private volatile boolean isRunning = false;
public class LocalBinder extends Binder {
CountService getService() {
return CountService.this;
}
}
public IBinder onBind(Intent intent) {
return mBinder;
}
public void setCountCallback(CountCallback countCallback) {
this.countCallback = countCallback;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
String action = intent.getStringExtra("action");
if (action != null) {
if (action.equals("start")) {
startCount();
} else if (action.equals("stop")) {
stopCount();
}
}
}
return START_STICKY;
}
private void startCount() {
if (!isRunning) {
isRunning = true;
new Thread(new Runnable() {
@Override
public void run() {
while (isRunning) {
count++;
Log.d("CountService", "count = " + count);
if (countCallback != null) {
countCallback.updateCount(count);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
private void stopCount() {
Log.d("CountService", "stop!!!!!!!!");
isRunning = false;
}
public interface CountCallback {
void updateCount(int count);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("CountService", "onDestroy()");
}
}
MainActivity.java
package com.example.musicservice;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.os.IBinder;
import android.content.Context;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, CountService.CountCallback {
private Button btn_main_play;
private Button btn_main_stop;
private Button btn_main_pause;
private Button btn_main_exit;
private Button btn_count_start;
private Button btn_count_stop;
private CountService countService;
private TextView tvCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取4个按钮
btn_main_play = findViewById(R.id.btn_main_play);
btn_main_stop = findViewById(R.id.btn_main_stop);
btn_main_pause = findViewById(R.id.btn_main_pause);
btn_main_exit = findViewById(R.id.btn_main_exit);
btn_main_play.setOnClickListener(this);
btn_main_stop.setOnClickListener(this);
btn_main_pause.setOnClickListener(this);
btn_main_exit.setOnClickListener(this);
btn_count_start = findViewById(R.id.btn_count_start);
btn_count_stop = findViewById(R.id.btn_count_stop);
tvCount=findViewById(R.id.tvCount);
Intent intentCount = new Intent(this, CountService.class);
btn_count_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intentCount.putExtra("action", "start");
startService(intentCount);
}
});
btn_count_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intentCount.putExtra("action", "stop");
startService(intentCount);
stopService(intentCount);
}
});
Intent intentCount2 = new Intent(this, CountService.class);
startService(intentCount2);
bindService(intentCount2, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
CountService.LocalBinder binder = (CountService.LocalBinder) service;
countService = binder.getService();
countService.setCountCallback(MainActivity.this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
countService = null;
}
}, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
//新建intent,传入不同的操作,通过intent的参数来判断是什么操作
Intent intent = new Intent(this,MusicService.class);
//判断点击了哪个按钮
//music
if(v== btn_main_play){
//播放
intent.putExtra("action","play");
//启动服务
startService(intent);
}else if(v == btn_main_stop){
//停止播放
intent.putExtra("action","stop");
//启动服务
startService(intent);
}else if(v == btn_main_pause){
//暂停音乐
intent.putExtra("action","pause");
//启动服务
startService(intent);
}else if(v ==btn_main_exit ){
//停止音乐--停止服务
stopService(intent);
finish();
}
//count
}
@Override
public void updateCount(int count) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tvCount.setText("计数: " + count);
}
});
}
}
MusicService.java
package com.example.musicservice;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import androidx.annotation.Nullable;
/*//播放
MediaPlayer player = MediaPlayer.create(context, R.raw.tmp);
player.start()
//暂停(再播放当前继续)
player.pause()
//停止(再播放重头开始)
player.stop();
player.reset();
player.release();*/
public class MusicService extends Service {
private MediaPlayer player;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*
* 通过传进来的intent来判断播放--暂停--停止
* @Override
public void onClick(View v) {
//新建intent,传入不同的操作,通过intent的参数来判断是什么操作
Intent intent = new Intent(this,MusicService.class);
//判断点击了哪个按钮
if(v== btn_main_play){
//播放
intent.putExtra("action","play");
//启动服务
startService(intent);
}else if(v == btn_main_stop){
//停止播放
intent.putExtra("action","stop");
//启动服务
startService(intent);
}else if(v == btn_main_pause){
//暂停音乐
intent.putExtra("action","pause");
//启动服务
startService(intent);
}else if(v ==btn_main_exit ){
//停止音乐--停止服务
stopService(intent);
finish();
}
}
* */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getStringExtra("action");
if("play".equals(action)){
palayMusic();
}else if("pause".equals(action)){
pauseMusic();
}else if("stop".equals(action)){
stopMusic();
}
return super.onStartCommand(intent, flags, startId);
}
//停止播放
private void stopMusic() {
if(player!=null){
player.stop();//停止
player.reset();//重置
player.release();//释放资源
player = null;//赋空
}
}
/*暂停播放*/
private void pauseMusic() {
if(player!=null && player.isPlaying()){
player.pause();
}
}
//播放音乐
private void palayMusic() {
if(player == null){
player = MediaPlayer.create(this, R.raw.water_hander);
}
player.start();
}
//activity退出时,停止音乐
@Override
public void onDestroy() {
super.onDestroy();
stopMusic();
}
}
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:ignore="MissingConstraints">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="音乐播放器"
></TextView>
<Button
android:id="@+id/btn_main_play"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="play" />
<Button
android:id="@+id/btn_main_stop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="stop" />
<Button
android:id="@+id/btn_main_pause"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="pause" />
<Button
android:id="@+id/btn_main_exit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="exit" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="计数器"
></TextView>
<Button
android:id="@+id/btn_count_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Count" />
<Button
android:id="@+id/btn_count_stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Count" />
<TextView
android:id="@+id/tvCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="计数: "
android:textSize="48sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
- 测试与运行
- 在调试程序的过程中遇到什么问题,是如何解决的?
添加服务连接状态的判断
在 MainActivity 中使用 countService 时,应该添加服务连接状态的判断,以避免空指针异常。可以在 CountService 中添加一个 isConnected() 方法,用于检查服务是否连接。
在 MainActivity 中使用 CountService 的回调方法时,应该先检查 countService 是否为空
当服务未连接时,countService 会为空,因此在使用 CountService 的回调方法时,应该先检查 countService 是否为空,以避免空指针异常。可以在 MainActivity 中添加一个 isCountServiceConnected() 方法,用于检查服务是否连接。
测试数据及运行结果。(无测试数据的,直接记录运行结果)
这个程序集合了音乐播放器和计数器,可以播放音乐停止暂停和退出,有一个整数计数器 count,它每秒自增 1。
运行截图:
- 总结
这个实验主要分为两部分:音乐播放器和计数器应用程序。在音乐播放器应用程序中,通过使用Service类创建一个后台服务,可以在后台播放音乐。Service类可以被启动并运行在后台,即使应用程序的Activity已经退出,Service仍可以继续运行。
在本实验中,创建了一个Media Player Service,该服务通过使用MediaPlayer类播放音乐文件。在该服务中,定义了多个方法来操作音乐播放器,例如开始、暂停、停止和跳转到指定时间等。使用Intent可以将这些操作从Activity发送到服务,以控制音乐播放器的状态。
在计数器应用程序中,同样使用了Service类来创建一个后台服务,该服务可以在后台计数。这个服务还实现了一个Binder类,以允许Activity与服务之间进行通信。
在本实验中,通过创建一个Counter Service和一个Counter Binder类,可以在服务中计数,并通过Binder类提供方法来允许Activity对计数器进行增加、减少和获取计数器值等操作。这些方法可以通过Intent发送到服务来实现。