Android跨進程通信IPC之21--binderService(6.0)

移步系列Android跨進程通信IPC系列
app是沒有權限向ServiceManager注冊service的。
bindService要負責找到service的代理binder并傳遞到app進程。這期間涉及到binder實體的跨進程傳輸,也就是所謂的匿名binder。因為這些binder并沒有在ServiceManager中注冊。

  • 對于通過startService啟動的Service,只能作為接收方,啟動方可以通過onStartCommand來傳遞參數。這種啟動方式其可以有多個啟動者,不過在銷毀的時候,一旦有任意一個啟動調用了stopService或者自身stopSelf后,該Service就會停止,而啟動者的生命周期無法影響到該Service
  • 對于通過bindService啟動的Service,其和啟動方有個“綁定”的過程,啟動方可以通過Service的binder引用來調用Service的方法。不過這種方式Service的生命周期會關聯著啟動方的,啟動方生命周期結束后,會默認unbindService來結束。
  • bindService的過程要比startService的過程復雜一些,因為bingService之后,發起者可以跨進程調用service的某些方法。
  • 使用bindService啟動的service的開發過程中一般都會借助aidl。

1 ServiceConnection

  • 當一個app進程bindService()時,它需要先準備好一個實現了ServiceConnection接口的對象。
  • ServiceConnection的定義如下:
public interface ServiceConnection {

    public void onServiceConnected(ComponentName name, IBinder service);

    public void onServiceDisconnected(ComponentName name);
}
  • bindService()見名知意即綁定服務,建立一個邏輯連接。當連接建立之后,AMS會回調ServiceConnection接口對象的onServiceConnected()方法。把遠端service的代理binder傳遞過來,這樣就可以通過這個代理binder跨進程調用service中的方法了。

  • ServiceConnection接口的onServiceDisconnected()方法并不會在unbindService()操作斷開邏輯連接時執行。而是在遠端service進程終止時,AMS才會回調onServiceDisconnected()。

  • 另外要注意的是縱然service進程的因為某些原因被終止,之前的綁定在該service上的邏輯連接仍處于激活狀態,當service再次運行的時候,AMS會回調onServiceConnected。

  • bindService的過程,我們要關心的binder有兩個

    • 一個是遠端service的binder,有了它客戶端進程才能跨進程調用service中的方法;
    • 另一個是binder是客戶端傳遞給AMS的,有了這個binder,AMS才會在邏輯連接建立之后,跨進程調用客戶端中的ServiceConnection接口的對象中的onServiceConnected()和onServiceDisconnected()。
  • 另外bindservice在建立連接時,如果發現service還沒啟動,會根據flag是否設置BIND_AUTO_CREATE,決定是否啟動這個service。

  • 同startService()一樣,bindService()也是一個異步的過程,也就是說當該方法返回時,邏輯連接很可能還沒有建立,通俗的說就是該方法返回時,onServiceConnected()方法很可能還沒執行。

2 bindService的過程

整個過程大體上是這樣的:

  1. 檢查要綁定的額service是否啟動,沒有的話,要先啟動service,然后執行service的生命周期方法。
  2. 執行綁定,先將遠端service的binder傳遞到AMS中,然后AMS在將其傳遞到客戶端組件進程中

3 bindService的過程分析

ContextImpl.bindService():

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

ContextImpl.bindServiceCommon():

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        IServiceConnection sd;
      ................
        if (mPackageInfo != null) {
          //從LoadedApk中拿到一個binder實體
          //首先會把ServiceConnection轉成Binder對象
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                    mMainThread.getHandler(), flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        //  Android5.0之后不允許使用隱式調用
        // 這里對傳入的intetn進行檢查,如果是android5.0以上,
        //是隱式intent的話拋出異常
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            ....................
            service.prepareToLeaveProcess();
            // 向AMS發起跨進程調用其bindService方法
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
          ..............................
            return res != 0;
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }
  • 這里便引出了第一個binder:IServiceConnection sd。
  • 這是一個實體binder,也就是說跨進程傳輸到AMS進程之后,AMS進程中最終得到的是一個代理binder。而且binder實體跨進程傳輸過程中,會在binder驅動層中為其創建相應的數據結構,這樣binder便在binder驅動中扎根了。
  • 那么這個binder實體最初是從哪里來的呢?

