【Android原始碼】Service的繫結過程

指間沙似流年發表於2017-12-23

前篇:Service的啟動過程

剛開始的過程和startService類似:

@Override
public boolean bindService(Intent service, ServiceConnection conn,
       int flags) {
   warnIfCallingFromSystemProcess();
   return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
           Process.myUserHandle());
}

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
   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 = ActivityManagerNative.getDefault().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();
   }
}
複製程式碼

bindServiceCommon主要完成了兩件事情:

  1. 通過mPackageInfo.getServiceDispatcherServiceConnection轉化成了ServiceDispatcher.InnerConnection物件,為什麼需要轉換呢?

    這是因為服務的繫結有可能是跨程式的,所以ServiceConnection必須藉助Binder機制才能讓伺服器來回撥自己的方法,而InnerConnection正好實現了IServiceConnection.Stub,轉化成了Binder物件。

  2. 完成繫結操作。

    ActivityManagerService.bindService
    -> ActiveServices.bindServiceLocked
    -> ActiveServices.bringUpServiceLocked
    -> ActiveServices.realStartServiceLocked
    複製程式碼

    同樣的和啟動過程類似,都是通過ApplicationThread來建立Service的例項,並執行onCreate方法。

之後會呼叫requestServiceBindingsLocked方法來繫結Service。

ActiveServices.requestServiceBindingsLocked
-> ActiveServices.requestServiceBindingLocked
-> ApplicaitonThread.scheduleBindService
複製程式碼

同樣的是通過Handler來傳送BIND_SERVICE訊息

 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);
                   ActivityManagerNative.getDefault().publishService(
                           data.token, data.intent, binder);
               } else {
                   s.onRebind(data.intent);
                   ActivityManagerNative.getDefault().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);
           }
       }
   }
}     
複製程式碼

這個時候通過s.onBind(data.intent)就將Service繫結起來了。

那麼客戶端是如何知道已經成功連線到Service了呢? 這個時候就要呼叫onServiceConnected方法,這個過程就由publishService來完成。

ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                                
-> ActivityManagerService.publishService  
-> ActiveServices.publishServiceLocked
複製程式碼

publishServiceLocked中核心就一句c.conn.connected(r.name, service)

其中c.conn就是一開始將ServiceConnection轉化成的ServiceDispatcher.InnerConnection物件。

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);
      }
  }
}
// LoadedApk.java
public void connected(ComponentName name, IBinder service) {
  if (mActivityThread != null) {
      mActivityThread.post(new RunConnection(name, service, 0));
  } else {
      doConnected(name, service);
  }
}
複製程式碼

由程式碼可以知道mActivityThread是一個Handler,其實就是ActivityThread中的H,是不可能為空的,所以必定會呼叫post方法。

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

public void doConnected(ComponentName name, IBinder service) {
  ServiceDispatcher.ConnectionInfo old;
  ServiceDispatcher.ConnectionInfo info;

  // If there was an old service, it is not disconnected.
  if (old != null) {
      mConnection.onServiceDisconnected(name);
  }
  // If there is a new service, it is now connected.
  if (service != null) {
      mConnection.onServiceConnected(name, service);
  }
}
複製程式碼

由於mCommand傳的值為0,所以呼叫的是doConnected方法,這個時候很方便的呼叫到了onServiceConnected

至此,Service的繫結過程就完全分析結束了。

相關文章