解析Service的绑定过程

Service 的绑定过程   

       我们可以通过调用Context 的 startService方法来启动Service,也可以通过Context 的bindService方法来绑定Service,绑定Service的过程要比启动Service的过程要复杂一些,首先建议先阅读“解析Service的启动过程”这篇文章,结合Service的启动过程会有更好的理解。 Service 的绑定过程将分为两个部分来进行讲解,分别是 Contextlmpl 到 AMS 的调用过程和绑定 Service 。本文基于Android8.1.0系统分析Service的启动过程。

1. Contextlmpl到AMS的调用过程

       我们可以用 bindService 方法绑定 Service ,它在 ContextWrapper中实现, 代码如下所示:

frameworks/base/core/java/android/content/ContextWrapper.java

    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }

       在“解析Service的启动过程”一文章我们分析过,mBase具体指向就是ContextImpl,接着查看ContextImpl的bindService方法,代码如下所示:

frameworks/base/core/java/android/app/Contextlmpl.java

    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
       在bindService 方法中,又返回了 bindServiceCommon 方法 ,代码如下所示:
 
frameworks/base/core/java/android/app/Contextlmpl.java
 
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, 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); // ... 1
        } 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()); // ... 2
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
       在注释1调 用了 LoadedApk 类型的对象 mPac kagelnfo 的  getServiceDispatcher 方法, 它的主要作用是将 ServiceConnection 封装为 IServiceConnection 类型的对象 sd ,从 IServiceConnection 的名字 我们就能得知它实现了 Binder 机制,这样 Service 的绑定就支 持了跨进程。接着在注释2处我们 又看 见了 熟悉 的代码 ,最终会调用 AMS 的  bindService 方法。
 

2. 绑定 Service

 接着我们来分析 AMS 的 bindService 方法,代码如下所示:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

   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 descriptors
        if (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);
        }
    }
       bindServi ce 方法 最后会 调用 Acti veServices 类型 的对象 mServices 的 bindServiceLocked方法 ,代码如下所示:
 
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
 
    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
        ...
       
        try {

            ...

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

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();

                // 启动Service
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) { // ... 2
                    return 0;
                }
            }

            ...

            if (s.app != null && b.intent.received) { // ... 3
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    c.conn.connected(s.name, b.intent.binder, false); // ... 4
                } 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) { // ... 5
                    requestServiceBindingLocked(s, b.intent, callerFg, true); // ... 6
                }
            } else if (!b.intent.requested) { // ... 7
                requestServiceBindingLocked(s, b.intent, callerFg, false); // ... 8
            }

            getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }
首先 介绍 几个与 Service 相 关的对象类型 ,这样有助于对源码进行理解, 如下所示
ServiceRecord :用于描述一个 Service。
ProcessRecord :用于描述一 个进程的信息。
ConnectionRecord :用 于描应 用程序进 程和 Service 建立的 一次通信。
AppBindRecord :应用程序进程通过 Intent 绑定 Service 时,会通过 AppBi ndRecord 来维护 Service 与应用程序进程之间的关联。其内部存储了谁绑定的 Service ( ProcessRecord ) 、被绑 定的 Service ( AppBindRecord )、绑定 Service 的  Intent ( IntentBindRecord )和所有绑定通信记录 的信息( ArraySet<Connecti onRecord> )。
IntentBindRecord :用于描述绑定 Service 的  Intent 。
 
 
       在注释1 处调用了  ServiceRecord 的  retrieveAppBindingLocked 方法 来获得 AppBindRecord , retrieveAppBindingLocked 方法内部创建 IntentBindRecord ,并对 IntentBindRecord 的成员 变量进行赋值,后面我们会详 细介 绍这 个关键的方方法。
 
       在注释2 处调用 bringUpServiceLocked 方法 ,在 bringUpServiceLocked 方法 中又调用 realStartServiceLocked 方法,最终由 ActivityThread 来调用 Service 的  on Create 方法 启动 Service ,这也说明了 bindService 方法 内部会启动 Service ,启动 Service 这一过程在 “ 解析Service的启动过程 ” 一文中已经讲过,这里不再赘述。在注释3处 s.app ! = null 表示 Service 已经运行,其中s 是  ServiceRecord 类型对象, app 是  Process Record 类型对象。 b. intent.received 表示当前应用程 序进程已经接收到绑定 Service 时返 回的 Binder ,这样应用程序 进程就可以通过 Binder 获取要绑定的 Serv ice 的访问接口。 在注释4处 调用 conn 的 connected 方法 ,其中 c.conn 指的是 IServiceConnection ,它的具体实 现为 ServiceDispatcher.InnerConnection ,其中 ServiceDispatcher 是  LoadedApk 的内部类, InnerConnection 的 connected 方法 内部会调用 H 的  post 方法 向主线程发送消息,并且解决 当前应用程序进程与  Service 跨进程通信的问题 。在注释5 处如果当前应用程序进程是第一 个与 Service 进行绑 定的,并且 Service 已经调 用过 onUnBind 方法 ,则 需要调 用注释6 处的代码。 在注释7处 如果应用程序进程的 Client 端没有发送过绑定 Service 的请求,则调 注释8 处的代码, 注释8 处和注释6 处的代码区别就是最后一 个参数 rebind 为  false ,表 示不是 重新 绑定。