3.1 LoadedApk.getServiceDispatcher()

  • 此處的mPackageInfo是用戶進程里和apk對應的LoadedApk對象
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
           Context context, Handler handler, int flags) {
       synchronized (mServices) {
           LoadedApk.ServiceDispatcher sd = null;
            // 先從LoadedApk.mService中查找,
            // 看看發起綁定操作的組件是否已經存在一個用來處理傳入的
            //ServiceConnection接口對象的ServiceDispatcher
           ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
           if (map != null) {
              // 存在的話直接返回這個ServiceDispatcher
               sd = map.get(c);
           }
           if (sd == null) {
              // 不存在的話創建一個ServiceDispatcher
              // 這里要注意第三個參數handler,是主線程的handler
              // 保存在了ServiceDispatcher.mActivityThread中
               sd = new ServiceDispatcher(c, context, handler, flags);
               if (map == null) {
                   map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                   mServices.put(context, map);
               }
                // 將創建的ServiceDispatcher緩存到LoadedApk.mService
               map.put(c, sd);
           } else {
               sd.validate(context, handler);
           }
           return sd.getIServiceConnection();
       }
   }

  • LoadedApk.ServiceDispatcher從它的名字中可以看出它類似于一個Dispatcher的作用,這個類的作用就是當client bind某個service成功之后,負責向client分配service IBinder的;以及當client unbind service時,負責通知client unbind service的狀態。
  • 對于ServiceDispatcher的管理,是以apk,即package為載體的,也就是說對于某個package,定義在其中的component如果請求去bind一個service,那么LoadedApk將為這個component分配一個ServiceDispatcher。
  • component每請求bind一個service,都會為其指定一個ServiceConnection接口的對象,同一component組件內,LoadedApk將會為這個ServiceConnection接口的對象分配一個唯一的ServiceDispatcher。
  • LoadApk會為每個context保存一個key為ServiceConnection、value為ServiceDispatcher的map,這樣盡量做到了ServiceConnection對應的Binder對象的復用。如果沒有可復用,則把ActivityThread的主Handler和ServiceConnection對應起來,這樣在復用的時候,ServiceDispatcher會調用validate方法檢查handler為當前主線程handler,保證不錯亂。
  • 每個app進程中是可以加載多個apk包的,記錄在ActivityThread.mPackages,整個關系如下所示:


    2780242-c11bbd267ebbd52a.png
  • packages中的組件component,例如某個activity中使用bindService去綁定一個service時,都需要提供一個實現ServiceConnection接口的對象,每一個這樣的對象都會與一個ServiceDispatcher對象綁定。
  • LoadedApk.mServices中存儲了本package中所有組件中所有的ServiceDispatcher。它是一個ArrayMap,key是context,實際上就是組件,因為組件繼承自Context。
  • value又是一個ArrayMap,因為一個組件中使可以綁定多個service的嘛。這個map的可以是實現了ServiceConnection接口的對象,value是與之綁定的ServiceDispatcher對象。

3.1.1 ServiceDispatcher

static final class ServiceDispatcher {
    // 一個binder實體對象
    private final ServiceDispatcher.InnerConnection mIServiceConnection;
    // 邏輯連接建立時,執行的回調接口對象
    private final ServiceConnection mConnection;
    // 所在的組件,即client
    private final Context mContext;
    // 進程主線程即UI線程中的handler
    private final Handler mActivityThread;
    private final ServiceConnectionLeaked mLocation;
    // 綁定service時,傳入的flag,例如BIND_AUTO_CREATE
    private final int mFlags;

    private RuntimeException mUnbindLocation;
    private boolean mDied;
    private boolean mForgotten;
    // 因為一個ServiceConnection可以被bindService方法多次調用,用來啟動不同的service,
    // 那么ServiceDispatcher中自然要存儲這些連接信息了:
    // ConnectionInfo很簡單只有兩個屬性成員
    // IBinder binder;遠端service的引用binder
    // IBinder.DeathRecipient deathMonitor; 遠端binder的死亡通知方法
    private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
       = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
................
  }

ServiceDispatcher.InnerConnection是一個繼承自 IServiceConnection.Stub的binder實體類:

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

其中ServiceDispatcher.connected():

public void connected(ComponentName name, IBinder service) {
    // 這里的mActivityThread是主線程的handler
    // 也就是說,實際上ServiceConnection中的回調是在主線程中執行的
      if (mActivityThread != null) {
          mActivityThread.post(new RunConnection(name, service, 0));
      } else {
          doConnected(name, service);
      }
}
  • ServiceDispatcher.InnerConnection是一個繼承自 IServiceConnection.Stub的binder實體類,從其名字上來看是一個內部連接,該怎么理解呢?
  • 因為bindService()傳遞的ServiceConnection接口對象,并沒有跨進程傳輸到AMS中,從bindServiceCommon()方法可以看出最終傳遞到AMS的是LoadedApk.getServiceDispatcher()返回的ServiceDispatcher.InnerConnection。AMS通過它跨進程間接調用ServiceConnection中的方法。
  • 因為一個ServiceConnection可以在同一個客戶端組件內被bindService方法多次調用,用來綁定不同的service,那么ServiceDispatcher中自然要存儲這些使用同一個serviceConnection綁定的service的連接信息了——ServiceDispatcher.mActiveConnections。

