Android中bindService()启动Service的过程分析

Android 中 startService()启动service的过程分析一文中讲到了startService()启动service的过程.

我们知道启动service有两种方式:startService()和bindService().两者也可以结合使用.

接下来就通过源码来分析一下bindService()的过程.

现在假设应用程序有一个activity和一个service,然后在activity中通过bindeService()启动service,清单文件中service的process属性没有设置.也就是activity和service在一个进程中.bindService()方法的参数与startService()方法参数不同,bindService()方法参数多了一个重要的参数.

bindService(intent, ServiceConnection, Context.BIND_AUTO_CREATE);

<pre name="code" class="java">

 
 
private ServiceConnection mConnection = new ServiceConnection() {
		
		@Override
		public void onServiceConnected(ComponentName arg0, IBinder service) {
			// TODO Auto-generated method stub
			...
		}

		@Override
		public void onServiceDisconnected(ComponentName arg0) {
			...
		}
		
	};
通过 android应用程序使用Binder实现进程间通信 , 使用aidl工具快速在应用层实现binder进程间通信 ,这两篇文章我们知道,bindService()这种方式启动service,可以实现通过Binder实现进程间通信.

好了,进入主题

bindService()这个方法是Activty的父类ContextWrapper中的.

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

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

这里mBase变量是ContextImpl类型,是在创建activity的时候,new 一个 ContextImpl对象,赋值给activity的.

第一步:frameworks/base/core/java/android/app/ContextImpl.java

  @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
    ....
        return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid()));
    }

    /** @hide */
    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
        IServiceConnection sd;
        if (conn == null) {
         ...
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                    mMainThread.getHandler(), flags);
        } else {
           ....
        }
        try {
            IBinder token = getActivityToken();//
        ....
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(),
                service, service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, userHandle);
            if (res < 0) {
             ....
            }
            return res != 0;
        } catch (RemoteException e) {
            ...
        }
    }

 这里的mMainThread是一个ActivityThread实例,通过它的getHandler函数可以获得一个Handler对象,有了这个Handler对象后,就可以把消息分发到ActivityThread所在的线程消息队列中去了,后面我们将会看到这个用法,现在我们暂时不关注,只要知道这里从ActivityThread处获得了一个Handler并且保存在下面要介绍的ServiceDispatcher中去就可以了。

我们先看一下ActivityThread.getHandler的实现,然后再回到这里的bindService函数来

第二步: 在frameworks/base/core/java/android/app/ActivityThread.java中

 final H mH = new H();
final Handler getHandler() {
        return mH;
    }
这里返回的Handler是在ActivityThread类内部从Handler类继承下来的一个H类实例变量。

 回到第一步中的ContextImpl.bindService函数中,获得了这个Handler对象后,就调用mPackageInfo.getServiceDispatcher函数来获得一个IServiceConnection接口,这里的mPackageInfo的类型是LoadedApk,我们来看看它的getServiceDispatcher函数的实现,然后再回到ContextImpl.bindService函数来。




第三步:getServiceDispatcher()

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

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }

先看一下关系图:


 看一下ServiceDispatcher类,是LoadedApk内部类

static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
      ....
        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) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }

        private final HashMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
            = new HashMap<ComponentName, ServiceDispatcher.ConnectionInfo>();

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

        void validate(Context context, Handler activityThread) {
            if (mContext != context) {
        ....
            }
            if (mActivityThread != activityThread) {
             .....
            }
        }

      .....
        IServiceConnection getIServiceConnection() {
            return mIServiceConnection;
        }

    .....
在getServiceDispatcher函数中,传进来的参数context是一个调用bindService的activity实例,先以它为Key值在mServices中查看一下,是不是已经存在相应的ServiceDispatcher实例,如果有了,就不用创建了,直接取出来。在我们这个情景中,需要创建一个新的ServiceDispatcher。在创建新的ServiceDispatcher实例的过程中,将上面传下来ServiceConnection参数c和Hanlder参数保存在了ServiceDispatcher实例的内部,并且创建了一个InnerConnection对象,这是一个Binder对象,一会是要传递给ActivityManagerService的,ActivityManagerServic后续就是要通过这个Binder对象和ServiceConnection通信的。

 函数getServiceDispatcher最后就是返回了一个InnerConnection对象给ContextImpl.bindService函数。回到ContextImpl.bindService函数中,它接着就要调用ActivityManagerService的远程接口来进一步处理了。

回到第一步

int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(),
                service, service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, userHandle);

参数getActivityToken()是与activity相关的一个Binder类型,每个activity都有一个关联的Binder对象

参考分析点击android桌面app图标启动应用程序的过程这篇文章.

service.resolveTypeIfNeeded(getContentResolver())获取这个intent的MIME类型,这里假设没有设置MIME类型  即AndroidManifest.xml没有设置Service的MIME类型,所以这里返回null.

第四步:bindService()

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

