前言:最近在处理anr问题的时候迫切需要搞清楚service的启动流程,抽时间梳理一下。

1.service启动简述

service启动分三种,比较简单的就是startService,Android O用于后台应用启动前台服务的startForegroundService和绑定服务的bindService。本篇继(六十四)Android O Service启动流程梳理——startService 继续梳理bindService方法。

2.流程分析

07-14 13:08:12.852 3992-3992/com.example.demo_69_service D/demo69_service: onCreatejava.lang.RuntimeExceptionat com.example.demo_69_service.MyService.onCreate(MyService.java:31)at android.app.ActivityThread.handleCreateService(ActivityThread.java:3442)at android.app.ActivityThread.-wrap4(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1711)at android.os.Handler.dispatchMessage(Handler.java:105)at android.os.Looper.loop(Looper.java:171)at android.app.ActivityThread.main(ActivityThread.java:6684)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:246)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
07-14 13:08:12.856 3992-3992/com.example.demo_69_service D/demo69_service: onBindjava.lang.RuntimeExceptionat com.example.demo_69_service.MyService.onBind(MyService.java:38)at android.app.ActivityThread.handleBindService(ActivityThread.java:3469)at android.app.ActivityThread.-wrap2(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1716)at android.os.Handler.dispatchMessage(Handler.java:105)at android.os.Looper.loop(Looper.java:171)at android.app.ActivityThread.main(ActivityThread.java:6684)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:246)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
07-14 13:08:12.857 3992-3992/com.example.demo_69_service D/demo69_activity: onServiceConnectedjava.lang.RuntimeExceptionat com.example.demo_69_service.MainActivity$1.onServiceConnected(MainActivity.java:22)at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1645)at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1674)at android.os.Handler.handleCallback(Handler.java:789)at android.os.Handler.dispatchMessage(Handler.java:98)at android.os.Looper.loop(Looper.java:171)at android.app.ActivityThread.main(ActivityThread.java:6684)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:246)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)

之前(六十九) 探究Service 各种启动方式的深层调用 有获取到小米mix2 bindService启动Service的流程和堆栈。

如下是bindService的时序图。

红框标注是anr的限制执行区间。

2.1 ContextImpl

    @Overridepublic boolean bindService(Intent service, ServiceConnection conn,int flags) {warnIfCallingFromSystemProcess();return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),Process.myUserHandle());//handler传递的是主线程的}
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handlerhandler, UserHandle user) {// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.IServiceConnection sd;if (conn == null) {throw new IllegalArgumentException("connection is null");}if (mLoadedApk != null) {sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);} else {throw new RuntimeException("Not supported in system context");}validateServiceIntent(service);//需要显示调用Servicetry {IBinder token = getActivityToken();if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null&& mLoadedApk.getApplicationInfo().targetSdkVersion< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {flags |= BIND_WAIVE_PRIORITY;}service.prepareToLeaveProcess(this);int res = ActivityManager.getService().bindService(mMainThread.getApplicationThread(), getActivityToken(), service,service.resolveTypeIfNeeded(getContentResolver()),sd, flags, getOpPackageName(), user.getIdentifier());if (res < 0) {throw new SecurityException("Not allowed to bind to service " + service);}return res != 0;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

下面会调用到AMS,在此之前看下LoadedApk这段初始化IServiceConnection的内部实现。

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,Context context, Handler handler, int flags) {synchronized (mServices) {LoadedApk.ServiceDispatcher sd = null;ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);if (map != null) {if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);sd = map.get(c);}if (sd == null) {sd = new ServiceDispatcher(c, context, handler, flags);if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);if (map == null) {map = new ArrayMap<>();mServices.put(context, map);}map.put(c, sd);} else {sd.validate(context, handler);}return sd.getIServiceConnection();}}

这边相当于将ServiceConnection c, Context context, Handler handler, int flags用ServiceDispatcher封装了起来,然后将自己以InnerConnection的形式返回。

        ServiceDispatcher(ServiceConnection conn,Context context, Handler activityThread, int flags) {mIServiceConnection = new InnerConnection(this);mConnection = conn;mContext = context;mActivityThread = activityThread;mLocation = new ServiceConnectionLeaked(null);mLocation.fillInStackTrace();mFlags = flags;}
        private static class InnerConnection extends IServiceConnection.Stub {final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);}public void connected(ComponentName name, IBinder service, boolean dead)throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {sd.connected(name, service, dead);}}}