3.1.3 ConnectionInfo

  • 它是ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>類型對象,
private static class ConnectionInfo {
  // 遠端service的代理binder
  IBinder binder;
  // 遠端service 死亡通知回調
  IBinder.DeathRecipient deathMonitor;
}

3.2 AMS中綁定service過程

  • ContextImpl.bindServiceCommon()方法中得到ServiceConnection對應的ServiceDispatcher對象之后,便向AMS發起請求,跨進程調用AMS.bindService():
public int bindService(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags, String callingPackage,
        int userId) throws TransactionTooLargeException {
    enforceNotIsolatedCaller("bindService");

    ..............
    synchronized(this) {
      // mServices是ActiveService
        return mServices.bindServiceLocked(caller, token, service,
                resolvedType, connection, flags, callingPackage, userId);
    }
}
  • AMS.bindService()對參數做了簡單檢查之后,又調用ActiveService.bindServiceLocked()方法。
  • 這里先對ActiveService.bindServiceLocked()方法的幾個重要參數做一些說明:
int bindServiceLocked(
         IApplicationThread caller, // 客戶端進程ActivityThread.mAppThread的代理binder
         IBinder token,
         Intent service,// 客戶端綁定service時的intent
         String resolvedType,
         IServiceConnection connection, //客戶端進程的中ServiceDispatcher.InnerConnection的代理binder
         int flags,
         String callingPackage, int userId) throws TransactionTooLargeException {

ActiveService.bindServiceLocked()startService的ActiveService.startServiceLocked()方法中有很多相似的邏輯。

  • 要先查找要啟動的servicee在AMS中的代表ServiceRecord是否存在,存在的話,意味著service已經啟動;不存在的話,要創建一個ServiceRecord對象,這些操作還是由retrieveServiceLocked()方法負責的。
  • 然后通過bringUpServiceLocked()調用service的生命周期方法。只不過bindservice除了這兩個基本操作外,還要執行綁定操作,即將service的binder傳跨進程傳輸到app客戶端組件中。

3.2.1 bindServiceLocked綁定前的準備工作

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
           String resolvedType, IServiceConnection connection, int flags,
           String callingPackage, int userId) throws TransactionTooLargeException {
  ...............
  // 得到service對應的ServiceRecord
  ServiceLookupResult res =
      retrieveServiceLocked(service, resolvedType, callingPackage,
              Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
  ServiceRecord s = res.record;
  ....
  // 根據傳入的intent和發起者進程,查找到一個合適的AppBindRecord對象,
  // 查找不到就創建一個,下面會介紹規則
  AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
  // 為本次連接創建ConnectionRecord對象
  ConnectionRecord c = new ConnectionRecord(b, activity,
          connection, flags, clientLabel, clientIntent);
  // 客戶端進程的中ServiceDispatcher.InnerConnection的代理binder
  IBinder binder = connection.asBinder();
  // 將創建的邏輯連接對象ConnectionRecord,記錄在ServiceRecord中
  ArrayList<ConnectionRecord> clist = s.connections.get(binder);
  if (clist == null) {
      clist = new ArrayList<ConnectionRecord>();
      s.connections.put(binder, clist);
  }
  clist.add(c);
  // 同時記錄在AppBindRecord.connections中
  b.connections.add(c);
  // AppBindRecord.client是ProcessRecord,代表客戶端的進程
  b.client.connections.add(c);
  .......
  // 除了ServiceRecord.connections記錄了該service連接信息外
  // ActiveService.mServiceConnections記錄了當前系統中所有的連接信息
  // 所以也要將創建的連接對象,加入ActiveService.mServiceConnections
  clist = mServiceConnections.get(binder);
  if (clist == null) {
      clist = new ArrayList<ConnectionRecord>();
      mServiceConnections.put(binder, clist);
  }
  clist.add(c);
 ............

在前面關于service機制介紹的文章中提到過ServiceRecord中有一些數據成員是bindservice時用到的:

final class ServiceRecord extends Binder {
    ...............
    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
                = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();// IBinder -> ConnectionRecord of all bound clients
    // service所在的進程
    ProcessRecord app;      // where this service is running or null.
}

AMS描述綁定service時的intent的IntentBindRecord類

  • app客戶端組件中發起綁定service操作時,一定要用到intent,一般是顯示的intent(android 5.0 之后要求必須是顯示intent),而且只設置intetn.mComponent字段。
  • AMS中對綁定操作傳入的intent是用Intent.FilterComparison類來描述,也就是說當客戶端綁定service時使用的intent指定的參數都一致的話,AMS會將其看做是同一類intent。
  • AMS會為這類intent為其創建一個IntentBindRecord對象。
final class IntentBindRecord {
    // 綁定的service在AMS中的代表
    final ServiceRecord service;
    /** The intent that is bound.*/
    final Intent.FilterComparison intent; //
    /** All apps that have bound to this Intent. */
    final ArrayMap<ProcessRecord, AppBindRecord> apps
            = new ArrayMap<ProcessRecord, AppBindRecord>();
    // service的binder代理binder
    IBinder binder;

其中IntentBindRecord.service 最終會保存綁定的service的ServiceRecord;

  • IntentBindRecord.intent 就是前面所說的綁定service時設定的參數一致的intent在AMS中的表示Intent.FilterComparison對象;
  • IntentBindRecord.apps用來記錄所有使用該類intetn綁定同一個service的客戶端信息。key是客戶端進程ProcessRecord,value是AppBindRecord。因為不同的客戶端可能使用相同的intent參數來綁定同一個service,所以IntentBindRecord要記錄下這些客戶端信息;
  • IntentBindRecord.binder最終保存service的代理binder;
  • AMS通過ServiceRecord.retrieveAppBindingLocked()方法為判斷是否為傳入的intent創建一個IntentBindRecord:

3.2.1.1 ServiceRecord.retrieveAppBindingLocked()

public AppBindRecord retrieveAppBindingLocked(
           Intent intent,// 客戶端發起綁定操作時傳入的intent
           ProcessRecord app // 客戶端組件進程) {
       // 為傳入的intent創建一個Intent.FilterComparison
       Intent.FilterComparison filter = new Intent.FilterComparison(intent);
       // 查找傳入的intent是否已經有IntentBindRecord
       IntentBindRecord i = bindings.get(filter);
       if (i == null) {
          // 沒有的話創建
           i = new IntentBindRecord(this, filter);
           //并緩存到ServiceRecord.bings中
           bindings.put(filter, i);
       }
       // 查找客戶端的組件是否已經綁定過該service
       AppBindRecord a = i.apps.get(app);
       // 綁定過的話,返回找到的AppBindRecord
       if (a != null) {
           return a;
       }
       // 沒有的話創建一個AppBindRecord對象
       a = new AppBindRecord(this, i, app);
       // 并緩存到IntentBindRecord.apps中
       i.apps.put(app, a);
       return a;
   }

retrieveAppBindingLocked()另一個主要作用是查找并創建AppBindRecord對象。
AMS用于描述綁定service的客戶端整體信息的AppBindRecord類

  • 對于一個Service來說,有多少app客戶端進程和它建立了綁定關系,就會有多少個AppBindRecord對象。一個app客戶端進程里可以有多個地方發起綁定動作,所以AppBindRecord里需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接對象。
final class AppBindRecord {
    // 所在的service
    final ServiceRecord service;    // The running service.
    // 客戶端發起的bindservice時傳入的intent,AMS會為其創建一個對應的IntentBindRecord
    final IntentBindRecord intent;  // The intent we are bound to.
    // 客戶端進程
    final ProcessRecord client;     // Who has started/bound the service.
    // 客戶端所在的app,其他組件綁定該service的邏輯連接
    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                    // All ConnectionRecord for this client.
  • AppBindRecord.service 用于描述客戶端綁定的service;
  • AppBindRecord.intent 用于描述客戶端綁定該service時使用的intent;
  • AppBindRecord.client 用于描述客戶端的進程;
  • AppBindRecord.connections 用于描述客戶端中所有組件綁定該service時創建的邏輯連接;

AMS通過ServiceRecord.retrieveAppBindingLocked()來查找并創建一個合適的AppBindRecord對象。

3.2.1.2 為本次連接創建ConnectionRecord對象

  • AMS為每次綁定過程中創建的連接分配一個ConnectionRecord類型對象.
  • ConnectionRecord用來描述一個連接信息,即綁定信息,要對客戶端和service端進行描述。
ConnectionRecord用來描述一個連接信息,即綁定信息,要對客戶端和service端進行描述。
  • ConnectionRecord.binding代表這個連接所在的AppBindRecord.
  • ConnectionRecord.coon是一個binder代理對象,其實體binder是app進程中ServiceDispatcher.mIServiceConnection。用來回調客戶端進程中當連接成功建立時的回調方法。
  • 客戶端組件在bindservice時,都要創建一個實現ServiceConnection接口的對象,每個這樣的對象在客戶端組件所在的LoadedApk中都會分配一個ServiceDispatcher對象,這個對象用于處理所有使用該ServiceConnection接口的對象綁定service的客戶端組件的回調方法的執行。
  • ConnectionRecord.flags 用于描述綁定該service時,指定的flags,例如BIND_AUTO_CREATE.
  • AMS創建的ConnectionRecord對象會存儲在ServiceRecord.connections成員變量中.
  • ServiceRecord.connections是一個map,key是ServiceDispatcher.InnerConnection的代理binder,value是ArrayList<ConnectionRecord>類型的。
  • 因為客戶端進程中一個Component中可能使用同一個ServiceConnection接口對象來多次綁定同一個service,因為每次綁定都會創建一個ConnectionRecord對象,那么這些ConnectionRecord需要使用ArrayList<ConnectionRecord>來保存。
  • value中ConnectionRecord.IServiceConnection實際上一致與key是一致的。
  • AMS除了將這個"邏輯連接"ConnectionRecord對象,記錄在ServiceRecord.connections中外,還要向至少下面的幾處位置做記錄:
  1. 客戶端所在的進程在AMS中的代表ProcessRecord.connections
  2. ActiveService.mServiceConnections,這里面記錄了AMS中所有app的service的連接
  3. AppBindRecord.connections中

之所以要在這么多地方做記錄,可能是為了在不同的場合下迅速查找到連接吧。

了解了以上內容后,就可以通過下圖簡明的描述客戶端進程和AMS之間的關系:


2780242-c11bbd267ebbd52a.png

3.2.2 bringUpServiceLocked 執行service生命周期方法

在做好前面的準備工作之后,binderservice()就開始準備與Service建立連接了。那么自然要先對service進行一些操作,說白了就是執行service的生命周期方法,這是由bringUpServiceLocked()方法來負責的。

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
           String resolvedType, IServiceConnection connection, int flags,
           String callingPackage, int userId) throws TransactionTooLargeException {
.................
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
    s.lastActivity = SystemClock.uptimeMillis();
    // 只有當要綁定的service所在的進程還啟動的時候,該方法返回非null
    // 因為啟動進程需要一段時間,所以就要先退出來
    // 這里暫時假設service所在的進程已經啟動
    if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
        return 0;
    }
}
....................

此時可以分為兩大情況:

  • 1.service進程還沒啟動
  • 2.service進程已經啟動,此時有可分為兩種情況:service還沒啟動和service已經運行。bringUpServiceLocked()方法依據ServiceRecord.app區分以上兩大情況:
    • 1.ServiceRecord.app不為null,而且ServiceRecord.app.thread也不為null,預示著service已經運行了,但是這時候并不會向startService()啟動service那樣跨進程調用service.onStartCommand()生命周期方法,因為bindService啟動service時沒有將信息記錄到ServiceRecord.pendingStarts。
    • .ServiceRecord.app為null,說明ServiceRecord還沒有和service所在的進程關聯。此時在依據ServiceRecord.processName,也就是service要求運行在的進程的名字,在AMS中查找是否有這樣的進程存在,如果有的話,只需要啟動service,也就是在找到的進程中創建service對象,并執行service.onCreate()生命周期方法。

如果沒有在AMS中找到名字為ServiceRecord.processName的進程,那么就要先創建進程了,這里不考慮這種情況。

3.2.2.1 綁定service bindServiceLocked

綁定實際上就是想辦法拿到service的binder,并將其傳遞到客戶端組件進程,另外還要對前面準備工作期間創建的數據結構設置相關的字段。

這部分代碼如下:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
           String resolvedType, IServiceConnection connection, int flags,
           String callingPackage, int userId) throws TransactionTooLargeException {
.................
// b.intent.received為true,表明已經拿到了service的binder
f (s.app != null && b.intent.received) {
        // Service is already running, so we can immediately
        // publish the connection.
        try {
            // 這里就可以直接遠程調用客戶端組件中onServiceConnected()方法將service的binder傳遞過去了
            c.conn.connected(s.name, b.intent.binder);
        } 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);
    }

    getServiceMap(s.userId).ensureNotStartingBackground(s);

} finally {
    Binder.restoreCallingIdentity(origId);
}