接着我们查看注释8 处的 requestServiceBindingLocked方法,代码如下所示:
 
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {

        ...

        if ((!i.requested || rebind) && i.apps.size() > 0) { // ... 1
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState); // ... 2
                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;
    }
       注释1处i .requested 表示是否发送过绑定 Service 的请求,从 bindServiceLocked 方法 的注释7处得知是没有发送过的,因此,! i. requested为true 。从 bindServiceLocked方法 的注释8处得知 rebind 值为 false ,那么(!i. requested I I rebind) 的值为 true 。 i.apps.size() > 0 表示什么呢? 其中i是 IntentBindRecord 类型的对 象, AMS 会为每个绑定 Service 的  Intent 分配一个
IntentBindRecord 类型对象,代码如下所示:
 
frameworks/base/services/core/java/com/android/server/am/IntentBindRecord.java
 
final class IntentBindRecord {
    /** 被绑定的Service */
    final ServiceRecord service;
    /** 绑定Service的Intent */
    final Intent.FilterComparison intent; // 
    /** 所有用当前Intent绑定Service的应用程序进程 */
    final ArrayMap<ProcessRecord, AppBindRecord> apps
            = new ArrayMap<ProcessRecord, AppBindRecord>(); // ... 1

   ...

}
       首先分析一下IntentB indRecord 类,不同的应用程序进程可能使用同一个  Intent 来绑定 Service ,因此在注释1 处会用 apps 来存储所有用当前 Intent 绑定 Service 的应用程序进程。 i.app s.size() >0  表示所有用当前 Intent 绑定 Service 的应用程序进程个数大于0  ,下面来验证 i.apps.size() > 0  是否为true   。我们回到 bindServiceLocked 方法 的注释1 处, ServiceRecord 的 retrieveAppB indingLocked 方法代码 如下所示:
 
frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
 
    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); // ... 1
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app); // ... 2
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app); // ... 3
        i.apps.put(app, a);
        return a;
    }
       注释1 处创建了 IntentB indRecord ,注释2 处根据 ProcessRecord 获得 IntentBindRecord 中存储的 AppB indRecord ,  如果 AppB indRecord 不为 null 就返回,如 果为 null 在注释3 处创建 AppBi ndRecord , 并将 ProcessReco rd 作为 key , AppBindRecord 作为 value 保存在 IntentBindRecord 的 apps ( i.apps )中 。回到 req uestServiceBindingLocked 方法 的注释1 处, 结合 ServiceRecord 的  retrieveAppBindingLocked 方法 得知 i.apps.size() > 0 为  true ,这 样就会调用注释2处的代码, r.app.t hread 的 类型为 IApp licationThread , 它的 实现我 们已经 很熟悉了,是ActivityThread 的内部类 Application Thread, scheduleBindService 方法代码 如下所示:
 
