在 Android 开发中,有时我们需要后台服务持续运行,以保持应用的某些功能。例如,音乐播放器需要在后台播放音乐,或者健康应用需要持续跟踪用户的运动数据。后台服务是 Android 中的一种组件,它不与用户界面交互,能够在后台执行长时间运行的任务。由于 Android 系统的资源管理策略,这些服务可能会被系统杀死以释放资源。在某些情况下,我们希望服务能够持续存在,避免被系统回收。这时,我们需要将服务设为持久(Persistent)。
一、基本使用
首先,我们需要创建一个服务类继承自 Service。以下是一个示例代码:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyPersistentService extends Service {
private static final String TAG = "MyPersistentService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service Created");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
// 不在这里执行长时间运行的操作,建议使用线程
// 确保服务在系统杀死后再次启动
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 不绑定到任何 Activity
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service Destroyed");
}
}
在上面的代码中,我们定义了一个名为 MyPersistentService 的基础服务。然后,在 onStartCommand 方法中,我们返回了 START_STICKY,这使得当系统因为资源紧张而杀死服务时,服务会被重新启动。
为了使后台服务持久,我们需要将其设为前台服务。前台服务通过展示通知使用户知道它的存在。只要用户不主动关闭该服务,Android 系统会优先保护这些服务。为此,我们需要在 onStartCommand 方法中增加创建通知的代码。首先,我们需要创建一个通知渠道(适用于 Android 8.0 及以上):
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
private static final String CHANNEL_ID = "MyServiceChannel";
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"My Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
然后,在 onStartCommand 方法中启动前台服务。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = new Notification.Builder(this, CHANNEL_ID)
.setContentTitle("Service is Running")
.setContentText("My Persistent Service is active")
.setSmallIcon(R.drawable.ic_notification)
.build();
startForeground(1, notification);
return START_STICKY;
}
最后,启动这项服务,可以在主活动中使用以下代码:
Intent serviceIntent = new Intent(this, MyPersistentService.class);
startService(serviceIntent);
需要注意的是,在实现持久化后台服务时,需要遵循以下几点:
- 用户体验:前台服务通过通知保持用户的知情权,注意不要在通知中释放敏感信息。
- 电池消耗:持久化后台服务会消耗更多的电量,优化服务逻辑是必要的。
- 权限管理:确保在 Android 6.0 及以上版本中获取合适的权限。
二、原理分析
当在AndroidManifest.xml中将Persistent属性设置为true时,那么该App就会具有如下两个特性:
- 在系统刚起来的时候,该App也会被启动起来。
- 该App被强制杀掉后,系统会重启该App。这种情况只针对系统内置的App,第三方安装的App不会被重启。
2.1 Persistent属性解析
Persistent属性的解析主要发生在App安装或者系统启动的时候,解析代码的位置如下:
/frameworks/base/core/java/android/content/pm/PackageParser.java
如果想要查看Android的源码,可以使用下面的网址:
深入到PackageParser.java的parseBaseApplication中:
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
...省略...
if ((flags&PARSE_IS_SYSTEM) != 0) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_persistent,
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
...省略...
}
在解析完系统中App的包信息后,会将解析好的信息保存在PMS中的mPackages的map中,ApplicationInfo的flag中有一个bit位用于保存该App是否是persistent。这里主要是将persistent的flag设置为ApplicationInfo.FLAG_PERSISTENT。
2.2 Persistent为true
在系统启动时,会启动persistent属性为true的App,代码位置在:
/frameworks/base//services/core/java/com/android/server/am/ActivityManagerService.java
在系统启动时,AMS中的systemReady()方法会将所有在AndroidManifest中设置了persistent为true的App进程拉起来。深入到AMS的systemReady()方法中,代码如下:
public void systemReady(final Runnable goingCallback) {
...省略...
synchronized (this) {
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
try {
List apps = AppGlobals.getPackageManager().
getPersistentApplications(STOCK_PM_FLAGS);//注释1
if (apps != null) {
int N = apps.size();
int i;
for (i=0; i<N; i++) {
ApplicationInfo info
= (ApplicationInfo)apps.get(i);
if (info != null &&
!info.packageName.equals("android")) {
addAppLocked(info, false, null /* ABI override */);//注释2
}
}
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
...省略...
}
在上面的代码中,我们重点说明两个地方:
- 注释1:调用PackageManagerServices的getPersistentApplications方法获取所有在AndroidManifest中设置了persistent属性为true的App。
- 注释2:调用ActivityManagerServcies的addAppLocked方法去启动App。
深入到PackageManagerServices的getPersistentApplications方法中,源码如下:
public List<ApplicationInfo> getPersistentApplications(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
if (ps != null) {
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
finalList.add(ai);
}
}
}
}
}
return finalList;
}
getPersistentApplications方法会遍历mPackages中所有的App,从判断条件中可以看到只有当在解析persistent属性时,ApplicationInfo的flag设置成了FLAG_PERSISTENT,且是系统App;或者是在非安全模式下,才会被选中。
可以看出被选中的情形有两种:
- 系统App,只要ApplicationInfo的flag设置成了FLAG_PERSISTENT。
- 第三方安装的App,不仅要ApplicationInfo的flag设置成了FLAG_PERSISTENT,还需要在非安全模式下。
回到ActivityManagerServcies的addAppLocked方法中:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
//传进来的isolated是false,所以就会调用getProcessRecordLocked方法,
//但由于是第一次启动,所以所有的返回都是app = null
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid, true);
} else {
app = null;
}
if (app == null) {
//为新的app创建新的ProcessRecord对象
app = newProcessRecordLocked(info, null, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
// This package really, really can not be stopped.
try {
//因为是开机第一次启动,所以新的App的启动状态就是将要被启动的状态
//所以将App的停止状态stoped设置为false
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
}
//如果是系统App,且persistent属性为true,则异常死亡后会重启
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
//如果App已启动,则不处理,否则调用startProcessLocked方法启动App
//启动App是异步的,因此会将正在启动,但还没启动完成的App添加到mPersistentStartingProcesses列表中,当启动完成后再移除
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
//启动App
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
在App启动完成后,会在ActivityThread中调用ActivityManagerService的attachApplicationLocked()方法,将该App从mPersistentStartingProcesses移除,并注册一个死亡讣告监听器AppDeathRecipient,用于在App异常被杀后的处理工作。
深入到ActivityManagerService的attachApplicationLocked()方法中,源码如下:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...省略...
final String processName = app.processName;
try {
//注册死亡讣告监听器AppDeathRecipient
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
//如果注册死亡讣告监听器失败,也会重新启动App进程
startProcessLocked(app, "link fail", processName);
return false;
}
...省略...
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
...省略...
到此,Persistent属性为true的App在开机时就会启动,并且会注册死亡讣告监听器AppDeathRecipient。
2.3 重启被强制kill掉的App
进程在启动时,会为App注册一个死亡讣告,当App被杀掉后,就会调用AppDeathRecipient的binderDied方法,对应的代码如下:
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (DEBUG_ALL) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
if (DEBUG_ALL) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
binderDied方法又会调用appDiedLocked()方法,源码如下:
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
...省略...
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.instrumentationClass == null;
boolean doOomAdj = doLowMem;
...省略...
handleAppDiedLocked(app, false, true);
...省略...
}
handleAppDiedLocked又会调用handleAppDiedLocked()方法,源码如下:
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
false /*replacingPid*/);
...省略...
}
继续调用cleanUpApplicationRecordLocked()方法,源码如下:
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
...省略...
//非persistent的App被kill后,就会被清理掉
if (!app.persistent || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
removeProcessNameLocked(app.processName, app.uid);
}
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
//该app是persistent的,需要对其进行重启,并把它添加到正在启动的列表中
//设置restart=true
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
...省略...
//通过这个判断添加决定是否重启App进程
//通过前面的过滤,persistent属性的App,restart=true,!app.isolated=true
if (restart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
ProcessList.remove(app.pid);
}
addProcessNameLocked(app);
//启动App进程
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
boolean removed;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
app.setPid(0);
}
return false;
}
到此,带有persistent属性为true的App,就会在强制kill掉进程后,还会重启。重启persistent应用的调用关系图如下。