這里分兩種情況:

  • 1.客戶端組件之前已經綁定過該service,現在這個組件又要再次綁定,也就是客戶端同一組件重復綁定
  • 2.客戶端組件首次綁定該service
    這兩種情況的區分是依據AppBindRecord.intent,即AMS為bindservice()傳入的intent分配的IntentBindRecord對象來決定的。
final class IntentBindRecord {
  /** Binder published from service. */
  IBinder binder;
  /** Set when we have initiated a request for this binder. */
  boolean requested;
  /** Set when we have received the requested binder. */
  boolean received;
  /** Set when we still need to tell the service all clients are unbound. */
  boolean hasBound;
}
  • IntentBindRecord.received為true,表明IntentBindRecord.binder已經指向遠端service的binder;
  • IntentBindRecord.requested為false,表明IntentBindRecord.binder還沒有指向遠端service的binder;

這里分析第二種情況,時序圖如下:


2780242-52921787ad1c7e6b.png
  • requestServiceBindingLocked()方法中跨進程調用service所在的進程中方法,最終會導致service.onBind()執行,該方法返回service的實體binder。
  • 這里有一點要貼別注意,那只要service被某個客戶端組件綁定過了,就不會再執行service.onBind()方法了,原因就在requestServiceBindingLocked()方法中:

