前言
在Android系统中,Service
是一个用来执行长时间运行的操作而不提供用户界面的应用组件。它可以在后台执行任务,即使用户切换到其他应用也不会被中断。
Service
在Android中主要用于在后台执行长时间运行的任务,例如播放音乐、执行文件下载、处理网络请求等。尽管Service
运行在后台,但它仍然属于应用程序的一部分,因此不会影响用户对前台应用的交互。
Service的类型
在Android中,Service
可以分为三种类型:
前台Service
前台Service
是用户可见的服务,它会在系统的通知区域显示一个通知,表示正在运行的服务。这种类型的Service
被视为用户当前活跃的一部分,因此系统不太可能在内存不足时终止它。前台Service
常用于音乐播放或GPS导航等需要用户明确知道并持续运行的功能。
后台Service
后台Service
在用户不可见的情况下执行操作,通常用于执行不需要与用户交互的任务。但从Android Oreo(API 级别 26)开始,后台服务的使用受到了严格限制,以减少对系统性能的影响和提升电池寿命。
绑定Service
绑定Service
是一种允许应用组件(如Activity)绑定到Service
并与之交互的服务。组件可以发送请求、接收响应,甚至进行进程间通信(IPC)。绑定的Service
只在其他应用组件与其绑定时运行,不会无限期运行。
Service的实现
实现Service
主要涉及以下几个步骤:
-
定义Service: 在Java或Kotlin文件中扩展
Service
基类,并重写其生命周期方法如onCreate()
,onStartCommand()
,onBind()
, 和onDestroy()
。 -
配置Manifest: 在
AndroidManifest.xml
中声明Service
,并设置适当的权限和属性。 -
启动和绑定Service: 通过
startService(Intent)
方法启动服务,或者使用bindService(Intent, ServiceConnection, int)
绑定服务。前者适用于执行单一操作或执行不返回结果的操作,后者适用于与服务进行交互。
如下是一个例子:创建一个服务,允许用户在后台播放音乐,即使他们离开了应用,音乐仍然可以继续播放:
public class MusicService extends Service {
private MediaPlayer mediaPlayer;
@Override
public void onCreate() {
super.onCreate();
// 初始化 MediaPlayer 对象
mediaPlayer = MediaPlayer.create(this, R.raw.sample_music);
mediaPlayer.setLooping(true); // 设置音乐循环播放
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mediaPlayer.start(); // 开始播放音乐
return START_STICKY; // 系统如果终止服务后,会尝试重新创建服务并调用 onStartCommand()
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
@Override
public IBinder onBind(Intent intent) {
return null; // 不提供绑定功能
}
}
<service android:name=".MusicService" />
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);
startButton.setOnClickListener(v -> startService(new Intent(this, MusicService.class)));
stopButton.setOnClickListener(v -> stopService(new Intent(this, MusicService.class)));
}
}
管理Service的生命周期
主要包括:
- onCreate(): 当服务第一次创建时调用。
- onStartCommand(): 每次通过 startService() 方法启动服务时调用。
- onBind(): 当其他组件想要与服务绑定时调用。
- onUnbind(): 当所有组件都与服务解绑时调用。
- onDestroy(): 当服务不再使用且将被销毁时调用。
与服务通信
Binder
对于在同一应用内运行的服务,可以通过定义一个 Binder 类并在服务中返回这个 Binder 的实例,从而实现与服务的通信。客户端(如 Activity)可以绑定到服务并获得这个 Binder 对象,通过这个对象调用服务中的方法。
public class LocalService extends Service {
// Binder 类的实例,用于返回给客户端
private final IBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
// 返回当前的 LocalService 实例以供客户端调用公开的方法
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
// 服务中的一个方法,客户端可以调用
public int getRandomNumber() {
return new Random().nextInt(100);
}
}
public class MainActivity extends AppCompatActivity {
private LocalService localService;
private boolean isBound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
localService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, LocalService.class), connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(connection);
isBound = false;
}
}
public void onButtonClick(View v) {
if (isBound) {
// 调用服务的方法
int num = localService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
}
Messager
如果服务需要与多个应用组件或其他应用进行跨进程通信,可以使用 Messenger
。在这种方式a中,服务使用 Handler
接收消息,并通过 Messenger
对象响应。
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "Hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger messenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
public class ActivityMessenger extends AppCompatActivity {
Messenger messenger = null;
boolean isBound;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
messenger = new Messenger(service);
isBound = true;
}
public void onServiceDisconnected(ComponentName className) {
messenger = null;
isBound = false;
}
};
public void sayHello(View v) {
if (!isBound) return;
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
AIDL
AIDL(Android Interface Definition Language)是一种接口定义语言,用于让客户端和服务之间能够在不同的进程中进行通信。使用 AIDL 是处理复杂数据传输或跨应用通信的标准方式。
定义一个 AIDL 文件,这个文件描述了服务将要公开的接口
interface IRandomNumberService {
int getRandomNumber();
}
当构建项目时,Android 构建工具会根据 AIDL 文件生成一个 Java 接口。然后可以创建一个实现这个接口的服务:
IRandomNumberService.Stub
是由 AIDL 文件自动生成的,需要实现这个 Stub 类中的方
public class RandomNumberService extends Service {
private final IRandomNumberService.Stub binder = new IRandomNumberService.Stub() {
public int getRandomNumber() throws RemoteException {
return new java.util.Random().nextInt(100);
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
现在,任何其他应用都可以绑定到这个服务并调用 getRandomNumber
方法
public class ClientActivity extends AppCompatActivity {
private IRandomNumberService randomNumberService;
private boolean isBound = false;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
randomNumberService = IRandomNumberService.Stub.asInterface(service);
isBound = true;
}
public void onServiceDisconnected(ComponentName className) {
randomNumberService = null;
isBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
bindService(new Intent("com.example.myapp.RandomNumberService"),
connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(connection);
isBound = false;
}
}
public void onButtonClick() {
if (isBound) {
try {
int randomNumber = randomNumberService.getRandomNumber();
// Use the random number...
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}