(七十四)Android O Service启动流程梳理——startForegroundService

本文主要是介绍(七十四)Android O Service启动流程梳理——startForegroundService,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:之前梳理了startService和bindService,现在接着梳理下Android O比较有特点的startForegroundService。

  1. (六十四)Android O Service启动流程梳理——startService
  2. (六十五)Android O StartService的 anr timeout 流程分析
  3. (七十)Android O Service启动流程梳理——bindService

 

 

参考:https://blog.csdn.net/lylddinghffw/article/details/80366791

 

1.service启动简述

service启动分三种,比较简单的就是startService,Android O用于后台应用启动前台服务的startForegroundService和绑定服务的bindService。本篇继续梳理startForegroundService,startForegroundService使用需要注意的是Service启动后要在5s之内调用startForeground显示一个通知,不然就会anr。

 

2.startForegroundService流程梳理

2.1 ContextImpl

    @Overridepublic ComponentName startForegroundService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, true, mUser);}

 

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {try {validateServiceIntent(service);service.prepareToLeaveProcess(this);ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground,getOpPackageName(), user.getIdentifier());if (cn != null) {if (cn.getPackageName().equals("!")) {throw new SecurityException("Not allowed to start service " + service+ " without permission " + cn.getClassName());} else if (cn.getPackageName().equals("!!")) {throw new SecurityException("Unable to start service " + service+ ": " + cn.getClassName());} else if (cn.getPackageName().equals("?")) {throw new IllegalStateException("Not allowed to start service " + service + ": " + cn.getClassName());}}return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

对照下之前梳理的startService,发现只是requireForeground参数由false改为true表明需要置为前台Service,其他流程是一样的。注意关注下这个参数带来的变化。

    @Overridepublic ComponentName startService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, false, mUser);}

那这里附一下startService的时序图,一样的流程就略过不赘述了。

 

2.2 AMS

    @Overridepublic ComponentName startService(IApplicationThread caller, Intent service,String resolvedType, boolean requireForeground, String callingPackage, int userId)throws TransactionTooLargeException {enforceNotIsolatedCaller("startService");// Refuse possible leaked file descriptorsif (service != null && service.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}if (callingPackage == null) {throw new IllegalArgumentException("callingPackage cannot be null");}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);synchronized(this) {final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();ComponentName res;try {res = mServices.startServiceLocked(caller, service,resolvedType, callingPid, callingUid,requireForeground, callingPackage, userId);} finally {Binder.restoreCallingIdentity(origId);}return res;}}

可以看到AMS并没有对requireForeground进行特殊处理,只是接着往下传。

 

2.3 ActiveServices

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)throws TransactionTooLargeException {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service+ " type=" + resolvedType + " args=" + service.getExtras());final boolean callerFg;if (caller != null) {final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);if (callerApp == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + callingPid+ ") when starting service " + service);}callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;} else {callerFg = true;}ServiceLookupResult res =retrieveServiceLocked(service, resolvedType, callingPackage,callingPid, callingUid, userId, true, callerFg, false);if (res == null) {return null;}if (res.record == null) {return new ComponentName("!", res.permission != null? res.permission : "private to package");}ServiceRecord r = res.record;if (!mAm.mUserController.exists(r.userId)) {Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);return null;}// If this isn't a direct-to-foreground start, check our ability to kick off an// arbitrary serviceif (!r.startRequested && !fgRequired) {// Before going further -- if this app is not allowed to start services in the// background, then at this point we aren't going to let it period.final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,r.appInfo.targetSdkVersion, callingPid, false, false);if (allowed != ActivityManager.APP_START_MODE_NORMAL) {Slog.w(TAG, "Background start not allowed: service "+ service + " to " + r.name.flattenToShortString()+ " from pid=" + callingPid + " uid=" + callingUid+ " pkg=" + callingPackage);if (allowed == ActivityManager.APP_START_MODE_DELAYED) {// In this case we are silently disabling the app, to disrupt as// little as possible existing apps.return null;}// This app knows it is in the new model where this operation is not// allowed, so tell it what has happened.UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);return new ComponentName("?", "app is in background uid " + uidRec);}}NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(callingUid, r.packageName, service, service.getFlags(), null, r.userId);// If permissions need a review before any of the app components can run,// we do not start the service and launch a review activity if the calling app// is in the foreground passing it a pending intent to start the service when// review is completed.if (mAm.mPermissionReviewRequired) {if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,callingUid, service, callerFg, userId)) {return null;}}if (unscheduleServiceRestartLocked(r, callingUid, false)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);}r.lastActivity = SystemClock.uptimeMillis();r.startRequested = true;r.delayedStop = false;r.fgRequired = fgRequired;r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),service, neededGrants, callingUid));final ServiceMap smap = getServiceMapLocked(r.userId);boolean addToStarting = false;if (!callerFg && !fgRequired && r.app == null&& mAm.mUserController.hasStartedUserState(r.userId)) {ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {// If this is not coming from a foreground caller, then we may want// to delay the start if there are already other background services// that are starting.  This is to avoid process start spam when lots// of applications are all handling things like connectivity broadcasts.// We only do this for cached processes, because otherwise an application// can have assumptions about calling startService() for a service to run// in its own process, and for that process to not be killed before the// service is started.  This is especially the case for receivers, which// may start a service in onReceive() to do some additional work and have// initialized some global state as part of that.if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "+ r + " in " + proc);if (r.delayed) {// This service is already scheduled for a delayed start; just leave// it still waiting.if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);return r.name;}if (smap.mStartingBackground.size() >= mMaxStartingBackground) {// Something else is starting, delay!Slog.i(TAG_SERVICE, "Delaying start of: " + r);smap.mDelayedStartList.add(r);r.delayed = true;return r.name;}if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);addToStarting = true;} else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {// We slightly loosen when we will enqueue this new service as a background// starting service we are waiting for, to also include processes that are// currently running other services or receivers.addToStarting = true;if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,"Not delaying, but counting as bg: " + r);} else if (DEBUG_DELAYED_STARTS) {StringBuilder sb = new StringBuilder(128);sb.append("Not potential delay (state=").append(proc.curProcState).append(' ').append(proc.adjType);String reason = proc.makeAdjReason();if (reason != null) {sb.append(' ');sb.append(reason);}sb.append("): ");sb.append(r.toString());Slog.v(TAG_SERVICE, sb.toString());}} else if (DEBUG_DELAYED_STARTS) {if (callerFg || fgRequired) {Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="+ callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r);} else if (r.app != null) {Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);} else {Slog.v(TAG_SERVICE,"Not potential delay (user " + r.userId + " not started): " + r);}}ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);return cmp;}

