【Android】安卓四大组件之服务(Service)详解:从基础到进阶

发布于:2025-07-11 ⋅ 阅读:(44) ⋅ 点赞:(0)

在安卓应用开发中,用户界面的交互往往只是冰山一角,大量的后台任务,如音乐播放、文件下载、数据同步等,都需要在不影响用户操作界面的情况下持续运行。这时,安卓四大组件之一的Service就派上了用场。

一、Service 的基本概念​

  • Service 是 Android 系统中无可视化界面、运行于后台的长生命周期组件。
  • 核心功能:执行与用户界面无关的持续性任务,如后台播放音乐、文件下载等。
  • Service 不依赖用户交互界面,生命周期独立于Activity。
  • 典型应用场景:网络请求、传感器数据采集、跨进程通信(AIDL)

二、生命周期管理

Service有两种主要类型:

特性 Started Service(启动式) Bound Service(绑定式)
启动方式 通过startService(Intent)启动 通过bindService(Intent,ServiceConnection,flags)启动
生命周期 onCreate()→onStartCommand()→onDestroy() onCreate()→onBind()→onUnbind()→onDestroy()
通信机制 无法直接与组件交互,需要通过广播或者Intent传递数据 通过Binder接口直接通信(支持方法调用)
销毁条件 需手动调用stopSelf()或着stopService() 所有绑定组件解绑后接口自动销毁
多组件绑定 不支持,每次启动独立运行 支持与多个组件同事绑定(如多个Activity共享同一服务实例)
适用场景 一次性后台任务(如下载,音乐播放) 长期交互服务(如数据同步、实时计算)
优先级与系统回收

后台服务可能会被系统回收,可通过startForeground()提升为前台服务

优先级较低,绑定组件退出后可能更快被回收
共存场景 可与Bound Service共存,需同时调用stopSelf()和解绑操作才能销毁 与Start Service共存时,需先解绑所有组件再手动停止服务

涉及的生命周期方法:

生命周期方法 出发场景
onCreate() Service首次创建时调用(仅一次)
onStartCommand() 每次通过startService()启动时调用
onBind() 通过bindService()绑定时调用
onUnbind() 所有客户端解绑时调用
onDestroy Service被销毁前调用(需手动停止或系统回收)

Service 默认运行在主线程,耗时操作需自行创建子线程或使用 IntentService

三、启动和停止Service

1. 定义 Service 类
继承 Service 类并实现核心方法:

public class MyService extends Service {
    private static final String TAG = "MyService";
    
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 非绑定模式时返回 null
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service 启动"); // 执行后台任务
        return START_STICKY; // 服务终止后自动重启
    }
 
    @Override
    public void onDestroy() {
        Log.d(TAG, "Service 销毁");
        super.onDestroy();
    }
}

2. 注册 Service

在 AndroidManifest.xml 中添加声明:

<application>
    <service android:name=".MyService" />
</application>

3. 通过 startService() 启动

在 Activity 或其他组件中调用:

Intent serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent); // 启动服务:ml-citation{ref="6,8" data="citationList"}

4. ‌通过 stopService() 或 stopSelf() 停止

stopService(new Intent(this, MyService.class)); // 外部停止
// 或在 Service 内部调用 stopSelf();:ml-citation{ref="6,8" data="citationList"}

四、绑定Service

1. ‌定义 Bound Service

  • 通过 LocalBinder 返回 Service 实例,实现组件间交互
  • onBind() 返回 IBinder 对象,供客户端绑定
// MyBoundService.java  
public class MyBoundService extends Service {  
    private final IBinder binder = new LocalBinder();  
    private static final String TAG = "MyBoundService";  
 
    public class LocalBinder extends Binder {  
        MyBoundService getService() {  
            return MyBoundService.this;  
        }  
    }  
 
    @Override  
    public IBinder onBind(Intent intent) {  
        Log.d(TAG, "Service 已绑定");  
        return binder;  
    }  
 
    @Override  
    public boolean onUnbind(Intent intent) {  
        Log.d(TAG, "所有客户端已解绑");  
        return super.onUnbind(intent);  
    }  
 
    // 自定义服务方法(供Activity调用)  
    public void performTask(String data) {  
        Log.d(TAG, "执行任务:" + data);  
    }  
}