InnerConnection是私有的静态内部类,内部用虚引用关联sd,这让我联想到了Handler为了防止内存泄露也是这么干的。

这边有个connected方法可以稍微瞄一眼,会继而调用到doConnected,回调到我们初始化的ServiceConnection。

        public void doConnected(ComponentName name, IBinder service, boolean dead) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;synchronized (this) {if (mForgotten) {// We unbound before receiving the connection; ignore// any connection received.return;}old = mActiveConnections.get(name);if (old != null && old.binder == service) {// Huh, already have this one.  Oh well!return;}if (service != null) {// A new service is being connected... set it all up.info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {// This service was dead before we got it...  just// don't do anything with it.mActiveConnections.remove(name);return;}} else {// The named service is being disconnected... clean up.mActiveConnections.remove(name);}if (old != null) {old.binder.unlinkToDeath(old.deathMonitor, 0);}}// If there was an old service, it is now disconnected.if (old != null) {mConnection.onServiceDisconnected(name);}if (dead) {mConnection.onBindingDied(name);}// If there is a new service, it is now connected.if (service != null) {mConnection.onServiceConnected(name, service);}}

2.2 ActivityManagerService

    public int bindService(IApplicationThread caller, IBinder token, Intent service,String resolvedType, IServiceConnection connection, int flags, String callingPackage,int userId) throws TransactionTooLargeException {enforceNotIsolatedCaller("bindService");// 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");}synchronized(this) {return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, callingPackage, userId);}}

将逻辑剥离到ActiveServices中去。

2.3 ActiveServices

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,String resolvedType, final IServiceConnection connection, int flags,String callingPackage, final int userId) throws TransactionTooLargeException {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service+ " type=" + resolvedType + " conn=" + connection.asBinder()+ " flags=0x" + Integer.toHexString(flags));final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);if (callerApp == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + Binder.getCallingPid()+ ") when binding service " + service);}ActivityRecord activity = null;if (token != null) {activity = ActivityRecord.isInStackLocked(token);if (activity == null) {Slog.w(TAG, "Binding with unknown activity: " + token);return 0;}}int clientLabel = 0;PendingIntent clientIntent = null;final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;if (isCallerSystem) {// Hacky kind of thing -- allow system stuff to tell us// what they are, so we can report this elsewhere for// others to know why certain services are running.service.setDefusable(true);clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);if (clientIntent != null) {clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);if (clientLabel != 0) {// There are no useful extras in the intent, trash them.// System code calling with this stuff just needs to know// this will happen.service = service.cloneFilter();}}}if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,"BIND_TREAT_LIKE_ACTIVITY");}if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {throw new SecurityException("Non-system caller " + caller + " (pid=" + Binder.getCallingPid()+ ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);}final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;ServiceLookupResult res =retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),Binder.getCallingUid(), userId, true, callerFg, isBindExternal);if (res == null) {return 0;}if (res.record == null) {return -1;}ServiceRecord s = res.record;boolean permissionsReviewRequired = false;// If permissions need a review before any of the app components can run,// we schedule binding to the service but do not start its process, then// we launch a review activity to which is passed a callback to invoke// when done to start the bound service's process to completing the binding.if (mAm.mPermissionReviewRequired) {if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(s.packageName, s.userId)) {permissionsReviewRequired = true;// Show a permission review UI only for binding from a foreground appif (!callerFg) {Slog.w(TAG, "u" + s.userId + " Binding to a service in package"+ s.packageName + " requires a permissions review");return 0;}final ServiceRecord serviceRecord = s;final Intent serviceIntent = service;RemoteCallback callback = new RemoteCallback(new RemoteCallback.OnResultListener() {@Overridepublic void onResult(Bundle result) {synchronized(mAm) {final long identity = Binder.clearCallingIdentity();try {if (!mPendingServices.contains(serviceRecord)) {return;}// If there is still a pending record, then the service// binding request is still valid, so hook them up. We// proceed only if the caller cleared the review requirement// otherwise we unbind because the user didn't approve.if (!mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(serviceRecord.packageName,serviceRecord.userId)) {try {bringUpServiceLocked(serviceRecord,serviceIntent.getFlags(),callerFg, false, false);} catch (RemoteException e) {/* ignore - local call */}} else {unbindServiceLocked(connection);}} finally {Binder.restoreCallingIdentity(identity);}}}});final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);if (DEBUG_PERMISSIONS_REVIEW) {Slog.i(TAG, "u" + s.userId + " Launching permission review for package "+ s.packageName);}mAm.mHandler.post(new Runnable() {@Overridepublic void run() {mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));}});}}final long origId = Binder.clearCallingIdentity();try {if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "BIND SERVICE WHILE RESTART PENDING: "+ s);}if ((flags&Context.BIND_AUTO_CREATE) != 0) {s.lastActivity = SystemClock.uptimeMillis();if (!s.hasAutoCreateConnections()) {// This is the first binding, let the tracker know.ServiceState stracker = s.getTracker();if (stracker != null) {stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),s.lastActivity);}}}mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,s.appInfo.uid, s.name, s.processName);// Once the apps have become associated, if one of them is caller is ephemeral// the target app should now be able to see the calling appmAm.grantEphemeralAccessLocked(callerApp.userId, service,s.appInfo.uid, UserHandle.getAppId(callerApp.uid));AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);ConnectionRecord c = new ConnectionRecord(b, activity,connection, flags, clientLabel, clientIntent);IBinder binder = connection.asBinder();ArrayList<ConnectionRecord> clist = s.connections.get(binder);if (clist == null) {clist = new ArrayList<ConnectionRecord>();s.connections.put(binder, clist);}clist.add(c);b.connections.add(c);if (activity != null) {if (activity.connections == null) {activity.connections = new HashSet<ConnectionRecord>();}activity.connections.add(c);}b.client.connections.add(c);if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {b.client.hasAboveClient = true;}if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {s.whitelistManager = true;}if (s.app != null) {updateServiceClientActivitiesLocked(s.app, c, true);}clist = mServiceConnections.get(binder);if (clist == null) {clist = new ArrayList<ConnectionRecord>();mServiceConnections.put(binder, clist);}clist.add(c);            if ((flags&Context.BIND_AUTO_CREATE) != 0) {s.lastActivity = SystemClock.uptimeMillis();if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,permissionsReviewRequired) != null) {return 0;}}if (s.app != null) {if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {s.app.treatLikeActivity = true;}if (s.whitelistManager) {s.app.whitelistManager = true;}// This could have made the service more important.mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities|| s.app.treatLikeActivity, b.client);mAm.updateOomAdjLocked(s.app, true);}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b+ ": received=" + b.intent.received+ " apps=" + b.intent.apps.size()+ " doRebind=" + b.intent.doRebind);if (s.app != null && b.intent.received) {// Service is already running, so we can immediately// publish the connection.try {c.conn.connected(s.name, b.intent.binder, false);} catch (Exception e) {Slog.w(TAG, "Failure sending service " + s.shortName+ " to connection " + c.conn.asBinder()+ " (in " + c.binding.client.processName + ")", e);}// If this is the first app connected back to this binding,// and the service had previously asked to be told when// rebound, then do so.if (b.intent.apps.size() == 1 && b.intent.doRebind) {requestServiceBindingLocked(s, b.intent, callerFg, true);}} else if (!b.intent.requested) {requestServiceBindingLocked(s, b.intent, callerFg, false);}getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);} finally {Binder.restoreCallingIdentity(origId);}return 1;}

首先,我们bindService一般设置flag为Context.BIND_AUTO_CREATE,则会走进如下方法中去。

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {s.lastActivity = SystemClock.uptimeMillis();if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,permissionsReviewRequired) != null) {return 0;}}