public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType, IServiceConnection connection,
            int flags, int userId) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeStrongBinder(token);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(connection.asBinder());
        data.writeInt(flags);
        data.writeInt(userId);
        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        data.recycle();
        reply.recycle();
        return res;
    }

经过Binder驱动

 case BIND_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            IBinder token = data.readStrongBinder();
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            b = data.readStrongBinder();
            int fl = data.readInt();
            int userId = data.readInt();
            IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
            int res = bindService(app, token, service, resolvedType, conn, fl, userId);
            reply.writeNoException();
            reply.writeInt(res);
            return true;
        }

上面就调用到了 ActivityManagerService中.

第五步:

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

 public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) {
     ....
        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service, resolvedType,
                    connection, flags, userId);
        }
    }

第六步: bindServiceLocked ()

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

int bindServiceLocked(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) {
      .....
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
         ....
        }
        ActivityRecord activity = null;
        if (token != null) {
            activity = mAm.mMainStack.isInStackLocked(token);
            if (activity == null) {
            ....
            }
        }
        int clientLabel = 0;
        PendingIntent clientIntent = null;
    .....
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true);
        if (res == null) {
            return 0;
        }
        if (res.record == null) {
            return -1;
        }
        ServiceRecord s = res.record;
  ...
            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);
          ....
            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(), false) != null) {
                    return 0;
                }
            }
  .....
    }
  函数首先根据传进来的参数token是启动service的activity在ActivityManagerService里面的一个令牌,通过这个令牌就可以将这个activity对应的的ActivityRecord取回来。

函数首先通过retrieveServiceLocked来解析service这个Intent,就是解析我们在AndroidManifest.xml定义的Service标签的intent-filter相关内容,然后将解析结果放在res.record中.

 接下来,就是把传进来的参数connection封装成一个ConnectionRecord对象。注意,这里的参数connection是一个Binder对象,它的类型是LoadedApk.ServiceDispatcher.InnerConnection,是在第三步中创建的,后续ActivityManagerService就是要通过它与ServiceConnection通信,因此,这里要把这个ConnectionRecord变量c保存下来,它保在在好几个地方,都是为了后面要用时方便地取回来的,这里就不仔细去研究了,只要知道ActivityManagerService要使用它时就可以方便地把它取出来就可以了。

 最后,传进来的参数flags的位Context.BIND_AUTO_CREATE为1,因此,这里会调用bringUpServiceLocked函数进一步处理。

第七步:bringUpServiceLocked();

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

private final String bringUpServiceLocked(ServiceRecord r,
            int intentFlags, boolean whileRestarting) {
     ....
        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
            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);
                    realStartServiceLocked(r, app);
                    return null;
                } 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 {
         ...
        }
....
    }
这里service所在的进程已经启动起来了,所以接下来直接在这个进程中启动service.

第八步:realStartServiceLocked();

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

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
     ...
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        app.services.add(r);
        bumpServiceExecutingLocked(r, "create");
        mAm.updateLruProcessLocked(app, true);

        boolean created = false;
        try {
        ....
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
            r.postNotification();
            created = true;
        } finally {
    ....
        }

        requestServiceBindingsLocked(r);
.....

        sendServiceArgsLocked(r, true);
    }

这里相继调用三个函数

scheduleCreateService()

requestServiceBindingsLocked()

sendServiceArgsLocked()

这里①就不分析了,和Android 中 startService()启动service的过程分析的第七步至第十步一样的.首先创建service,即service的onCreate()方法会被调用.

好了,我们看②

第九步:requestServiceBindingsLocked();

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

 private final void requestServiceBindingsLocked(ServiceRecord r) {
        Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
        while (bindings.hasNext()) {
            IntentBindRecord i = bindings.next();
            if (!requestServiceBindingLocked(r, i, false)) {
                break;
            }
        }
    }
private final boolean requestServiceBindingLocked(ServiceRecord r,
            IntentBindRecord i, boolean rebind) {
     ....
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, "bind");
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (RemoteException e) {
           ....
            }
        }
        return true;
    }
    这里的参数r就是我们在前面的 第六步 中创建的ServiceRecord了,它代表创建的service。函数requestServiceBindingsLocked调用了requestServiceBindingLocked函数来处理绑定服务的操作,而函数requestServiceBindingLocked又调用了app.thread.scheduleBindService函数执行操作,前面我们已经介绍过app.thread,它是一个Binder对象的远程接口,类型是ApplicationThreadProxy。

第十 步:scheduleBindService();

frameworks/base/core/java/android/app/ApplicationThreadNative.java文件中,这里又是Binder进程间通信了.

 public final void scheduleBindService(IBinder token, Intent intent, boolean rebind)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeStrongBinder(token);
        intent.writeToParcel(data, 0);
        data.writeInt(rebind ? 1 : 0);
        mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY);
        data.recycle();
    }