3.2.2.2 requestServiceBindingLocked

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {

        ..............
        // 傳入的rebind為false還是true,取決于AMS調用service.onUnbind()返回值
        // 如果希望客戶端下一次綁定到服務時接收 onRebind() 調用(而不是接收 onBind() 調用),onUnbind()返回true
        // service首次被綁定時,rebind肯定為false
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                // 跨進程調用service所在進程的scheduleBindService()方法,執行綁定操作,這是一個異步方法,會立即返回
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    // 只要service被綁定過了,IntentBindRecord.requested就會被設置為true
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
              .................
            } catch (RemoteException e) {
              .................
            }
        }
        return true;
    }
  • 通過以上代碼可知,只要service被綁定過一次,那么IntentBindRecord.requested救回被設置為true,其他客戶端組件再次綁定的時候,由于if條件為假,所以不會再次跨進程調用service進程中的scheduleBindService()方法,也就不會調用service.onBind()方法了。
  • 因為bindservice時傳入的intent一般都是顯示intent,不會設置其他參數,所以只要客戶端綁定的是同一個service,那么在AMS中只會有一個IntentBindRecord對象。
  • 除非該servcie綁定過之后,所有綁定它的客戶端組件都執行了unbindService(),那么最終會導致service.onUnbind()方法執行,如果該方法返回了true,那么下次再有客戶端組件綁定該service時,rebind會被設置為true,這導致不會調用service.onBind(),而是調用service.rebind()方法。
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 {
                    // 傳入flase時執行onBind()
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                      // 傳入true時執行onRebind()
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

這里考慮首次綁定時的情況,所以rebind肯定為false,那么service調用過onBind()之后,又通過AMS的代理,跨進程調用AMS的publishService()將service的binder傳遞到AMS中,然后在傳遞到客戶端。

3.2.2.3 publishServiceLocked

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
.......
IntentBindRecord b = r.bindings.get(filter);
// 初次綁定
if (b != null && !b.received) {
      // 保存service的代理binder
      b.binder = service;
      // 設置下面的兩個標志為true
      b.requested = true;
      b.received = true;
      // 一般情況下,首次綁定時,connections.size為1
      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 {
                   // 將service的binder傳遞到客戶端
                   c.conn.connected(r.name, service);
               } catch (Exception e) {
                   Slog.w(TAG, "Failure sending service " + r.name +
                         " to connection " + c.conn.asBinder() +
                         " (in " + c.binding.client.processName + ")", e);
               }
           }
       }
   }