这边是复用了startService的启动流程,会走onCreate和onStartCommand的流程,如果onCreate调用过了,则只走onStartCommand的流程。但是这和我们打印的log不符,我们是不会走onStartCommand的。另外我们也发现了重复调用bindService,onBind也不会重复调用的,说明这些都有控制,继续往下看。

   private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,boolean whileRestarting, boolean permissionsReviewRequired)throws TransactionTooLargeException {//Slog.i(TAG, "Bring up service:");//r.dump("  ");        if (r.app != null && r.app.thread != null) {sendServiceArgsLocked(r, execInFg, false);return null;}if (!whileRestarting && mRestartingServices.contains(r)) {// If waiting for a restart, then do nothing.return null;}if (DEBUG_SERVICE) {Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired);}// We are now bringing the service up, so no longer in the// restarting state.if (mRestartingServices.remove(r)) {clearRestartingIfNeededLocked(r);}// Make sure this service is no longer considered delayed, we are starting it now.if (r.delayed) {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);getServiceMapLocked(r.userId).mDelayedStartList.remove(r);r.delayed = false;}// Make sure that the user who owns this service is started.  If not,// we don't want to allow it to run.if (!mAm.mUserController.hasStartedUserState(r.userId)) {String msg = "Unable to launch app "+ r.appInfo.packageName + "/"+ r.appInfo.uid + " for service "+ r.intent.getIntent() + ": user " + r.userId + " is stopped";Slog.w(TAG, msg);bringDownServiceLocked(r);return msg;}// Service is now being launched, its package can't be stopped.try {AppGlobals.getPackageManager().setPackageStoppedState(r.packageName, false, r.userId);} catch (RemoteException e) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ r.packageName + ": " + e);}final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;final String procName = r.processName;String hostingType = "service";ProcessRecord app;if (!isolated) {app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid+ " app=" + app);if (app != null && app.thread != null) {try {app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);realStartServiceLocked(r, app, execInFg);return null;} catch (TransactionTooLargeException e) {throw e;} catch (RemoteException e) {Slog.w(TAG, "Exception when starting service " + r.shortName, e);}// If a dead object exception was thrown -- fall through to// restart the application.}} else {// If this service runs in an isolated process, then each time// we call startProcessLocked() we will get a new isolated// process, starting another process if we are currently waiting// for a previous process to come up.  To deal with this, we store// in the service any current isolated process it is running in or// waiting to have come up.app = r.isolatedProc;if (WebViewZygote.isMultiprocessEnabled()&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {hostingType = "webview_service";}}// Not running -- get it started, and enqueue this service record// to be executed when the app comes up.if (app == null && !permissionsReviewRequired) {if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,hostingType, r.name, false, isolated, false)) == null) {String msg = "Unable to launch app "+ r.appInfo.packageName + "/"+ r.appInfo.uid + " for service "+ r.intent.getIntent() + ": process is bad";Slog.w(TAG, msg);bringDownServiceLocked(r);return msg;}if (isolated) {r.isolatedProc = app;}}if (r.fgRequired) {if (DEBUG_FOREGROUND_SERVICE) {Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)+ " for fg-service launch");}mAm.tempWhitelistUidLocked(r.appInfo.uid,SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch");}if (!mPendingServices.contains(r)) {mPendingServices.add(r);}if (r.delayedStop) {// Oh and hey we've already been asked to stop!r.delayedStop = false;if (r.startRequested) {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,"Applying delayed stop (in bring up): " + r);stopServiceLocked(r);}}return null;}
   private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {if (app.thread == null) {throw new RemoteException();}if (DEBUG_MU)Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid+ ", ProcessRecord.uid = " + app.uid);r.app = app;r.restartTime = r.lastActivity = SystemClock.uptimeMillis();final boolean newService = app.services.add(r);bumpServiceExecutingLocked(r, execInFg, "create");mAm.updateLruProcessLocked(app, false, null);updateServiceForegroundLocked(r.app, /* oomAdj= */ false);mAm.updateOomAdjLocked();boolean created = false;try {if (LOG_SERVICE_START_STOP) {String nameTerm;int lastPeriod = r.shortName.lastIndexOf('.');nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;EventLogTags.writeAmCreateService(r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);}synchronized (r.stats.getBatteryStats()) {r.stats.startLaunchedLocked();}mAm.notifyPackageUse(r.serviceInfo.packageName,PackageManager.NOTIFY_PACKAGE_USE_SERVICE);app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);r.postNotification();created = true;} catch (DeadObjectException e) {Slog.w(TAG, "Application dead when creating service " + r);mAm.appDiedLocked(app);throw e;} finally {if (!created) {// Keep the executeNesting count accurate.final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);// Cleanup.if (newService) {app.services.remove(r);r.app = null;}// Retry.if (!inDestroying) {scheduleServiceRestartLocked(r, false);}}}if (r.whitelistManager) {app.whitelistManager = true;}requestServiceBindingsLocked(r, execInFg);updateServiceClientActivitiesLocked(app, null, true);// If the service is in the started state, and there are no// pending arguments, then fake up one so its onStartCommand() will// be called.
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),null, null, 0));}sendServiceArgsLocked(r, execInFg, true);if (r.delayed) {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);getServiceMapLocked(r.userId).mDelayedStartList.remove(r);r.delayed = false;}if (r.delayedStop) {// Oh and hey we've already been asked to stop!r.delayedStop = false;if (r.startRequested) {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,"Applying delayed stop (from start): " + r);stopServiceLocked(r);}}}

这里有两块代码是与onBind和onStartCommand的调用相关的

1.onBind

        requestServiceBindingsLocked(r, execInFg);

2.onStartCommand

这边有控制,如果pendingStarts是空的话走进去会立刻返回的,不会调用到onStartCommand,bindService应该就是如此。

       // If the service is in the started state, and there are no// pending arguments, then fake up one so its onStartCommand() will// be called.if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),null, null, 0));}sendServiceArgsLocked(r, execInFg, true);
    private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,boolean oomAdjusted) throws TransactionTooLargeException {final int N = r.pendingStarts.size();if (N == 0) {return;}

回头来看下requestServiceBindingLocked方法。

    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)throws TransactionTooLargeException {for (int i=r.bindings.size()-1; i>=0; i--) {IntentBindRecord ibr = r.bindings.valueAt(i);if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {break;}}}