这里对于fgRequired为true的情况其实也只是赋值了一下

r.fgRequired = fgRequired;

后面流程和startService的onCreate一样的,没啥好重复说的,直到流程到了onStartCommand有点不一样了。

onStartCommand的开始是

   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 correctfinal boolean inDestroying = mDestroyingServices.contains(r);for (int i = 0; i < args.size(); i++) {serviceDoneExecutingLocked(r, inDestroying, inDestroying);}if (caughtException instanceof TransactionTooLargeException) {throw (TransactionTooLargeException)caughtException;}}}

这里面有下面一句比较关键

            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;}}
    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);}

这里表示在onStartCommand的流程开始时会设置一个5s的anr timeout,超过5s就会anr并且停止Service。

    // 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;

也附带看下AMS是怎么处理这个消息的:

    final class MainHandler extends Handler {public MainHandler(Looper looper) {super(looper, null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case UPDATE_CONFIGURATION_MSG: {final ContentResolver resolver = mContext.getContentResolver();Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,msg.arg1);} break;case GC_BACKGROUND_PROCESSES_MSG: {synchronized (ActivityManagerService.this) {performAppGcsIfAppropriateLocked();}} break;case SERVICE_TIMEOUT_MSG: {mServices.serviceTimeout((ProcessRecord)msg.obj);} break;case SERVICE_FOREGROUND_TIMEOUT_MSG: {mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);} break;

还是扔回来调用ActiveServices来处理。

    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()");}}

直接停止Service并且通过AMS使应用 anr,提示“Context.startForegroundService() did not then call Service.startForeground()”。

 

3. startForeground流程梳理

 

3.1 Service

    /*** If your service is started (running through {@link Context#startService(Intent)}), then* also make this service run in the foreground, supplying the ongoing* notification to be shown to the user while in this state.* By default started services are background, meaning that their process won't be given* foreground CPU scheduling (unless something else in that process is foreground) and,* if the system needs to kill them to reclaim more memory (such as to display a large page in a* web browser), they can be killed without too much harm.  You use* {@link #startForeground} if killing your service would be disruptive to the user, such as* if your service is performing background music playback, so the user* would notice if their music stopped playing.** <p>Note that calling this method does <em>not</em> put the service in the started state* itself, even though the name sounds like it.  You must always call* {@link #startService(Intent)} first to tell the system it should keep the service running,* and then use this method to tell it to keep it running harder.</p>** @param id The identifier for this notification as per* {@link NotificationManager#notify(int, Notification)* NotificationManager.notify(int, Notification)}; must not be 0.* @param notification The Notification to be displayed.* * @see #stopForeground(boolean)*/public final void startForeground(int id, Notification notification) {try {mActivityManager.setServiceForeground(new ComponentName(this, mClassName), mToken, id,notification, 0);} catch (RemoteException ex) {}}

 

3.2 AMS

    @Overridepublic void setServiceForeground(ComponentName className, IBinder token,int id, Notification notification, int flags) {synchronized(this) {mServices.setServiceForegroundLocked(className, token, id, notification, flags);}}

 

3.3 ActiveServices

    public void setServiceForegroundLocked(ComponentName className, IBinder token,int id, Notification notification, int flags) {final int userId = UserHandle.getCallingUserId();final long origId = Binder.clearCallingIdentity();try {ServiceRecord r = findServiceLocked(className, token, userId);if (r != null) {setServiceForegroundInnerLocked(r, id, notification, flags);}} finally {Binder.restoreCallingIdentity(origId);}}

 

   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;}}}}

