Android service 启动篇之 bindService

系列博文:

Android 中service 详解

Android service 启动篇之 startService

Android service 启动篇之 bindService

Android service 启动篇之 startForegroundService

基于版本:Android O

0. 前言

通过source code 分析了AMS 中service 的启动过程,bindService 相对复杂一点,主要是多了一些service 和app的绑定关系处理。本文继续结合source code 来剖析。

1. 入口函数

1.1 bindService()

    @Overridepublic boolean bindService(Intent service, ServiceConnection conn,int flags) {warnIfCallingFromSystemProcess();return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),Process.myUserHandle());}/** @hide */@Overridepublic boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,UserHandle user) {return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);}

参数:

  • service 的intent
  • bind 时候的ServiceConnection
  • bind service 用到的flag,例如BIND_AUTO_CREATE
  • 当前的user

1.2 bindServiceCommon()

    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 (mPackageInfo != null) {sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);} else {throw new RuntimeException("Not supported in system context");}validateServiceIntent(service);try {IBinder token = getActivityToken();if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null&& mPackageInfo.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();}}
  • ServiceConnection 不能为null,必须要创建
  • android L 之后的版本不能隐式启动service
  • 调用AMS 的bindService(),calling 的thread,calling 的package 都会传入

注意的是bindService 第 5 个参数sd,传入到AMS 不在是client 的ServiceConnection,而是ServiceDispatcher,详细可以看LoadedApk.ServiceDispatcher,每个应用都有这样的dispatcher 会注册到AMS 中,用于AMS 的回调。

    static final class ServiceDispatcher {private final ServiceDispatcher.InnerConnection mIServiceConnection;private final ServiceConnection mConnection;private final Context mContext;private final Handler mActivityThread;private final ServiceConnectionLeaked mLocation;private final int mFlags;private RuntimeException mUnbindLocation;private boolean mForgotten;private static class ConnectionInfo {IBinder binder;IBinder.DeathRecipient deathMonitor;}private static class InnerConnection extends IServiceConnection.Stub {final WeakReference mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference(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);}}}......public void connected(ComponentName name, IBinder service, boolean dead) {if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0, dead));} else {doConnected(name, service, dead);}}......public void doConnected(ComponentName name, IBinder service, boolean dead) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;......// 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);}}......}

最终会调用到应用中定义的ServiceConnection 的onServiceDisconnected() 和 onServiceConnected(), 分析AMS 的时候会继续说明这里用处。

2. bindServiceLocked

通过上面的bindService,最终会调用到ActiveServices 中的bindServiceLocked()

2.1 retrieveServiceLocked()

这个在 Android service 启动篇之 startService 的 3.2 节中已经大概解释了下 ,如果app 已经service 在内部进行运行,那么需要满足下面几个条件:

  • service 需要置上flag ServiceInfo.FLAG_EXTERNAL_SERVICE
  • service 需要置上flag ServiceInfo.FLAG_ISOLATED_PROCESS
  • service 的exported 必须为true
  • app 在bind service 的时候置上flag Context.BIND_EXTERNAL_SERVICE
        ComponentName name = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name);if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {if (isBindExternal) {if (!sInfo.exported) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +" is not exported");}if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +" is not an isolatedProcess");}// Run the service under the calling package's application.ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);if (aInfo == null) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +"could not resolve client package " + callingPackage);}sInfo = new ServiceInfo(sInfo);sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);sInfo.applicationInfo.packageName = aInfo.packageName;sInfo.applicationInfo.uid = aInfo.uid;name = new ComponentName(aInfo.packageName, name.getClassName());service.setComponent(name);} else {throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +name);}

最终获取的ComponentName 的package 为app 的package,name 是service 的name。

2.2 变量AppBindRecord b

 AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

来看下函数retrieveAppBindingLocked():

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

 注意,后面在bringUpServiceLocked() 的时候会根据bindings 来确定是否可以bind service。

变量类型为AppBindRecord,如下:

final class AppBindRecord {final ServiceRecord service;    // The running service.final IntentBindRecord intent;  // The intent we are bound to.final ProcessRecord client;     // Who has started/bound the service.final ArraySet connections = new ArraySet<>();// All ConnectionRecord for this client.

retrieveServiceLocked查找到ServiceRecord之后,生成Service和Client(callerApp)之间的绑定关系AppBindRecord,AppBindRecord的字段包括service,client,intent,确定了他们之间的绑定关系。

2.3 变量 ConnectionRecord c