如果binding的尺寸大于0,才会继续往下走,这应该就是startService后onBind不会被回调的原因。

之前在bindServiceLocked中我们会往这个集合里加成员。

AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
    public AppBindRecord retrieveAppBindingLocked(Intent intent,ProcessRecord app) {Intent.FilterComparison filter = new Intent.FilterComparison(intent);IntentBindRecord i = bindings.get(filter);if (i == null) {i = new IntentBindRecord(this, filter);bindings.put(filter, i);}AppBindRecord a = i.apps.get(app);if (a != null) {return a;}a = new AppBindRecord(this, i, app);i.apps.put(app, a);return a;}

这边继续往下梳理requestServiceBindingLocked

    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,boolean execInFg, boolean rebind) throws TransactionTooLargeException {if (r.app == null || r.app.thread == null) {// If service is not currently running, can't yet bind.return false;}if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested+ " rebind=" + rebind);if ((!i.requested || rebind) && i.apps.size() > 0) {try {bumpServiceExecutingLocked(r, execInFg, "bind");r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,r.app.repProcState);if (!rebind) {i.requested = true;}i.hasBound = true;i.doRebind = false;} catch (TransactionTooLargeException e) {// Keep the executeNesting count accurate.if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);throw e;} catch (RemoteException e) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);// Keep the executeNesting count accurate.final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);return false;}}return true;}

bumpServiceExecutingLocked已经很熟悉了,设置timeout时限。可以得出结论Service的create、start和bind过程是分别设置timeout时限的。

之后继续调用scheduleBindService方法。

2.4 ActivityThread

        public final void scheduleBindService(IBinder token, Intent intent,boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();s.token = token;s.intent = intent;s.rebind = rebind;if (DEBUG_SERVICE)Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());sendMessage(H.BIND_SERVICE, s);}
                case BIND_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");handleBindService((BindServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;
    private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);if (DEBUG_SERVICE)Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);if (s != null) {try {data.intent.setExtrasClassLoader(s.getClassLoader());data.intent.prepareToEnterProcess();try {if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);ActivityManager.getService().publishService(data.token, data.intent, binder);} else {
                        s.onRebind(data.intent);ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}ensureJitEnabled();} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(s, e)) {throw new RuntimeException("Unable to bind to service " + s+ " with " + data.intent + ": " + e.toString(), e);}}}}