...........

3.2.2.4 bindServiceLocked

那么當首次綁定之后,又有其他客戶端組件來綁定這個service,那么在bindServiceLocked()方法中,會直接跨進程調用客戶端的connected():

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags,
        String callingPackage, int userId) throws TransactionTooLargeException {
        ................
        if (s.app != null && b.intent.received) {
              // Service is already running, so we can immediately
              // publish the connection.
              try {
                  // 直接跨進程調用客戶端的connected()
                  // 不在需要跨進程調用service.onBind()
                  c.conn.connected(s.name, b.intent.binder);
              } catch (Exception e) {
                  Slog.w(TAG, "Failure sending service " + s.shortName
                          + " to connection " + c.conn.asBinder()
                          + " (in " + c.binding.client.processName + ")", e);
              }
        ..........................

}

在看客戶端進程的connected()

3.2.2.5 connected()

public void connected(ComponentName name, IBinder service) throws RemoteException {
    // 先得到處理ServiceConnection的ServiceDispatcher
    LoadedApk.ServiceDispatcher sd = mDispatcher.get();
    if (sd != null) {
        sd.connected(name, service);
    }
}
public void connected(ComponentName name, IBinder service) {
   // mActivityThread是在創建 ServiceDispatcher對象時,傳入的組件所在進程的主線程的handler
   // 也就是說RunConnection是在組件所在的主線程中執行的
   if (mActivityThread != null) {
       mActivityThread.post(new RunConnection(name, service, 0));
   } else {
       doConnected(name, service);
   }
}

RunConnection.run()中會調用LoadedApk.doConnected()方法:

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

3.2.2.6 LoadedApk.doConnected():

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

    synchronized (this) {
        .....................
        ServiceDispatcher.ConnectionInfo old;
        //很有意思,也就是說同一組件中對同一個service重復綁定,onServiceConnected()只會執行一次
        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.
            mDied = false;
            info = new ConnectionInfo();
            info.binder = service;
            info.deathMonitor = new DeathMonitor(name, service);
            try {
                // 設置死亡回調
                service.linkToDeath(info.deathMonitor, 0);
                // 將info保存在LoadedApk.mActiveConnections
                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 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);
    }
}

