Android开发--Lesson08--服务Service
Service的定义
Service(服务)就是Android里的一个“后台工作者”,它专门用来处理那些不需要用户盯着看的任务。它可以一直运行,即使你切换到其他应用或者关掉屏幕,它也不会停下来。
它的特点很简单:
- 没界面:你看不到它,但它确实在干活。
- 两种模式:
- 一种是自己默默运行,比如播放音乐、下载文件。
- 另一种是可以跟其他组件互动,比如给Activity提供数据。
- 可能会被系统停掉:如果手机内存不够了,系统会优先关掉不重要的服务,所以重要任务需要特别标记为“前台服务”。
- 安全可控:你可以设置权限,只让特定的应用使用它。
总之,Service就是一个能在后台长时间工作的工具,适合处理耗时任务或者需要持续运行的功能。
服务的作用
-
后台操作:即使应用程序不在前台显示,Service也能继续在后台执行任务,比如播放音乐、文件下载等。
-
跨进程通信(IPC):通过AIDL(Android Interface Definition Language),Service可以实现不同应用之间的通信。
-
绑定服务:允许其他组件(如Activity)与Service进行交互,甚至可以通过绑定的方式获取Service返回的结果。
-
处理周期性任务或定时任务:例如,在特定时间点执行某些操作或者定期更新数据。
-
执行网络操作:当需要进行网络请求时,使用Service可以在后台完成这些操作,不影响用户的其他操作。
服务的特点
-
独立于UI线程:Service默认运行在主线程中,但其主要设计目的是为了执行不需要用户界面的长期操作。如果需要执行耗时操作,应该在Service内部创建新的线程来避免阻塞主线程。
-
生命周期管理:Service具有比Activity更简单的生命周期,主要包括
onCreate()
、onStartCommand()
、onDestroy()
等方法。对于绑定服务,还有onBind()
方法用于客户端和服务端的连接。 -
两种启动方式:
- Started Service(启动状态的服务):当一个组件(如Activity)通过调用
startService()
启动服务时,该服务即处于“启动”状态。这种服务可以无限期地运行,并且必须显式调用stopSelf()
或由外部组件调用stopService()
才能停止。 - Bound Service(绑定状态的服务):当一个组件通过调用
bindService()
与服务绑定时,该服务即处于“绑定”状态。这种服务允许其他组件与之交互,交换数据等。所有绑定到服务的组件断开连接后,系统会销毁该服务。
- Started Service(启动状态的服务):当一个组件(如Activity)通过调用
-
优先级较低:当系统资源紧张时,可能会销毁后台Service以回收内存。为避免这种情况影响用户体验,可以将Service设置为前台服务,通过调用
startForeground()
方法并在通知栏显示持续的通知。 -
安全性:可以通过声明权限限制哪些应用可以与你的Service交互,保护敏感数据不被未授权的应用访问。
创建一个服务类
新建一个服务类之后,可以就会弹窗出服务的默认配置,直接默认就好
点击Finsh之后就可以获得一个继承于 android.app.Service包下面的Service的抽象类,此类就是提供了相关的后台服务的相关方法
生成的service类会在android的清单文件中完成注册
<service android:name=".MyService" android:enabled="true" android:exported="true" />
Service的相关方法
一个基本的service类会重写父类的以下方法:
import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class MyService extends Service { // 构造方法 public MyService() { } // 当客户端绑定到服务时调用 @Override public IBinder onBind(Intent intent) { Log.i("MyService","myBinder调用"); return new myBinder(); } // 当客户端解除绑定到服务时调用 @Override public boolean onUnbind(Intent intent) { Log.i("MyService","onUnbind调用"); return super.onUnbind(intent); } // 在服务创建时调用 @Override public void onCreate() { super.onCreate(); Log.i("MyService","onCreate启动"); } // 在服务启动时调用 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("MyService","onStartCommand启动"); return super.onStartCommand(intent, flags, startId); } // 在服务销毁时调用 @Override public void onDestroy() { super.onDestroy(); Log.i("MyService","onDestroy启动"); } // 自定义方法 public void myMethod(){ Log.i("MyService","myMethod方法"); } // 继承Binder类,用于在客户端和服务之间进行通信 class myBinder extends Binder{ // 调用自定义方法 public void callMethod(){ myMethod(); } } }
首先是构造方法,这是用来初始化服务的,肯定是必须拥有的
然后是onBind方法和onUnBind方法,这两个方法一个是用于绑定服务启动之后需要做的事情,需要一个Binder类的对象来描述服务需要后台运行的事情,而onUnBind方法则是取消绑定,也就是取消当前服务在做的事情
onCreate方法只有服务第一次进行调用,也就是一个服务的生命周期只会被调用一次,如果是bind后,然后unBind,再次bind看似换了一个服务,但其实onCreate不会被调用,换而言之就是没有新建服务
onStartCommand方法也是在服务启动的时候会被调用,但是和onCreate不同的是每次startService()方法调用时,都会运行onStartCommand,而onCreate则不会
onDestory方法则是在服务销毁的时候会被调用
后面的方法和内部类则是因为onBind方法需要的,故而简单测试,打印相关的调用信息看看
自定义方法就输出一个日志,而内部类则是提供一个方法来调用自定义方法,后面的自定义方法可以作为服务需要处理的具体的事情而修改
测试相关方法的注册状态
测试service的生命周期可以使用按钮来确定每次绑定一个操作(开启服务和关闭服务),然后查看控制台的日志就可以清楚的看到每个方法调用的时机了
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/bt1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动"/> <Button android:id="@+id/bt2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="暂停"/> </LinearLayout>
页面代码就两个按钮,负责启动服务和关闭服务,接着就是上下文对象MainActivity来负责启动和暂停服务:
在绑定Service之前,我们需要学习一个新的接口:ServiceConnection ;使用它可以描述当前上下文和service的连接状态,并且可以通过内置的一些方法定义当和service产生连接或者断开连接后需要执行那些操作
这也是MainActivity上下文对象连接bindService的必要参数对象
class MyConnection implements ServiceConnection{ //当服务连接成功时调用 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("MyService","onServiceConnected"); //将IBinder转换为MyService.myBinder类型 MyService.myBinder binder = (MyService.myBinder) service; //调用MyService中的callMethod方法 binder.callMethod(); } //当服务断开连接时调用 @Override public void onServiceDisconnected(ComponentName name) { //异常终端 Log.i("MyService","onServiceDisconnected"); } }
当连接成功时的方法中,IBinder的参数值就是我们定义的MyService类中的内部类binder,它会有一个绑定方法用来定义服务执行需要做什么
在上下文对象中可以将其取出,然后调用其的方法,实现在上下文启动服务并执行其方法
接下来就是绑定具体的服务了,绑定服务也需要Intent对象,来指明意图,这里的意图就是当前上下文和service类的具体连接
//获取按钮 Button bt1 = findViewById(R.id.bt1); Button bt2 = findViewById(R.id.bt2); //为按钮1添加点击事件 bt1.setOnClickListener(v->{ //添加意图 Intent intent = new Intent(MainActivity.this,MyService.class); //启动服务 startService(intent); //如果连接为空 if (connection==null){ connection = new MyConnection(); //绑定服务,并执行其具体的方法 bindService(intent,connection,BIND_AUTO_CREATE); } }); //为按钮2添加点击事件 bt2.setOnClickListener(v->{ Intent intent = new Intent(MainActivity.this,MyService.class); //停止服务 stopService(intent); //如果连接不为空 if (connection!=null){ //解除绑定服务 unbindService(connection); //将连接置为空 connection=null; } });
上面的代码就是启动服务和停止服务,并且为服务启动后的连接执行方法
bindService(intent,connection,BIND_AUTO_CREATE); 实现当前上下文和service的绑定,第一个intent参数就是意图,它创建的时候就表明了当前上下文和那个service产生连接,connection就是连接服务需要的对象,可以指定当前上下文和服务连接成功或终止后应该做什么,第三个参数就是常量标识绑定的模式为自自动绑定,也就是intent指定的服务没有启动则自动创建服务
以下是完整的MainActivity的代码:
public class MainActivity extends AppCompatActivity { private MyConnection connection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); //获取按钮 Button bt1 = findViewById(R.id.bt1); Button bt2 = findViewById(R.id.bt2); //为按钮1添加点击事件 bt1.setOnClickListener(v->{ //添加意图 Intent intent = new Intent(MainActivity.this,MyService.class); //启动服务 startService(intent); //如果连接为空 if (connection==null){ connection = new MyConnection(); //绑定服务,并执行其具体的方法 bindService(intent,connection,BIND_AUTO_CREATE); } }); //为按钮2添加点击事件 bt2.setOnClickListener(v->{ Intent intent = new Intent(MainActivity.this,MyService.class); //停止服务 stopService(intent); //如果连接不为空 if (connection!=null){ //解除绑定服务 unbindService(connection); //将连接置为空 connection=null; } }); } class MyConnection implements ServiceConnection{ //当服务连接成功时调用 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("MyService","onServiceConnected"); //将IBinder转换为MyService.myBinder类型 MyService.myBinder binder = (MyService.myBinder) service; //调用MyService中的callMethod方法 binder.callMethod(); } //当服务断开连接时调用 @Override public void onServiceDisconnected(ComponentName name) { //异常终端 Log.i("MyService","onServiceDisconnected"); } } @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }
测试结果:
启动服务按钮点击之后:
停止服务按钮点击:
音乐播放案例
引入Mp3资源
需要在res包下面新建一个raw包,用于存放需要播放的音乐:
新建一个服务项目
此服务需要提供三个方法,分别需要指定媒体元素的开始,暂停,和停止
musicService:
import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; public class MusicService extends Service { //声明一个MediaPlayer对象 private MediaPlayer mediaPlayer; //构造方法 public MusicService() { } //绑定服务 @Override public IBinder onBind(Intent intent) { return new MusicBinder(); } //声明一个内部类,继承Binder class MusicBinder extends Binder{ //播放 public void play(){ //如果mediaPlayer为空,则创建MediaPlayer对象并开始播放 if (mediaPlayer == null){ mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.gentlemen); mediaPlayer.start(); }else { //如果mediaPlayer不为空,且正在播放,则继续播放 if (!mediaPlayer.isPlaying()){ mediaPlayer.start(); } } } //暂停 public void pause(){ //如果mediaPlayer不为空,且正在播放,则暂停播放 if (mediaPlayer !=null && mediaPlayer.isPlaying()){ mediaPlayer.pause(); } } //结束 public void stop(){ if (mediaPlayer !=null){ mediaPlayer.stop(); mediaPlayer =null; } } } @Override public void onDestroy() { super.onDestroy(); if (mediaPlayer !=null){ //停止播放 mediaPlayer.stop(); //释放资源 mediaPlayer.release(); mediaPlayer =null; } } }
上面的service的实现代码其实和第二节讲解是一样的,这个时候就是在Binder类中指定了服务需要实现的三个方法,即开始音乐,暂停音乐和关闭音乐
界面XML文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/bt1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放"/> <Button android:id="@+id/bt2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="暂停"/> <Button android:id="@+id/bt3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止"/> </LinearLayout>
界面也只有三个按钮对应三个事件
接着就是MainActivity了,需要定义的就是具体的事件绑定:
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//声明MusicConnecet对象
private MusicConnecet musicConnecet;
//声明MusicService.MusicBinder对象
private MusicService.MusicBinder musicBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//启用EdgeToEdge
EdgeToEdge.enable(this);
//设置布局
setContentView(R.layout.activity_main);
//设置窗口边距
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
//获取按钮
Button bt1 = findViewById(R.id.bt1);
Button bt2 = findViewById(R.id.bt2);
Button bt3 = findViewById(R.id.bt3);
//设置按钮点击事件
bt1.setOnClickListener(this);
bt2.setOnClickListener(this);
bt3.setOnClickListener(this);
//创建Intent对象,指定要启动的服务
Intent intent = new Intent(MainActivity.this,MusicService.class);
//创建MusicConnecet对象
musicConnecet = new MusicConnecet();
//绑定服务
bindService(intent,musicConnecet,BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
//根据按钮id执行不同的操作
if (v.getId()==R.id.bt1){
musicBinder.play();
}
if(v.getId()==R.id.bt2){
musicBinder.pause();
}
if (v.getId()==R.id.bt3){
musicBinder.stop();
}
}
//创建MusicConnecet类,实现ServiceConnection接口
class MusicConnecet implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//将service转换为MusicBinder对象
musicBinder=(MusicService.MusicBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除服务绑定
unbindService(musicConnecet);
}
}
最后实现的效果就是完成了音乐播放,暂停和停止
-----END----