2.5 AMS

    public void publishService(IBinder token, Intent intent, IBinder service) {// Refuse possible leaked file descriptorsif (intent != null && intent.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}synchronized(this) {if (!(token instanceof ServiceRecord)) {throw new IllegalArgumentException("Invalid service token");}mServices.publishServiceLocked((ServiceRecord)token, intent, service);}}

2.6 ActiveServices

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {final long origId = Binder.clearCallingIdentity();try {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r+ " " + intent + ": " + service);if (r != null) {Intent.FilterComparison filter= new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {b.binder = service;b.requested = true;b.received = true;for (int conni=r.connections.size()-1; conni>=0; conni--) {ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);for (int i=0; i<clist.size(); i++) {ConnectionRecord c = clist.get(i);if (!filter.equals(c.binding.intent.intent)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Not publishing to: " + c);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Published intent: " + intent);continue;}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);try {c.conn.connected(r.name, service, false);} catch (Exception e) {Slog.w(TAG, "Failure sending service " + r.name +" to connection " + c.conn.asBinder() +" (in " + c.binding.client.processName + ")", e);}}}}serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);}} finally {Binder.restoreCallingIdentity(origId);}}

回调一开始说的ServiceConnection的onServiceConnected方法,移除timeout时限。这边的ConnectionRecord也是一开始的bindServiceLocked加载的clist。之后虽然会迭代所有ServiceConnection但由于filter不一样,只会调用bindService对应的conn。

           AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);ConnectionRecord c = new ConnectionRecord(b, activity,connection, flags, clientLabel, clientIntent);IBinder binder = connection.asBinder();ArrayList<ConnectionRecord> clist = s.connections.get(binder);if (clist == null) {clist = new ArrayList<ConnectionRecord>();s.connections.put(binder, clist);}clist.add(c);b.connections.add(c);if (activity != null) {if (activity.connections == null) {activity.connections = new HashSet<ConnectionRecord>();}activity.connections.add(c);}b.client.connections.add(c);