经过Binder驱动

 case SCHEDULE_BIND_SERVICE_TRANSACTION: {
            data.enforceInterface(IApplicationThread.descriptor);
            IBinder token = data.readStrongBinder();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            boolean rebind = data.readInt() != 0;
            scheduleBindService(token, intent, rebind);
            return true;
        }

这样就调用到了应用程序中了

第十一步:scheduleBindService();

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

 public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind) {
            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());
            queueOrSendMessage(H.BIND_SERVICE, s);
        }

第十二步: queueOrSendMessage();

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

 private void queueOrSendMessage(int what, Object obj) {
        queueOrSendMessage(what, obj, 0, 0);
    }

    private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
        synchronized (this) {
            if (DEBUG_MESSAGES) Slog.v(
                TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
                + ": " + arg1 + " / " + obj);
            Message msg = Message.obtain();
            msg.what = what;
            msg.obj = obj;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            mH.sendMessage(msg);
        }
    }

第十三步: handleBindService();

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

 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 (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);//
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                     ....
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                }
            } catch (Exception e) {
            ....
            }
        }
    }

s.onRebind(data.intent);就会回到service的onBind()方法了,这个方法大家应该熟悉.我们会在这个回调里新建一个binder对象,然后返回给 ActivityManagerService.

第十四步:publishService();

这里通过Binder进程通信,调用到ActivityManagerService中去,参数中有上一步创建的Binder对象.

中间通信步骤就省略了,和第四步类似.

第十五步:publishService();

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

 public void publishService(IBinder token, Intent intent, IBinder service) {
       ...
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        ....
    }
第十六步: publishServiceLocked();
frameworks/base/services/java/com/android/server/am/ ActiveServices.java

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
       ...
        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;
                    if (r.connections.size() > 0) {
                        Iterator<ArrayList<ConnectionRecord>> it
                                = r.connections.values().iterator();
                        while (it.hasNext()) {
                            ArrayList<ConnectionRecord> clist = it.next();
                            for (int i=0; i<clist.size(); i++) {
                                ConnectionRecord c = clist.get(i);
                                if (!filter.equals(c.binding.intent.intent)) {
                                  ....
                                    continue;
                                }
                               ....
                                try {
                                    c.conn.connected(r.name, service);
                                } catch (Exception e) {
                                   ....
                                }
                            }
                        }
                    }
                }
             ...
            }
        } finally {
         ...
        }
    }

第六步 中,我们曾经把一个ConnectionRecord放在ServiceRecord.connections列表中,因此,这里可以从r.connections中将这个ConnectionRecord取出来, 每一个ConnectionRecord里面都有一个成员变量conn,它的类型是IServiceConnection,是一个Binder对象的远程接口,这个Binder对象又是什么呢?这就是我们在第三步中创建的LoadedApk.ServiceDispatcher.InnerConnection对象了。因此,这里执行c.conn.connected函数后就会进入到LoadedApk.ServiceDispatcher.InnerConnection.connected函数中去了。参数service就是onBind()方法创建的Binder对象

第十七步:InnerConnection.connected();

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

 static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
    .....
        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) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }
这里它将操作转发给ServiceDispatcher.connected函数。

第十七步:ServiceDispatcher.connected();

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

 public void connected(ComponentName name, IBinder service) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0));
            } else {
              ...
            }
        }
我们在前面 第三步 中说到,这里的mActivityThread是一个Handler实例,它是通过ActivityThread.getHandler函数得到的,因此,调用它的post函数后,就会把一个消息放到ActivityThread的消息队列中去了。

第十八步:H.post;

由于H类继承于Handler类,因此,这里实际执行的Handler.post函数,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件,调用了这个函数之后,这个消息就真正地进入到ActivityThread的消息队列去了,与sendMessage把消息放在消息队列不一样的地方是,post方式发送的消息不是由这个Handler的handleMessage函数来处理的,而是由post的参数Runnable的run函数来处理的。这里传给post的参数是一个RunConnection类型的参数,它继承了Runnable类,因此,最终会调用RunConnection.run函数来处理这个消息。

 第十九步: RunConnection.run

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

 private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command) {
                mName = name;
                mService = service;
                mCommand = command;
            }

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

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
        }
这里的mCommand值为0,于是就执行ServiceDispatcher.doConnected函数来进一步操作了。


 第二十步: ServiceDispatcher.doConnected

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

 public void doConnected(ComponentName name, IBinder service) {
           ....
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }
mConnection.onServiceConnected(name, service);这个回调大家应该也很熟悉吧,这里就把service这个Binder对象传到activity中来了.

好了,到这一步,service 已经onCreate()和onBind()了,同时,绑定服务时,服务返回的Binder对象也接收到了,

然后回到第八步.③也不分析了,Android 中 startService()启动service的过程分析第十一步第十三步一样的.即service的onStart()方法和onStartCommand()会被调用.

上面就是bindService()的过程了.
























你可能感兴趣的:(bindService)