doConnected()比較有意思的是下面的代碼:

ServiceDispatcher.ConnectionInfo old;
old = mActiveConnections.get(name);
      if (old != null && old.binder == service) {
          // Huh, already have this one.  Oh well!
          return;
}

mActiveConnections來自ServiceDispatcher:

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

ServiceDispatcher.mActiveConnections用來記錄該LoadedApk中的組件所綁定的service的連接信息。key是service的組件名,value是ServiceDispatcher.ConnectionInfo。

ConnectionInfo中記錄了service的代理binder以及死亡通知回調。

private static class ConnectionInfo {
  // 遠端service的代理binder
  IBinder binder;
  // 遠端service 死亡通知回調
  IBinder.DeathRecipient deathMonitor;
}

connected(ComponentName name, IBinder service)方法的第一個參數是service的組件名,第二個參數是service的代理binder。

  • connected()方法中首先以name為key在mActiveConnections查找,如果索引到的ConnectionInfo的binder與傳入的binder一致的話,說明是同一組件內使用同一個ServiceConnection接口對象對同一個service重復綁定,此時不會執行onServiceConnected()方法。

4 unbindeService

現在在看一下unbindeService()的過程,直接看AMS.unbindServiceLocked(),整個過程大體上就是找到相關的連接對象ConnectionRecord,將其從相關的map中移除,然后根據情況決定是否調用serive.onUnbind生命周期方法

4.1 unbindServiceLocked

boolean unbindServiceLocked(IServiceConnection connection) {
       // 得到ServiceDispatcher.InnerConnection的binder
       IBinder binder = connection.asBinder();
       if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
       // 前面說了ActiveService.mServiceConnections,這里面記錄了AMS中所有app的service的連接
       // 自然也包括同一組件內使用同一個ServiceConnection綁定同一個service的情況
       ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
       // 說明沒有使用該ServiceConnection接口對象綁定過service
       // 所以無需unbind
       if (clist == null) {
           Slog.w(TAG, "Unbind failed: could not find connection for "
                 + connection.asBinder());
           return false;
       }

       final long origId = Binder.clearCallingIdentity();
       try {
           // 依次取出同一組件內使用該ServiceConnection接口對象綁定的service的連接信息對象ConnectionRecord
           // 這里要注意的是,這相當于在發起unbindService()操作的組件中,對所有使用該ServiceConnection接口對象綁定的sercvice
           // 發起unbindService操作
           while (clist.size() > 0) {
               ConnectionRecord r = clist.get(0);
               removeConnectionLocked(r, null, null);
               if (clist.size() > 0 && clist.get(0) == r) {
                   // In case it didn't get removed above, do it now.
                   Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
                   clist.remove(0);
               }
               if (r.binding.service.app != null) {
                   // This could have made the service less important.
                   if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                       r.binding.service.app.treatLikeActivity = true;
                       mAm.updateLruProcessLocked(r.binding.service.app,
                               r.binding.service.app.hasClientActivities
                               || r.binding.service.app.treatLikeActivity, null);
                   }
                   mAm.updateOomAdjLocked(r.binding.service.app);
               }
           }
       } finally {
           Binder.restoreCallingIdentity(origId);
       }

       return true;
   }

4.2 removeConnectionLocked():