3.总结

(七十)Android O Service启动流程梳理——bindService相关推荐

  1. (四十四)Android O WiFi启动流程梳理

    前言:最近又重新拿起来WiFi模块,从WiFi 各个流程梳理开始复习一下. 参考博客:https://blog.csdn.net/csdn_of_coder/article/details/51541 ...

  2. 深入分析Android 9.0源代码——Service启动流程(startService方式)

    引言 点击此处查看<深入分析Android 9.0源代码>系列的组织结构和相关说明. 1 应用进程发起启动请求 本章的调用流程如下图所示: (Context)ContextWrapperC ...

  3. Android service启动流程分析.

    文章仅仅用于个人的学习记录,基本上内容都是网上各个大神的杰作,此处摘录过来以自己的理解学习方式记录一下. 参考链接: https://my.oschina.net/youranhongcha/blog ...

  4. 结合源码探讨Android系统的启动流程

    结合源码探讨Android系统的启动流程 由于本人能力有限,所考虑或者疏忽错漏的地方或多或少应该存在.同时,Android从启动过程开始,实际上就涉及多个技术难点和多种通信机制的知识点. 基于上面两个 ...

  5. Android系统的启动流程简要分析

    这是我结合网上的资料以及自己分析android4.4的源码整理的笔记,对整个安卓系统的流程启动进行了梳理,很多细节并未展开,只是简要的进行了介绍. 一.Android系统的架构介绍 Android的整 ...

  6. c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...

    在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析​zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...

  7. Android Q 开机启动流程

    https://www.it610.com/article/1304931662924124160.htm Android Q 开机启动流程 开机启动概述: step 1: 上电开机 长按power键 ...

  8. Android开机向导启动流程分析

    Android开机向导启动流程 首先来看Android启动流程: 1.Bootloader(系统启动加载器,将Linux加载到RAM): 2.Kernel 3.init进程 4.Zygote(Zygo ...

  9. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

最新文章

  1. VISTA中注册表项LEGACY_****的删除
  2. 40行python开发一个区块链
  3. 50 年人类登月史:那些不为人知的故事
  4. C语言 | 编写一个使用指针的c函数,交换数组a和数组b中的对应元素
  5. C++在哪几种情况只能用intialization list 而不能用assignment?
  6. 学习使用bilstm_crf_model出现的bug
  7. 螺丝孔槽中的螺丝拧花了的物理原理分析
  8. 软件架构设计箴言理解
  9. Chemical table CFR500 div2D(并查集)
  10. JavaScript + CSS3 实现的海报画廊特效
  11. Kibana插件sentinl使用教程
  12. 天地图JS API制作专题图
  13. arcgis重心迁移分析,ArcGIS支持下三峡库区适度人口重心迁移研究
  14. Go语言 gorutine和channel协同工作经典应用案例 (Golang经典编程案例)
  15. 163VIP邮箱登录入口有哪些?VIP163邮箱怎么登陆?
  16. Python:绘制数学图形
  17. Sky光遇云野光之翼在哪获得
  18. 使用理想低通滤波器对图像进行处理显示
  19. 03-QNX Shell常用指令
  20. 为什么决策树模型不考虑变量之间的共线性?

热门文章

  1. 均匀分布 卡方分布_总结 | 深度学习那些需要掌握的概率分布
  2. webpack大拷问
  3. 【Linux应用】goahead5.1.1移植
  4. 谜之wxs,uni-app如何用它大幅提升性能
  5. 2009年下半年11月份信息系统项目管理师上午试题答案(分析与解答)(第5次修订 修订时间:2010年03月14日02时02分)
  6. ​详细教程:如何使用代理服务器进行网页抓取?
  7. python毕业设计 深度学习卫星遥感图像检测与识别 opencv 目标检测
  8. linux开发stm32和keil的区别,STM32cubeIDE 和 KEIL 的区别
  9. strcat strcpy的实现
  10. 如何管理国外同事、下属和老板,取全球资源建好团队