2. 注册 Service

在 AndroidManifest.xml 中添加声明:

<application>  
    <service android:name=".MyBoundService" />  
</application>  

3. Activity 绑定与通信

  • 通过 bindService() 建立绑定
  • ServiceConnection 处理绑定成功/断开事件
  • 绑定后通过 myService 实例直接调用服务方法
  • 必须调用 unbindService() 释放资源,避免内存泄漏
  • 多个组件可绑定同一服务,全部解绑后服务销毁
// MainActivity.java  
public class MainActivity extends AppCompatActivity {  
    private MyBoundService myService;  
    private boolean isBound = false;  
 
    private ServiceConnection connection = new ServiceConnection() {  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            MyBoundService.LocalBinder binder
                = (MyBoundService.LocalBinder) service;  
            myService = binder.getService();  
            isBound = true;  
            myService.performTask("Hello from Activity!"); // 调用服务方法  
        }  
 
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            isBound = false;  
        }  
    };  
 
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
 
        // 绑定服务  
        Intent intent = new Intent(this, MyBoundService.class);  
        bindService(intent, connection, Context.BIND_AUTO_CREATE);  
    }  
 
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        if (isBound) {  
            unbindService(connection); // 必须解绑避免泄漏  
            isBound = false;  
        }  
    }  
}  

Activity 创建时: bindService() → Service: onCreate() → onBind()

Activity 销毁时: unbindService() → Service: onUnbind() → onDestroy()

四、前台服务

前台Service具有较高的优先级,会在状态栏显示一个通知,告知用户该Service正在运行,以防止被系统轻易终止。常见使用场景包括:​

  • 音乐播放:在状态栏显示播放控制通知,用户可随时暂停、切换歌曲。​
  • 文件下载:实时展示下载进度,避免下载任务被系统回收。
  • Android 8.0+ 必须创建 NotificationChannel,否则通知无法显示
  • 通过 IMPORTANCE_LOW 设置低优先级(无提示音)
  • startForeground() 必须在 onCreate() 或 onStartCommand() 中调用,调用后服务优先级提升,避免被系统轻易回收
  • stopForeground(true) 确保通知栏通知被移除
// ForegroundService.java
public class ForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1001;
    private static final String CHANNEL_ID = "foreground_service_channel";
 
    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        startForegroundWithNotification("服务初始化中...");
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 获取 Activity 传递的数据(可选)
        String inputData = intent != null
            ? intent.getStringExtra("input_data") : null;
        updateNotification("正在运行: " + inputData);
 
        // 模拟耗时任务
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                    updateNotification("进度: " + (i + 1) * 10 + "%");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            stopSelf(); // 任务完成后自动停止服务
        }).start();
 
        return START_STICKY; // 服务被系统杀死后自动重启
    }
 
    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "前台服务示例",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel.setDescription("用于展示前台服务的持续运行状态");
            NotificationManager manager
                = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }
 
    private void startForegroundWithNotification(String text) {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                notificationIntent,
                PendingIntent.FLAG_IMMUTABLE
        );
 
        Notification notification = new NotificationCompat
                .Builder(this, CHANNEL_ID)
                .setContentTitle("前台服务示例")
                .setContentText(text)
                .setSmallIcon(R.drawable.ic_notification)
                .setContentIntent(pendingIntent)
                .setOnlyAlertOnce(true) // 避免重复提示音
                .build();
 
        startForeground(NOTIFICATION_ID, notification);
    }
 
    private void updateNotification(String text) {
        Notification notification = new NotificationCompat
                .Builder(this, CHANNEL_ID)
                .setContentTitle("前台服务示例")
                .setContentText(text)
                .setSmallIcon(R.drawable.ic_notification)
                .setOnlyAlertOnce(true)
                .build();
 
        NotificationManager manager
            = getSystemService(NotificationManager.class);
        manager.notify(NOTIFICATION_ID, notification);
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 无需绑定功能
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true); // 停止时移除通知
    }
}

2. 配置清单文件注册服务

<!-- 服务端 AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 必须的权限声明,否则startForeground方法不可用 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
    <application>
        <!-- 服务定义 -->
        <service
            android:name=".ForegroundService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="mediaPlayback"/>  <!-- 按需配置类型 -->
    </application>