setServiceForegroundInnerLocked这个方法很关键,关键代码分如下几部分梳理:

1)首先Service创建的notification id不能是0并且notification不能是null,并且Android O 如果创建通知的话还要设置channel的。

        if (id != 0) {if (notification == null) {throw new IllegalArgumentException("null notification");}

 

2)设置r.fgRequired为false,表明已经设置Service为foreground了,不需要了。并且移除了之前发给AMS的SERVICE_FOREGROUND_TIMEOUT_MSG,这样就不会anr了。但是这句话要在发出消息的5s之内调用到,否则还是会anr。

由于消息是在onStartCommand流程开始时发出的,如果我们的Service在onCreate就开始调用startForeground,时限也许会长于5s。但由于一个是AMS的流程,一个是APP的流程,是异步的,长也长不了多少,不是等onCreate执行完再发的。

            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);}

3)取消通知,但会检查应用的其他前台Service是否有相同的notification id,如果一样,则不取消。

            if (r.foregroundId != id) {cancelForegroundNotificationLocked(r);r.foregroundId = id;}
    private void cancelForegroundNotificationLocked(ServiceRecord r) {if (r.foregroundId != 0) {// First check to see if this app has any other active foreground services// with the same notification ID.  If so, we shouldn't actually cancel it,// because that would wipe away the notification that still needs to be shown// due the other service.ServiceMap sm = getServiceMapLocked(r.userId);if (sm != null) {for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {ServiceRecord other = sm.mServicesByName.valueAt(i);if (other != r && other.foregroundId == r.foregroundId&& other.packageName.equals(r.packageName)) {// Found one!  Abort the cancel.return;}}}r.cancelNotification();}}

4)将Service设为前台Service

            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;}

 

4. Service “Context.startForegroundService() did not then call Service.startForeground()” anr原因

结合之前2和3对startForegroundService和startForeground的分析,可以很清楚的判断出Service anr原因:

Service没有在AMS SERVICE_FOREGROUND_TIMEOUT_MSG发出5s内将其移除,简单地对应于应用描述就是调用startForegroundService后5s内没有及时在Service内调用startForeground。(5s在这的描述是不精确的,但差不了多少,startForeground建议在Service onCreate一开始就调用,也许能争取点时间)

 

5. Service “Context.startForegroundService() did not then call Service.startForeground()” crash原因