    ConnectionRecord c = new ConnectionRecord(b, activity,connection, flags, clientLabel, clientIntent);

其中的connection 是从app 中传进来的,在上面 1.2 节中已经解析过,相当于一个ServiceDispatcher。记住了每一个需要bind 到该service 的所有信息。在bind 信息有更新的时候也会通过这里的conn 进行connected() 调用。

final class ConnectionRecord {final AppBindRecord binding;    // The application/service binding.final ActivityRecord activity;  // If non-null, the owning activity.final IServiceConnection conn;  // The client connection.final int flags;                // Binding options.final int clientLabel;          // String resource labeling this client.final PendingIntent clientIntent; // How to launch the client.String stringName;              // Caching of toString.boolean serviceDead;            // Well is it?

2.4 变量 connections

    IBinder binder = connection.asBinder();ArrayList clist = s.connections.get(binder);if (clist == null) {clist = new ArrayList();s.connections.put(binder, clist);}clist.add(c);b.connections.add(c);if (activity != null) {if (activity.connections == null) {activity.connections = new HashSet();}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();mServiceConnections.put(binder, clist);}clist.add(c);

AppBindRecord的connections字段则保存了这个client的所有ServiceConnection连接ConnectionRecord,ConnectionRecord和IServiceConnection对象是对应的。ServiceRecord也有个connections列表,但ServiceRecord的connections列表存储的是这个Service相关的所有ConnectionRecord,Service和Client之间是多对多的关系,所以其各自维护了一个connections。

2.5 bringUpServiceLocked()

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

如果bindService 的时候置上flag Context.BIND_AUTO_CREATE,那么会直接进入bringUpServiceLocked() 进行唤醒。

2.5.1 进入create 流程

在 Android service 启动篇之 startService 的 3.6.1 节中我们对该函数进行的详细的分析,第一次进入唤醒的时候需要通过函数realStartServiceLocked() 进行启动,并且通过函数bumpServiceExecutingLocked(r, execInFg, "create"); 埋下create 流程ANR的炸弹,最后通过scheduleCreateService进入create 流程。

2.5.2 进入bind service 流程

接着会进入bind service 流程:

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

这里的bindings 就是上面 2.2 节中创建的。

注意:

其实在start service 的时候也会进入这个流程,但是bindings 为空,所以,最终不会进入bind service 的流程。 

详细的函数requestServiceBindingsLocked(r, execInFg); 分析请看下面2.6节。

2.5.3 进入start service 流程

其实在bringUpServiceLocked() 最后会通过函数:

sendServiceArgsLocked(r, execInFg, true);

进入start service 的流程,详细可以看 Android service 启动篇之 startService 的 3.6.1.2 节,但是对于bind service 来说,在函数的最开始条件就不满足return 了。详细看Android service 启动篇之 startService 的 3.4 节和3.6.1.1 节。

2.6 requestServiceBindingsLocked()

上一节,我们看到如果service 第一次启动的时候,会进入realStartServiceLocked()。最终会进入该函数,执行bind 的完成流程。

但是,如果service 已经启动,这个时候在bringUpServiceLocked() 中:

        if (r.app != null && r.app.thread != null) {sendServiceArgsLocked(r, execInFg, false);return null;}

而 sendServiceArgsLocked() 在上面 2.5.3 中知道bind service 是不会执行的。

那最终会跳过 2.5 节的流程继续往下执行:

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

第一次bind service 的时候requested 为false,最终会调用到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;}

通过该函数进行bind 的request 申请,通过函数:

bumpServiceExecutingLocked(r, execInFg, "bind");

埋下onBind操作的ANR 炸弹,要求onBind 处理不能超过20s。

并通过:

r.app.thread.scheduleBindService()

最终调用handleBindService函数:

    private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);...if (s != null) {try {...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) {...}}}

onBind 接口调用完成后会调用AMS.publishService,最终会调用到ActiveServices.publishServiceLocked 进行onBind 的后期处理,最终会调用serviceDoneExecutingLocked 解除ANR炸弹

至此,bindService的流程基本完成,总结下:

  • 注意两个核心变量AppBindRecord 和ConnectionRecord;
  • 如果没有启动service,会顺序进入create、bind、start 流程,并且在每个流程都会埋下ANR炸弹,要求每个流程在20s 内处理完成,在处理结束后ActiveServices 会拆除炸弹;
  • 如果已经启动service,会进入onStartCommand流程,同样伴随着ANR炸弹;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部