frameworks/base/core/java/android/app/ActivityThread.java
 
        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);
        }
       首先将 Service 的信息封装成 BindServiceData 对象, BindServiceData 的成员变 rebind 值为 false ,后面会用到它。接着将B indServiceData 传入到 se ndMessage方法中,  sendMessage 向 H 发送消息,  我们接 着查看 handleMessage 方法,代码如下所示
 
frameworks/base/core/java/android/app/ActivityThread.java
 
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ...
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
            }
           ...           
        }

       在HandleMessage方法中会调用HandleBindService方法,代码如下所示:

frameworks/base/core/java/android/app/ActivityThread.java


    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token); // ... 1
        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) { // ... 2
                        IBinder binder = s.onBind(data.intent); // ... 3
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder); // ... 4
                    } else {
                        s.onRebind(data.intent); // ... 5
                        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);
                }
            }
        }
    }
       在注释1 处获取要绑定的 Service 。注释2 处的 BindServiceData 的成员变量 rebind 值为 false ,这样会 调用注释3 处的代码来调用 Service 的  onBind 方法 ,到这里  Service 处于绑定状态了。 如果 rebind 的值为 true就会 调用注释5 处的 Service 的  onRebind 方法,这一点结合前文的 bindServiceLocked 方法 的注释5 处,得出的结论就是:如果当前应用程序进程第一个与 Service 进行绑定,并 Service 调用过 onUnBind 方法,则会调 Service 的 onRebind 方法。 handleBindService 方法 有两个分支,一 个是绑定过 Servive 的情况,另一个是未绑定的情况,这里分析未绑定的情况,查 看注释4 处的代码 ,实 际上是调用 AMS的publishService 方法,代码如下所示:
 
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 
    public void publishService(IBinder token, Intent intent, IBinder service) {
        // Refuse possible leaked file descriptors
        if (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);
        }
    }

       在publishService方法中调用了ActiveServices类型的mServices对象的publishServiceLocked方法,代码如下所示:

 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {          
            ...       
            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); // ... 1
                            } 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);
        }
    }
       注释1 处的代码 在前面介绍过, c.conn 指的是 ISe rviceConn ect ion ,它是 ServiceConnection 在本地的代理,用于 解决当前应 用程序进程和 Serv ice  跨进程通信 的问题, 具体实现为 ServiceDispatche r.Inner Connec tion ,其中 ServiceDispatcher 是 LoadedApk 的内部 类, Serv iceDispatcher.InnerConnection 的  connected 方法 的代码如下所示:
 
frameworks/base/core/java/android/app/LoadedApk.java
 
 static final class ServiceDispatcher {  
      ...
      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); // ... 1
                }
            }
        }
      ...
  }
       在注释1 处调用了 ServiceDispatcher 类型的 sd 对象的 co nnected 方法,代码如下所示:
 
frameworks/base/core/java/android/app/LoadedApk.java
 
        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead)); // ... 1
            } else {
                doConnected(name, service, dead);
            }
        }
       在注释1处调 Handler 类型的对象 mActivityThread 的  post 方法 mActi vi tyThread 实际 指向的是H  。因 此, 通过调用 post 方法 将 R un Connection 的内 容运行在主线 程中。 Run Connection 是  LoadedApk 的内 部类,定义如下所示:
 
frameworks/base/core/java/android/app/LoadedApk.java
 
        private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                mName = name;
                mService = service;
                mCommand = command;
                mDead = dead;
            }

            public void run() {
                if (mCommand == 0) {
                    doConnected(mName, mService, mDead);
                } else if (mCommand == 1) {
                    doDeath(mName, mService);
                }
            }

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
            final boolean mDead;
        }

       在RunConnection的run方法中调用了doConnected方法,代码如下所示:

frameworks/base/core/java/android/app/LoadedApk.java


        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ...

            // 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); // ... 1
            }
        }
       在注释1处调 用了 ServiceConnection 类型的对象  mConnection 的  onServiceConnected 方法 ,这样在客户端实现了 ServiceConnection 接口类的 onServiceConnected 方法 就会被执行。至此, Service 的绑定过程就分析完成。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值