目錄
一、什么是Service
二、啟停?Service
三、綁定 Service
四、前臺服務
五、遠程服務擴展
六、服務保活
七、服務啟動方法混用
你可以把Service想象成一個“后臺默默打工的工人”。它沒有UI界面,默默地在后臺干活,比如播放音樂、下載文件、處理網絡請求等。即使你退出了App,Service也可以繼續運行。
一、什么是Service
Service是Android應用的核心后臺組件。
1. 無界面后臺任務
- Service 是 Android 系統中無可視化界面、運行于后臺的長生命周期組件。
- 核心功能:執行與用戶界面無關的持續性任務,如后臺播放音樂、文件下載等。
- Service 不依賴用戶交互界面,生命周期獨立于Activity。
- 典型應用場景:網絡請求、傳感器數據采集、跨進程通信(AIDL)。
2. 生命周期管理
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() ?和解綁操作才能銷毀 | 與 Started 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";@Overridepublic IBinder onBind(Intent intent) {return null; // 非綁定模式時返回 null}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "Service 啟動"); // 執行后臺任務return START_STICKY; // 服務終止后自動重啟}@Overridepublic 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()
四、前臺服務
1. 服務端實現
- 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";@Overridepublic void onCreate() {super.onCreate();createNotificationChannel();startForegroundWithNotification("服務初始化中...");}@Overridepublic 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);}@Overridepublic IBinder onBind(Intent intent) {return null; // 無需綁定功能}@Overridepublic 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><!-- 服務定義 --><serviceandroid:name=".ForegroundService"android:enabled="true"android:exported="false"android:foregroundServiceType="mediaPlayback"/> <!-- 按需配置類型 --></application>
</manifest>
3. 客戶端調用(Activity)
- 通過?
stopSelf()
?或?stopService()
?停止服務
// MainActivity.java
public class MainActivity extends AppCompatActivity {@Overrideprotected 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. 自?定義 Parcelable 對象
// CustomData.java
package com.example.model; import android.os.Parcel;
import android.os.Parcelable; public class CustomData implements Parcelable { private String content; // 構造函數 public CustomData(String content) { this.content = content; } // Parcelable 反序列化構造函數 protected CustomData(Parcel in) { content = in.readString(); } // Parcelable CREATOR public static final Creator<CustomData> CREATOR = new Creator<CustomData>() { @Override public CustomData createFromParcel(Parcel in) { return new CustomData(in); } @Override public CustomData[] newArray(int size) { return new CustomData[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(content); } // Getter public String getContent() { return content; }
}
2. 定義 AIDL 接口
- AIDL 文件定義跨進程通信的接口方法
- 支持基本類型、
String
、List
、Parcelable
?等數據類型 - 需在 AIDL 中顯式導入?
parcelable
?類型
// IRemoteService.aidl
package com.example.service; parcelable CustomData;
interface IRemoteService { int add(int a, int b); String getData(String input); void sendData(in CustomData data); void registerCallback(IRemoteCallback callback);void unregisterCallback(IRemoteCallback callback);
} // 回調接口(IRemoteCallback.aidl)
interface IRemoteCallback {void onResult(int result);
}
3. 服務端實現 AIDL 接口
- 繼承?
IRemoteService.Stub
?實現接口方法 - 通過?
onBind()
?返回?IBinder
?對象 - 直接使用?
CustomData
?對象(已自動反序列化) - 服務端通過?
RemoteCallbackList
?自動清理無效回調,無需手動處理。 RemoteCallbackList.register()
?會自動去重,多次注冊同一回調不會重復觸發
public class RemoteService extends Service { private final IBinder binder = new RemoteBinder();// 使用 RemoteCallbackList 管理跨進程回調(線程安全)private final RemoteCallbackList<IRemoteCallback> callbackList= new RemoteCallbackList<>();private class RemoteBinder extends IRemoteService.Stub { @Override public int add(int a, int b) { int result = a + b;notifyResult(result); // 觸發回調通知return result;} @Override public String getData(String input) { return "Processed: " + input; } @Override public void sendData(CustomData data) throws RemoteException { Log.d(TAG, "收到數據: " + data.getContent()); // 處理數據邏輯 } @Overridepublic void registerCallback(IRemoteCallback callback) {if (callback != null) {callbackList.register(callback);}}@Overridepublic void unregisterCallback(IRemoteCallback callback) {if (callback != null) {callbackList.unregister(callback);}}// 通知所有客戶端計算結果private void notifyResult(int result) {int count = callbackList.beginBroadcast();try {for (int i = 0; i < count; i++) {IRemoteCallback callback = callbackList.getBroadcastItem(i);callback.onResult(result); // 跨進程回調}} catch (RemoteException e) {e.printStackTrace(); // 客戶端已斷開,自動從列表中移除} finally {callbackList.finishBroadcast();}}} @Override public IBinder onBind(Intent intent) { return binder; } @Overridepublic void onDestroy() {super.onDestroy();callbackList.kill(); // 清理回調列表}
}
注意:必須使用 Android 提供的?
RemoteCallbackList
?管理跨進程回調(自動處理客戶端進程死亡情況),普通集合(如?ArrayList
)無法正確識別跨進程的?IBinder
?對象。
4.?注冊 Service(AndroidManifest.xml)
android:exported="true"
?允許跨進程訪問- 定義唯一?
action
?供客戶端綁定 - android:process=":remote" 強制指定獨立進程,所有跨進程綁定均指向此進程,復用同一實例
<!-- 服務端AndroidManifest.xml-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><!-- 聲明自定義權限(可選,用于安全控制) --><permissionandroid:name="com.example.permission.REMOTE_SERVICE"android:protectionLevel="signature"/> <!-- 僅允許同簽名應用訪問 --><!-- 添加前臺服務權限(若涉及前臺服務) --><uses-permission android:name="android.permission.FOREGROUND_SERVICE"/><application><!-- RemoteService 定義 --><serviceandroid:name=".RemoteService"android:enabled="true"android:exported="true" <!-- 允許跨進程訪問 -->android:permission="com.example.permission.REMOTE_SERVICE" <!-- 綁定權限 -->android:process=":remote"> <!-- 指定獨立進程,全局唯一 --><intent-filter><action android:name="com.example.service.IRemoteService"/></intent-filter></service></application>
</manifest>
5.?客戶端綁定與調用遠程服務
- 通過隱式 Intent 指定服務端包名和 Action
- 使用?
IRemoteService.Stub.asInterface()
?轉換?IBinder
?對象 - 所有跨進程方法調用需處理?
RemoteException
- 在?
onServiceDisconnected()
?和?onBindingDied()
?中,直接清理本地資源(如置空?remoteService
),?不調用任何遠程方法?。 - 客戶端無需在斷開時調用?
unregisterCallback()
,服務端能正確處理死亡 Binder,其注冊的回調會自動從列表中移除。 - 如果客戶端維護了本地回調列表(如?
localCallbackList
),需在斷開時直接清理,無需依賴服務端確認。
public class MainActivity extends AppCompatActivity { private IRemoteService remoteService; private boolean isBound = false; private IRemoteCallback callback = new IRemoteCallback.Stub() {@Overridepublic void onResult(int result) {// 注意:此處運行在 Binder 線程,需切到主線程更新 UInew Handler(Looper.getMainLooper()).post(() -> {textView.setText("計算結果: " + result);});}};// 綁定服務方法(封裝復用)private void bindService() {Intent intent = new Intent("com.example.service.IRemoteService");intent.setPackage("com.example.service"); // 顯式指定包名// 判斷是否 Android 11+ 需要添加 flagsif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {bindService(intent, connection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);} else {bindService(intent, connection, Context.BIND_AUTO_CREATE);}}private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { remoteService = IRemoteService.Stub.asInterface(service); isBound = true; try { remoteService.registerCallback(callback); // 注冊回調int result = remoteService.add(3, 5); Log.d("Client", "Result: " + result); CustomData data = new CustomData("Hello from Client"); remoteService.sendData(data);} catch (RemoteException e) { // 遠程調用異常捕獲e.printStackTrace(); Log.e("Client", "Remote call failed: " + e.getMessage());} } @Override public void onServiceDisconnected(ComponentName name) { isBound = false; remoteService = null; if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {new Handler(Looper.getMainLooper()).postDelayed(() -> {reconnectAttempts++;Log.w(TAG, "嘗試第 " + reconnectAttempts + " 次重連...");bindService(); // 調用綁定方法}, 3000); // 延遲 3 秒后重試(避免頻繁請求)} else {Log.e(TAG, "已達到最大重連次數,停止嘗試");}} @Overridepublic void onBindingDied(ComponentName name) {// Android 10+ 新增回調,處理綁定失效場景onServiceDisconnected(name);}}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(); } @Override protected void onDestroy() { super.onDestroy(); try {if (remoteService != null && callback != null) {remoteService.unRegisterCallback(callback); // 主動注銷remoteService = null;}} catch (RemoteException e) {e.printStackTrace();}if (isBound) { unbindService(connection); isBound = false; } }
}
6. 客戶端聲明權限
<!-- 客戶端 AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.client"><!-- Android 11+ 跨應用通信需添加 queries(必選) --><queries><!-- 聲明目標服務端包名 --><package android:name="com.example.service" /><!-- 若需要調用特定組件(如 Activity)可擴展為 --><!--<intent><action android:name="android.intent.action.VIEW" /><data android:scheme="https" /></intent>--></queries><application><!-- 其他組件聲明(如 Activity) --></application>
</manifest>
六、服務保活
方法 | 適用場景 | 廠商兼容性 | 系統限制 |
---|---|---|---|
前臺服務+通知 | 用戶感知型任務 | 高 | Android 8+ |
JobScheduler 定時拉活 | 低頻后臺任務 | 中 | Android 5+ |
系統廣播監聽 | 緊急恢復場景 | 低 | Android 7+ |
獨立進程守護 | 高穩定性要求場景 | 中 | 全版本 |
1. 前臺服務 + 通知 (前面已介紹)
- 使用?
startForeground()
?提升服務優先級至前臺級別 - Android 9+ 需動態申請?
FOREGROUND_SERVICE
?權限
public class PersistentService extends Service {@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 創建前臺通知(Android 8.0+ 需通知渠道)Notification notification = new NotificationCompat.Builder(this, "channel_id").setContentTitle("服務運行中").setSmallIcon(R.drawable.ic_notification).build();startForeground(1, notification); // 必須顯示通知return START_STICKY; // 服務終止后嘗試重啟:// ml-citation{ref="1,6" data="citationList"}}
}
2.?粘性服務重啟策略
覆蓋生命周期方法:
@Override
public void onTaskRemoved(Intent rootIntent) {// 任務被移除時(如用戶劃掉最近任務)觸發重啟Intent restartIntent = new Intent(this, PersistentService.class);restartIntent.setPackage(getPackageName());startService(restartIntent);super.onTaskRemoved(rootIntent);
}@Override
public void onDestroy() {// 服務被系統殺死時觸發重啟邏輯// 發送廣播,需要注冊一個廣播接收器sendBroadcast(new Intent("RESTART_SERVICE_ACTION")); super.onDestroy();
}
注冊廣播接收器,通過廣播重新拉起服務 :
<receiver android:name=".RestartReceiver"><intent-filter><action android:name="RESTART_SERVICE_ACTION" /></intent-filter>
</receiver>
頻繁調用?
startService()
?可能導致 ANR,建議結合?JobScheduler
?優化
3.?系統廣播監聽
聽高頻觸發廣播,利用網絡變化、解鎖等事件觸發服務重啟。
<receiver android:name=".SystemEventReceiver"><intent-filter><action android:android:name="android.intent.action.BOOT_COMPLETED" /><action android:name="android.net.conn.CONNECTIVITY_CHANGE" /><action android:name="android.intent.action.USER_PRESENT" /></intent-filter>
</receiver>
4.?進程守護與JobScheduler
獨立進程運行,減少主進程崩潰對服務的影響。
<service android:name=".PersistentService"android:process=":persistent_process" />
JobScheduler 定時喚醒,定期檢查服務狀態并拉起。
ComponentName serviceComponent = new ComponentName(this, PersistentService.class);
JobInfo jobInfo = new JobInfo.Builder(1, serviceComponent).setPeriodic(15 * 60 * 1000) // 15分鐘間隔.setPersisted(true) // 設備重啟后保持任務.build();
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
七、服務啟動方法混用
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