赞
踩
Android从SDK26
开始增加了启动服务的startForegroundService
接口, 该接口需要在服务启动后调用startForeground
接口,服务将成为前台服务,其相对普通服务,有更高的优先级.如果服务启动后在指定的时间内没有调用startForeground, 服务将被终止,并且抛出ANR. 出错信息类似如下:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.foregroundservice.demo, PID: 19005
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1800)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6566)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
当调用启动服务接口startForegroundService时, 会依次调用到bringUpServiceLocked
–>realStartServiceLocked
–>sendServiceArgsLocked
, 该函数的实现为(ActiveServices.java):
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) throws TransactionTooLargeException { final int N = r.pendingStarts.size(); if (N == 0) { return; } ArrayList<ServiceStartArgs> args = new ArrayList<>(); while (r.pendingStarts.size() > 0) { ServiceRecord.StartItem si = r.pendingStarts.remove(0); if (DEBUG_SERVICE) { Slog.v(TAG_SERVICE, "Sending arguments to: " + r + " " + r.intent + " args=" + si.intent); } if (si.intent == null && N > 1) { // If somehow we got a dummy null intent in the middle, // then skip it. DO NOT skip a null intent when it is // the only one in the list -- this is to support the // onStartCommand(null) case. continue; } si.deliveredTime = SystemClock.uptimeMillis(); r.deliveredStarts.add(si); si.deliveryCount++; if (si.neededGrants != null) { mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, si.getUriPermissionsLocked()); } mAm.grantEphemeralAccessLocked(r.userId, si.intent, r.appInfo.uid, UserHandle.getAppId(si.callingId)); bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; mAm.updateOomAdjLocked(r.app, true); } if (r.fgRequired && !r.fgWaiting) { if (!r.isForeground) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r); } scheduleServiceForegroundTransitionTimeoutLocked(r); } else { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Service already foreground; no new timeout: " + r); } r.fgRequired = false; } } int flags = 0; if (si.deliveryCount > 1) { flags |= Service.START_FLAG_RETRY; } if (si.doneExecutingCount > 0) { flags |= Service.START_FLAG_REDELIVERY; } args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent)); } ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args); slice.setInlineCountLimit(4); Exception caughtException = null; try { r.app.thread.scheduleServiceArgs(r, slice); } catch (TransactionTooLargeException e) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size() + " args, first: " + args.get(0).args); Slog.w(TAG, "Failed delivering service starts", e); caughtException = e; } catch (RemoteException e) { // Remote process gone... we'll let the normal cleanup take care of this. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r); Slog.w(TAG, "Failed delivering service starts", e); caughtException = e; } catch (Exception e) { Slog.w(TAG, "Unexpected exception", e); caughtException = e; } if (caughtException != null) { // Keep nesting count correct final boolean inDestroying = mDestroyingServices.contains(r); for (int i = 0; i < args.size(); i++) { serviceDoneExecutingLocked(r, inDestroying, inDestroying); } if (caughtException instanceof TransactionTooLargeException) { throw (TransactionTooLargeException)caughtException; } } }
Line37:42 如果该ServiceRecord的fgRequired为true(即要求启动前台服务), fgWaiting为false(即还没有开始等待startForeground调用),isForeground为false(即还没有成为前台服务),则进一步调用scheduleServiceForegroundTransitionTimeoutLocked
,该函数的实现为(ActiveServices.java):
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
if (r.app.executingServices.size() == 0 || r.app.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
msg.obj = r;
r.fgWaiting = true;
mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}
该函数将延迟发送消息SERVICE_FOREGROUND_TIMEOUT_MSG
, 延迟时间间隔为SERVICE_START_FOREGROUND_TIMEOUT
, 定义在类ActiveServices中:
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 5*1000;
该消息超时后将执行serviceForegroundTimeout
,其实现为(ActiveServices.java):
void serviceForegroundTimeout(ServiceRecord r) { ProcessRecord app; synchronized (mAm) { if (!r.fgRequired || r.destroying) { return; } if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Service foreground-required timeout for " + r); } app = r.app; r.fgWaiting = false; stopServiceLocked(r); } if (app != null) { mAm.mAppErrors.appNotResponding(app, null, null, false, "Context.startForegroundService() did not then call Service.startForeground()"); } }
该函数将终止服务, 并且抛出ANR异常.
如果服务在启动后调用了startForeground, 其将执行函数setServiceForegroundInnerLocked
, 其实现为(ActiveServices.java):
private void setServiceForegroundInnerLocked(ServiceRecord r, int id, Notification notification, int flags) { if (id != 0) { if (notification == null) { throw new IllegalArgumentException("null notification"); } // Instant apps need permission to create foreground services. if (r.appInfo.isInstantApp()) { final int mode = mAm.mAppOpsService.checkOperation( AppOpsManager.OP_INSTANT_APP_START_FOREGROUND, r.appInfo.uid, r.appInfo.packageName); switch (mode) { case AppOpsManager.MODE_ALLOWED: break; case AppOpsManager.MODE_IGNORED: Slog.w(TAG, "Instant app " + r.appInfo.packageName + " does not have permission to create foreground services" + ", ignoring."); return; case AppOpsManager.MODE_ERRORED: throw new SecurityException("Instant app " + r.appInfo.packageName + " does not have permission to create foreground services"); default: try { if (AppGlobals.getPackageManager().checkPermission( android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid)) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Instant app " + r.appInfo.packageName + " does not have permission to create foreground" + "services"); } } catch (RemoteException e) { throw new SecurityException("Failed to check instant app permission." , e); } } } if (r.fgRequired) { if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Service called startForeground() as required: " + r); } r.fgRequired = false; r.fgWaiting = false; mAm.mHandler.removeMessages( ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r); } if (r.foregroundId != id) { cancelForegroundNotificationLocked(r); r.foregroundId = id; } notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; r.foregroundNoti = notification; if (!r.isForeground) { final ServiceMap smap = getServiceMapLocked(r.userId); if (smap != null) { ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName); if (active == null) { active = new ActiveForegroundApp(); active.mPackageName = r.packageName; active.mUid = r.appInfo.uid; active.mShownWhileScreenOn = mScreenOn; if (r.app != null) { active.mAppOnTop = active.mShownWhileTop = r.app.uidRecord.curProcState <= ActivityManager.PROCESS_STATE_TOP; } active.mStartTime = active.mStartVisibleTime = SystemClock.elapsedRealtime(); smap.mActiveForegroundApps.put(r.packageName, active); requestUpdateActiveForegroundAppsLocked(smap, 0); } active.mNumActive++; } r.isForeground = true; } r.postNotification(); if (r.app != null) { updateServiceForegroundLocked(r.app, true); } getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r); mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); } else { if (r.isForeground) { final ServiceMap smap = getServiceMapLocked(r.userId); if (smap != null) { decActiveForegroundAppLocked(smap, r); } r.isForeground = false; if (r.app != null) { mAm.updateLruProcessLocked(r.app, false, null); updateServiceForegroundLocked(r.app, true); } } if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) { cancelForegroundNotificationLocked(r); r.foregroundId = 0; r.foregroundNoti = null; } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { r.stripForegroundServiceFlagFromNotification(); if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) { r.foregroundId = 0; r.foregroundNoti = null; } } } }
该函数将ServiceRecord的fgRequired更新为false, fgWaiting更新为false,isForeground更新为true, 并移除延迟消息SERVICE_FOREGROUND_TIMEOUT_MSG.因此只要startForeground在5秒中内被调用, 则延迟的消息就不会被发送,相应的消息处理函数就不会被调用, 也不会抛出ANR.
我们可以推测在调用startForegroundService启动服务时, 其ServiceRecord
类的fgRequired字段应该为true, 事实也是如此, 因为以这种方式启动服务时,创建的PendingIntent
为(PendingIntent.java):
/** * Retrieve a PendingIntent that will start a foreground service, like calling * {@link Context#startForegroundService Context.startForegroundService()}. The start * arguments given to the service will come from the extras of the Intent. * * <p class="note">For security reasons, the {@link android.content.Intent} * you supply here should almost always be an <em>explicit intent</em>, * that is specify an explicit component to be delivered to through * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the service. * @param requestCode Private request code for the sender * @param intent An Intent describing the service to be started. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, * {@link #FLAG_IMMUTABLE} or any of the flags as supported by * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts * of the intent that can be supplied when the actual send happens. * * @return Returns an existing or new PendingIntent matching the given * parameters. May return null only if {@link #FLAG_NO_CREATE} has been * supplied. */ public static PendingIntent getForegroundService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { return buildServicePendingIntent(context, requestCode, intent, flags, ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE); }
其Intent的serviceKind
被设置为INTENT_SENDER_FOREGROUND_SERVICE
,该intent被send发送后,创建的ServiceRecord类中fgRequired字段被赋值为true, 具体可以参考函数startServiceLocked
.
使用startService
启动服务后, 再调用startForeground还是可以将服务设置为前台服务,但是这种方式启动服务,就不会设置超时事件, 也不会抛出ANR.
在adb shell下可以通过dumpsys
查看某个包下的服务是否为前台服务,在adb shell中输入如下命令:
$ dumpsys activity services -p com.foregroundservice.demo
-p
后面是指定的包名, 如果服务是前台服务,其输出结果中会有类似的信息:
isForeground=true foregroundId=1 foregroundNoti=Notification
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。