</manifest>

3. 客户端调用(Activity)

  • 通过 stopSelf() 或 stopService() 停止服务
// MainActivity.java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // 启动前台服务按钮
        findViewById(R.id.btn_start).setOnClickListener(v -> {
            Intent serviceIntent = new Intent(this, ForegroundService.class);
            serviceIntent.putExtra("input_data", "用户启动任务");
 
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(serviceIntent); // Android 8.0+ 专用方法
            } else {
                startService(serviceIntent);
            }
        });
 
        // 停止服务按钮
        findViewById(R.id.btn_stop).setOnClickListener(v -> {
            Intent serviceIntent = new Intent(this, ForegroundService.class);
            stopService(serviceIntent);
        });
    }
}

4. 客户端配置权限

<!-- 客户端 AndroidManifest.xml -->  
<manifest>  
    <!-- 添加权限 -->  
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>  
 
    <!-- 跨进程调用时添加(可选) -->  
    <queries>  
        <package android:name="com.example.service"/>  
    </queries>  
 
    <application>  
        <!-- 无需声明服务组件 -->  
    </application>  
</manifest>  

五、服务启动方法混用

1. startService() 重复调用

  • 首次调用‌:触发 onCreate() → onStartCommand()
  • 后续调用‌:仅触发 onStartCommand()onCreate() 不再执行
// 第一次调用
startService(intent); // onCreate() -> onStartCommand()
 
// 第二次调用
startService(intent); // 仅 onStartCommand()

2. bindService() 重复调用

  • 首次绑定‌:触发 onCreate() → onBind()
  • 后续绑定‌:若 Service 已存在,直接返回已创建的 IBinder 对象,不再触发 onBind()
// 第一次绑定
// onCreate() -> onBind()
bindService(intent, conn, BIND_AUTO_CREATE); 
 
// 第二次绑定(同一进程)
// 无生命周期方法调用,复用已有 IBinder
bindService(intent, conn2, BIND_AUTO_CREATE); 

不同进程再次绑定‌:

‌同一 Service 实例‌:若 Service 已在独立进程运行,后续绑定直接复用已有实例,‌不再触发 onCreate() 和 onBind()‌,仅通过 ServiceConnection 返回 IBinder 代理对象。
‌新 Service 实例‌:若应用配置多进程且未声明 android:process,不同组件进程可能触发多个 Service 实例(需避免此设计)

3. 混合调用场景

 startService() 后 bindService():

Service 生命周期持续到 unbindService() 和 stopService()/stopSelf() 均被调用

startService(intent); // onCreate() -> onStartCommand()
bindService(intent, conn); // onBind(),Service 已存在无需创建

同理,先bindService()后startService()

bindService(intent, conn); // 创建 Service 实例,onCreate() → onBind()
startService(intent); // onStartCommand(),Service 已存在无需创建

混合调用时需同时调用 stopService() 和 unbindService() 才能销毁 Service

六、Service 与其他组件的交互

1. Service 与 Activity 通信​

  • 启动式通信:Activity通过startService()启动Service,并传递参数(如Intent.putExtra()),但难以获取Service的执行结果。​
  • 绑定式通信:Activity通过bindService()绑定Service,获取IBinder对象后,可调用Service中的公共方法,实现双向通信。​

2. Service 与 BroadcastReceiver 联动​

Service可通过sendBroadcast()发送广播,由BroadcastReceiver接收并处理;反之,BroadcastReceiver也可在接收到特定广播后启动Service,例如,当设备网络状态改变时,BroadcastReceiver启动Service进行数据同步。

七、Service 使用的注意事项​

  • 避免在主线程执行耗时操作:Service默认运行在主线程,若执行耗时任务(如网络请求),需开启子线程,否则会导致应用 ANR(Application Not Responding)。​
  • 内存管理:及时释放Service占用的资源,避免内存泄漏。​
  • 适配高版本安卓系统:关注安卓系统对Service的限制和优化,如安卓 10(API 级别 29)进一步限制后台服务的使用,需采用合适的替代方案(如 WorkManager) 

网站公告

今日签到

点亮在社区的每一天
去签到