void removeConnectionLocked(
        ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
        IBinder binder = c.conn.asBinder();
        AppBindRecord b = c.binding;
        ServiceRecord s = b.service;
        // 從ServiceRecord中取出所有綁定該service的連接
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
        if (clist != null) {
            // 將使用要unbindService()的ServiceConnection創建的連接
            // 從ServiceRecord.connections中移除
            clist.remove(c);
            if (clist.size() == 0) {
                s.connections.remove(binder);
            }
        }
        // 將使用要unbindService()的ServiceConnection創建的連接
        // 從AppBindRecord.connections中移除
        b.connections.remove(c);
        if (c.activity != null && c.activity != skipAct) {
            if (c.activity.connections != null) {
                c.activity.connections.remove(c);
            }
        }
        if (b.client != skipApp) {
            b.client.connections.remove(c);
            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                b.client.updateHasAboveClientLocked();
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
        }
        clist = mServiceConnections.get(binder);
        if (clist != null) {
            // 將使用要unbindService()的ServiceConnection創建的連接
            // 從ActiveService.mServiceConnections中移除
            clist.remove(c);
            if (clist.size() == 0) {
                mServiceConnections.remove(binder);
            }
        }

        mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);

        // 如果AppBindRecord.connections.size為0
        // 表示某客戶端已經沒有組件與該service綁定了
        // 那么將客戶端從IntentBindRecord.apps中移除
        if (b.connections.size() == 0) {
            b.intent.apps.remove(b.client);
        }

        if (!c.serviceDead) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Disconnecting binding " + b.intent
                    + ": shouldUnbind=" + b.intent.hasBound);
            // 如果IntentBindRecord.apps.size為0,表示沒有客戶端與該service綁定了
            // 那么開始回調service進程的scheduleUnbindService(),執行service.unbind()
            if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                    && b.intent.hasBound) {
                try {
                    bumpServiceExecutingLocked(s, false, "unbind");
                    if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
                            && s.app.setProcState <= ActivityManager.PROCESS_STATE_RECEIVER) {
                        // If this service's process is not already in the cached list,
                        // then update it in the LRU list here because this may be causing
                        // it to go down there and we want it to start out near the top.
                        mAm.updateLruProcessLocked(s.app, false, null);
                    }
                    mAm.updateOomAdjLocked(s.app);
                    b.intent.hasBound = false;
                    // Assume the client doesn't want to know about a rebind;
                    // we will deal with that later if it asks for one.
                    b.intent.doRebind = false;
                    s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
                } catch (Exception e) {
                    Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
                    serviceProcessGoneLocked(s);
                }
            }

            if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
                boolean hasAutoCreate = s.hasAutoCreateConnections();
                if (!hasAutoCreate) {
                    if (s.tracker != null) {
                        s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
                                SystemClock.uptimeMillis());
                    }
                }
                // 根據情況決定是否調用service.onDestroy()方法
                bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
            }
        }
    }

scheduleUnbindService()會導致下面的方法在service的主線程中執行:

4.3 scheduleUnbindService()

private void handleUnbindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                // 執行service.onUnbind()生命周期方法
                boolean doRebind = s.onUnbind(data.intent);
                try {
                    if (doRebind) {
                        // 如果onUnbind()的返回值設置為true的話,
                        // 調用AMS.unbindFinished()
                        ActivityManagerNative.getDefault().unbindFinished(
                                data.token, data.intent, doRebind);
                    } else {
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                } catch (RemoteException ex) {
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to unbind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

unbindeService()代碼就說道這里,接下來總結下:

    1. ServiceConnection.onServiceDisconnected()并不會在unbindeService()過程中調用,它只會在service進程被終止時回調通知綁定的客戶端;
    1. 只有在綁定這個service的所有客戶端中的組件都執行了unbindeService()時,service才會被銷毀,執行servcice.onDestroy(),這也就要求暗示我們當完成于服務的交互后,最好unbindeService,這樣有利于系統及時回收service資源;
    1. 當目標service所在的進程被殺掉時(即除了正?;厥誷ervice),系統并不會銷毀之前的與該service綁定的組件創建的連接,一旦service后續再次運行,系統會再次回調onServiceConnected();
    1. 如果service是通過startService()啟動的,那么service將一直運行到其通過 stopSelf() 自行停止,或其他組件調用 stopService() 為止,無論其是否綁定到任何客戶端;
    1. unbindeService()操作也是異步操作的;
    1. 一般情況下,只要service被客戶端綁定過了,當其再被綁定時,不會在調用service.onBind()方法了,也就是說通常service.onBind()只會執行一次;
    1. 同一個組件內使用同一個ServiceConnection接口對象,重復綁定一個service時,會在AMS中為起創建連接,但是不會導致ServiceConnection.onServiceConnected()方法執行
    1. 同一個組件內執行unbindeService(ServiceConnection sc)時,相當于對所有使用sc綁定的service執行一次unbindeService操作;

這里比較有趣的是,假設同一組件內使用sc重復綁定的一個servcie N次,那么這一次unbindeService(),當對于該該組件綁定該service來說執行了N次unbindeService操作。

5 生命周期的總結:

  • 單獨使用bindService(),unbindService()會經歷:->onCreate()->onBind()->Service running->onUnbind() -> onDestroy();
  • 單獨使用startService(),stopService()會經歷:->onCreate()->onStartCommand()->Service running-> onDestroy();
  • 先調用startService(),再調用bindService()方法:
    • a. 如果結束只調用unbindService(),那么只會執行到onUnbind(),將不會執行onDestroy():->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind();
    • b. 如果在unbindService后,在調用stopService(),那么:->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()->onDestroy();

service的生命周期方法都運行在主線程中,所以如果要在生命周期中執行耗時操作,請額外開啟線程。

參考

Android6.0之App的Service組件運行機制之bindService
Android 7.0 中 Service bind 流程詳解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372