之前都梳理的anr,其实Android O这边还埋了个crash的坑。如果5s之内没有及时调用startForeground,然后Service destroy了,比如stopService,那Android O很人性化地不让Service anr了,让Service crash。

    private final void bringDownServiceLocked(ServiceRecord r) {//Slog.i(TAG, "Bring down service:");//r.dump("  ");// Report to all of the connections that the service is no longer// available.for (int conni=r.connections.size()-1; conni>=0; conni--) {ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);for (int i=0; i<c.size(); i++) {ConnectionRecord cr = c.get(i);// There is still a connection to the service that is// being brought down.  Mark it as dead.cr.serviceDead = true;try {cr.conn.connected(r.name, null, true);} catch (Exception e) {Slog.w(TAG, "Failure disconnecting service " + r.name +" to connection " + c.get(i).conn.asBinder() +" (in " + c.get(i).binding.client.processName + ")", e);}}}// Tell the service that it has been unbound.if (r.app != null && r.app.thread != null) {for (int i=r.bindings.size()-1; i>=0; i--) {IntentBindRecord ibr = r.bindings.valueAt(i);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr+ ": hasBound=" + ibr.hasBound);if (ibr.hasBound) {try {bumpServiceExecutingLocked(r, false, "bring down unbind");mAm.updateOomAdjLocked(r.app, true);ibr.hasBound = false;ibr.requested = false;r.app.thread.scheduleUnbindService(r,ibr.intent.getIntent());} catch (Exception e) {Slog.w(TAG, "Exception when unbinding service "+ r.shortName, e);serviceProcessGoneLocked(r);}}}}// Check to see if the service had been started as foreground, but being// brought down before actually showing a notification.  That is not allowed.if (r.fgRequired) {Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "+ r);r.fgRequired = false;r.fgWaiting = false;mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);if (r.app != null) {Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);msg.obj = r.app;mAm.mHandler.sendMessage(msg);}}if (DEBUG_SERVICE) {RuntimeException here = new RuntimeException();here.fillInStackTrace();Slog.v(TAG_SERVICE, "Bringing down " + r + " " + r.intent, here);}r.destroyTime = SystemClock.uptimeMillis();if (LOG_SERVICE_START_STOP) {EventLogTags.writeAmDestroyService(r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);}final ServiceMap smap = getServiceMapLocked(r.userId);ServiceRecord found = smap.mServicesByName.remove(r.name);// Note when this method is called by bringUpServiceLocked(), the service is not found// in mServicesByName and found will be null.if (found != null && found != r) {// This is not actually the service we think is running...  this should not happen,// but if it does, fail hard.smap.mServicesByName.put(r.name, found);throw new IllegalStateException("Bringing down " + r + " but actually running "+ found);}smap.mServicesByIntent.remove(r.intent);r.totalRestartCount = 0;unscheduleServiceRestartLocked(r, 0, true);// Also make sure it is not on the pending list.for (int i=mPendingServices.size()-1; i>=0; i--) {if (mPendingServices.get(i) == r) {mPendingServices.remove(i);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed pending: " + r);}}cancelForegroundNotificationLocked(r);if (r.isForeground) {decActiveForegroundAppLocked(smap, r);}r.isForeground = false;r.foregroundId = 0;r.foregroundNoti = null;// Clear start entries.r.clearDeliveredStartsLocked();r.pendingStarts.clear();if (r.app != null) {synchronized (r.stats.getBatteryStats()) {r.stats.stopLaunchedLocked();}r.app.services.remove(r);if (r.whitelistManager) {updateWhitelistManagerLocked(r.app);}if (r.app.thread != null) {updateServiceForegroundLocked(r.app, false);try {bumpServiceExecutingLocked(r, false, "destroy");mDestroyingServices.add(r);r.destroying = true;mAm.updateOomAdjLocked(r.app, true);r.app.thread.scheduleStopService(r);} catch (Exception e) {Slog.w(TAG, "Exception when destroying service "+ r.shortName, e);serviceProcessGoneLocked(r);}} else {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed service that has no process: " + r);}} else {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed service that is not running: " + r);}if (r.bindings.size() > 0) {r.bindings.clear();}if (r.restarter instanceof ServiceRestarter) {((ServiceRestarter)r.restarter).setService(null);}int memFactor = mAm.mProcessStats.getMemFactorLocked();long now = SystemClock.uptimeMillis();if (r.tracker != null) {r.tracker.setStarted(false, memFactor, now);r.tracker.setBound(false, memFactor, now);if (r.executeNesting == 0) {r.tracker.clearCurrentOwner(r, false);r.tracker = null;}}smap.ensureNotStartingBackgroundLocked(r);}

上面代码中让Service用crash代替anr的代码如下:

        // Check to see if the service had been started as foreground, but being// brought down before actually showing a notification.  That is not allowed.if (r.fgRequired) {Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "+ r);r.fgRequired = false;r.fgWaiting = false;mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);if (r.app != null) {Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);msg.obj = r.app;mAm.mHandler.sendMessage(msg);}}

 

5. 总结

其实startForegroundService的流程和startService总体来讲是差不多的,区别就在于5s 的anr时间限制内Service要调用startForeground。

如果没有及时调用,则会anr。又或者5s之内Service destroy了,那就来一个差不多的crash。

anr和crash中都会带有如下信息:“Context.startForegroundService() did not then call Service.startForeground()”

这篇关于(七十四)Android O Service启动流程梳理——startForegroundService的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511422

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存