一圖解惑之Android管理Service數(shù)據(jù)結構

準備

本文以bindService為例來說明問題,因為bindService的流程比startService復雜的多淮摔。在看圖前需要一些準備浑玛。
Framework中,ServiceRecord對應到應用層的一個Service噩咪。
包括ServiceRecord在內(nèi)的幾個與Service管理相關的數(shù)據(jù)結構在下面簡要說明(列出的成員變量忽略了其他與本文分析無關的變量)顾彰。

  • ServiceRecord :一個表示應用層的Service的數(shù)據(jù)結構。
    因為bindService是可以跨進程的胃碾,在A進程綁定的服務可以與A運行在同一進程涨享,也可以運行在另一個B進程中。所以為了方便理解我將其內(nèi)部主要成員分為兩類仆百。
    第一類成員描述請求綁定服務的客戶端進程:
  1. bindings :ArrayMap<Intent.FilterComparison, IntentBindRecord> 綁定一個服務一定會在客戶端進程綁定時傳入一個Intent厕隧,IntentBindRecord可以簡單理解為對應客戶端進程的Intent。這里的集合表示對客戶端綁定服務時傳入的Intent的一個集合(因為同一個服務可能會有多個不同的Intent與它對應)。
  2. connections :ArrayMap<IBinder, ArrayList< ConnectionRecord >> 綁定一個服務一定會在客戶端傳入一個ServiceConnection吁讨,ConnectionRecord可以簡單理解為對應客戶端的ServiceConnection髓迎。這里的集合表示對客戶端綁定服務時傳入的ServiceConnection在系統(tǒng)中的一個集合(因為同一個服務可能會有多個ServiceConnection與它對應)。

第二類成員描述服務本身宿主進程相關的信息:

  1. serviceInfo :ServiceInfo 通過PMS查到的Service信息建丧。
  2. appInfo :ApplicationInfo 該Service宿主的進程信息排龄。
  3. packageName :String 該Service宿主的進程的包名。
  4. processName :String 該Service宿主的進程的進程名翎朱。
  5. name :ComponentName 該Service的ComponentName橄维。

從該結構得到信息:

  1. 一個應用層的Service對應一個Framework的ServiceRecord。
  2. 一個應用層的ServiceConnection對應一個Framework的ConnectionRecord拴曲。
  3. 一個ServiceRecord宿主于一個進程争舞。
  4. 一個ServiceRecord可以對應多個不同的Intent(比如指定不同的data的不同Intent可以對應同一個服務)。在ServiceRecord中將他們以IntentBindRecord的數(shù)據(jù)結構保存在成員變量bindings中澈灼。
  5. 一個ServiceRecord有可能存在多個ServiceConnection竞川,這些ServiceConnection有可能屬于同一個進程,也可能屬于不同的進程叁熔。在ServiceRecord中直接以客戶端進程綁定服務時傳入的ServiceConnnectio所對應的IBinder為key(實際上并不是直接的對應流译,中間還經(jīng)過一個ServiceDispatcher類),以connections成員變量來保存所有綁定到該ServiceRecord的ConnectionRecord者疤。
  • IntentBindRecord :一個表示表示待綁定的服務對應的Intent的數(shù)據(jù)結構福澡。
  1. service :ServiceRecord 要綁定的服務。
  2. Intent : Intent.FilterComparison 用于區(qū)別不同的Intent驹马。
  3. apps : ArrayMap<ProcessRecord, AppBindRecord> 記錄綁定服務的進程(以ProcessRecord為鍵革砸,對應一個數(shù)據(jù)結構AppBindRecord)。
  4. binder : IBinder 綁定的服務(宿主進程的Service的onBind方法返回)糯累。

從該結構得到信息:
系統(tǒng)對ServiceRecord的管理優(yōu)先針對Intent歸類算利,然后在同一個Intent發(fā)起的同一個服務下再根據(jù)進程歸類。

  • AppBindRecord :一個表示請求綁定服務的客戶端進程的數(shù)據(jù)結構泳姐。
  1. service :ServiceRecord 要綁定的服務效拭。
  2. intent :IntentBindRecord 表示客戶端進程綁定服務時的Intent。
  3. client :ProcessRecord 表示綁定該服務的客戶端進程胖秒。
  4. connections :ArraySet< ConnectionRecord > 表示在當前請求綁定服務的進程中綁定當前服務時的ServiceConnection列表(有可能存在多個缎患,因為一個客戶端進程內(nèi)可能構造了多個ServiceConnection在不同的地方綁定服務。區(qū)別于ServiceRecord中的connections阎肝,這里保存的僅僅是當前進程內(nèi)綁定當前服務的ConnectionRecord挤渔,而前者保存的是所有進程中綁定到該服務的ConnectionRecord)。
  • ConnectionRecord :一個表示應用層ServiceConnection的數(shù)據(jù)結構风题,用于與應用層實現(xiàn)的ServiceConnection通信判导。
  1. binding :AppBindRecord 表示當前ConnectionRecord所屬的客戶端進程嫉父。
  2. activity :ActivityRecord 表示客戶端進程綁定服務時的Activity上下文(當綁定的上下文不為Activity時為null)。
  3. conn :IServiceConnection 表示客戶端的ServiceConnection對應的IServiceConnection對象眼刃。通過Binder間進程通信該成員可以實現(xiàn)與客戶端ServiceConnection的通信绕辖。

從該結構得到信息:
ConnectionRecord結構大致對應客戶端進程的ServiceConnection。

上面是對這些數(shù)據(jù)結構內(nèi)部關鍵屬性的簡要分析擂红。如果你覺得看不懂(暗罵這傻X寫的啥呀)仪际,請息怒,我堅信下面上完圖加完料后你應該可能大概會清楚些(對自己的表達能力還是不太自信啊 -_-')篮条。

上圖

此處略去n行源碼,小二吩抓,上圖:


Service Framework.png

查看原圖

圖中矩形代表一個類涉茧,圓角矩形代表其父節(jié)點所代表的類中的成員變量。
簡要總結:

  1. 圖中根節(jié)點ActiveServices是服務于AMS的一個類疹娶,主要負責管理Service伴栓。
  2. ActiveServices中兩個集合分別用于保存當前系統(tǒng)中啟動的服務和當前系統(tǒng)中有綁定到任何服務的ConnectionRecord。
  3. 系統(tǒng)中保存的服務以ServiceRecord結構表示雨饺,其中兩個重要的成員钳垮,bindings是一個容器,用于保存啟動該服務時使用的Intent额港。connections也是一個容器饺窿,用于保存系統(tǒng)中綁定到當前服務的ConnectionRecord。
  4. bindings中保存表示Intent的IntentBindRecord結構類移斩,前面分析過肚医,當前ServiceRecord有可能會在綁定時被傳入不同的Intent,系統(tǒng)中用IntentBindRecord結構來表示向瓷。
  5. 在當前ServiceRecord對Intent歸類后再利用IntentBindRecord內(nèi)部的apps成員來對綁定當前服務的客戶端進程歸類肠套。也就是說如果不同的兩個進程用相同的Intent綁定服務時,這兩個不同的進程會保存在同一個IntentBindRecord的apps成員中猖任。
  6. 最終在表示客戶端進程的結構AppBindRecord的connections成員變量中保存了在AppBindRecord表示的進程中綁定到當前服務的ConnectionRecord(注意區(qū)別ServiceRecord中的connections)你稚。

加料

我覺得可能來幾個栗子吃吃會更形象些。

  • 栗子一
    • 綁定服務的客戶端進程與服務宿主進程為同一個進程朱躺。
    • 使用同樣的Intent(注意這里雖然是不同的Intent實例對象刁赖,但是Intent的比較是根據(jù)內(nèi)部數(shù)據(jù)內(nèi)容來比較的,詳見Intent源碼)长搀。
    • 使用同樣的ServiceConnection實例對象乾闰。

MyService.java

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i("TEST", "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i("TEST", "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intentA = new Intent(this, MyService.class);
        bindService(intentA, mConnection, Context.BIND_AUTO_CREATE);

        Intent intentB = new Intent(this, MyService.class);
        bindService(intentB, mConnection, Context.BIND_AUTO_CREATE);
    }
}

adb shell dumpsys activity -p com.catsuo.servicerecorddemo services 查看dump信息:

ACTIVITY MANAGER SERVICES (dumpsys activity services)
  User 0 active services:
  * ServiceRecord{9306740 u0 euid: 0 com.catsuo.servicerecorddemo/.MyService}
    intent={cmp=com.catsuo.servicerecorddemo/.MyService}
    packageName=com.catsuo.servicerecorddemo
    processName=com.catsuo.servicerecorddemo
    baseDir=/data/app/com.catsuo.servicerecorddemo-2/base.apk
    dataDir=/data/user/0/com.catsuo.servicerecorddemo
    app=ProcessRecord{a9d76e3 9048:com.catsuo.servicerecorddemo/u0a102}
    createTime=-30s682ms startingBgTimeout=--
    lastActivity=-30s679ms restartTime=-30s681ms createdFromFg=true
    Bindings:
    * IntentBindRecord{839703a CREATE}:
      intent={cmp=com.catsuo.servicerecorddemo/.MyService}
      binder=null
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{782a4eb ProcessRecord{a9d76e3 9048:com.catsuo.servicerecorddemo/u0a102}}
        Per-process Connections:
          ConnectionRecord{491a679 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
          ConnectionRecord{89822c3 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
    All Connections:
      ConnectionRecord{89822c3 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
      ConnectionRecord{491a679 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
    Client:
      nothing to dump

  Connection bindings to services:
  * ConnectionRecord{89822c3 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
    binding=AppBindRecord{782a4eb com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@1515e72 flags=0x1
  * ConnectionRecord{491a679 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
    binding=AppBindRecord{782a4eb com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@1515e72 flags=0x1

說明:
1.第二行User 0 active services下面列出的就是ActiveServices中mServicesMap成員中保存的ServiceRecord結構。com.catsuo.servicerecorddemo定義了一個Service盈滴,當前Service被其自身綁定了涯肩,所以在系統(tǒng)中就保存了一個對應的ServiceRecord結構對象轿钠。
2.當前ServiceRecord的bindings中只保存了一個IntentBindRecord結構對象,因為代碼中我們使用的Intent是相同的病苗,都是intent={cmp=com.catsuo.servicerecorddemo/.MyService}疗垛。
3.在IntentBindRecord結構的apps成員變量中保存使用這個Intent綁定當前服務的進程。在這里栗子我們只有一個進程硫朦,所以這里看到只有一個AppBindRecord{782a4eb ProcessRecord{a9d76e3 9048:com.catsuo.servicerecorddemo/u0a102}}贷腕。
4.在AppBindRecord的connections成員中保存的是該進程下綁定了當前服務的連接,這個栗子中有兩個ConnectionRecord:
ConnectionRecord{491a679 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
ConnectionRecord{89822c3 u0 CR com.catsuo.servicerecorddemo/.MyService:@1515e72}
注意雖然在栗子中客戶端只使用了一個ServiceConnection實例咬展,但是在系統(tǒng)中每次綁定服務時都會包裝客戶端的ServiceConnection實例后新構造一個ConnectionRecord實例泽裳。但是ConnectionRecord的dump信息中將內(nèi)部包裝的客戶端ServiceConnection實例的hashcode也打出來了,就是緊跟在冒號后面的@1515e72破婆,所以從這里說明當前雖然使用的是不同的ConnectionRecord實例涮总,但是綁定服務時的ServiceConnection是同一個。
5.跟著的All Connections與Bindings并列祷舀,表示的是ServiceRecord結構中connections包含的ConnectionRecord瀑梗,也就是綁定了當前服務的所有進程中的連接。因為這個栗子中只有一個進程綁定了當前服務裳扯,所以這里的內(nèi)容與上一條一致抛丽。
6.最后面的Connection bindings to service在dump出來的格式化內(nèi)容中是與第二行的User 0 active services并列的,表示的是ActiveServices類中mServiceConnections成員變量保存的ConnectionRecord饰豺,表示當前系統(tǒng)中所有綁定了服務的ConnectionRecord亿鲜,但這里我們只看到綁定了MyService服務的兩個ConnectionRecord,這是因為dumpsys activity -p 指定了包名冤吨,所以這里在dump時過濾了不在該包名的進程下綁定服務的連接狡门。這里也正好印證了圖中mServiceMap和mServiceConnections的關系。

  • 栗子二
    • 綁定服務的客戶端進程與服務宿主進程為同一個進程锅很。
    • 使用不同的Intent(注意這里雖然是不同的Intent實例對象其馏,但是Intent的比較是根據(jù)內(nèi)部數(shù)據(jù)內(nèi)容來比較的,詳見Intent源碼)爆安。
    • 使用同樣的ServiceConnection實例對象叛复。

MyService代碼不變。
MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i("TEST", "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i("TEST", "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intentA = new Intent(this, MyService.class);
        bindService(intentA, mConnection, Context.BIND_AUTO_CREATE);

        Intent intentB = new Intent(this, MyService.class);
        intentB.setData(Uri.parse("file://xxxx"));
        bindService(intentB, mConnection, Context.BIND_AUTO_CREATE);
    }
}

adb shell dumpsys activity -p com.catsuo.servicerecorddemo services 查看dump信息:

ACTIVITY MANAGER SERVICES (dumpsys activity services)
  User 0 active services:
  * ServiceRecord{8e856ad u0 euid: 0 com.catsuo.servicerecorddemo/.MyService}
    intent={cmp=com.catsuo.servicerecorddemo/.MyService}
    packageName=com.catsuo.servicerecorddemo
    processName=com.catsuo.servicerecorddemo
    baseDir=/data/app/com.catsuo.servicerecorddemo-1/base.apk
    dataDir=/data/user/0/com.catsuo.servicerecorddemo
    app=ProcessRecord{e5bedcd 12499:com.catsuo.servicerecorddemo/u0a102}
    createTime=-6s238ms startingBgTimeout=--
    lastActivity=-6s235ms restartTime=-6s237ms createdFromFg=true
    Bindings:
    * IntentBindRecord{28fb789 CREATE}:
      intent={cmp=com.catsuo.servicerecorddemo/.MyService}
      binder=null
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{e13138e ProcessRecord{e5bedcd 12499:com.catsuo.servicerecorddemo/u0a102}}
        Per-process Connections:
          ConnectionRecord{b7a7c4 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
    * IntentBindRecord{be8c7af CREATE}:
      intent={dat=file://xxxx cmp=com.catsuo.servicerecorddemo/.MyService}
      binder=null
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{f4e87bc ProcessRecord{e5bedcd 12499:com.catsuo.servicerecorddemo/u0a102}}
        Per-process Connections:
          ConnectionRecord{c23cde2 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
    All Connections:
      ConnectionRecord{b7a7c4 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
      ConnectionRecord{c23cde2 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
    Client:
      nothing to dump

  Connection bindings to services:
  * ConnectionRecord{b7a7c4 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
    binding=AppBindRecord{e13138e com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@91c2d7 flags=0x1
  * ConnectionRecord{c23cde2 u0 CR com.catsuo.servicerecorddemo/.MyService:@91c2d7}
    binding=AppBindRecord{f4e87bc com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@91c2d7 flags=0x1

說明:
1.與上一個栗子唯一的不同是此時第二次綁定服務使用的intentB添加了data屬性扔仓,這導致intentA與intentB不再相同褐奥,所以看到在SeviceRecord的bindings成員變量中此時保存了兩個IntentBindRecord結構:
intent={cmp=com.catsuo.servicerecorddemo/.MyService}
intent={dat=file://xxxx cmp=com.catsuo.servicerecorddemo/.MyService}
即當前的ServiceRecord對應了兩個不同的Intent。
2.兩個IntentBindRecord結構的apps成員變量中都保存了一個相同的AppBindRecord結構:
AppBindRecord{f4e87bc ProcessRecord{e5bedcd 12499:com.catsuo.servicerecorddemo/u0a102}}
所以這里看到系統(tǒng)在管理ServiceRecord的相關數(shù)據(jù)結構時翘簇,是優(yōu)先對Intent歸類撬码,再在每一個IntentBindRecord中對進程做歸類。

  • 栗子三
    • 綁定服務的客戶端進程與服務宿主進程為同一個進程版保。
    • 使用不同的Intent(注意這里雖然是不同的Intent實例對象呜笑,但是Intent的比較是根據(jù)內(nèi)部數(shù)據(jù)內(nèi)容來比較的夫否,詳見Intent源碼)。
    • 使用不同樣的ServiceConnection實例對象叫胁。

MyService代碼不變凰慈。
MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intentA = new Intent(this, MyService.class);
        bindService(intentA, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.i("TEST", "onServiceConnected");
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.i("TEST", "onServiceDisconnected");
            }
        }, Context.BIND_AUTO_CREATE);

        Intent intentB = new Intent(this, MyService.class);
        intentB.setData(Uri.parse("file://xxxx"));
        bindService(intentB, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.i("TEST", "onServiceConnected");
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.i("TEST", "onServiceDisconnected");
            }
        }, Context.BIND_AUTO_CREATE);
    }
}

adb shell dumpsys activity -p com.catsuo.servicerecorddemo services 查看dump信息:

ACTIVITY MANAGER SERVICES (dumpsys activity services)
  User 0 active services:
  * ServiceRecord{6346701 u0 euid: 0 com.catsuo.servicerecorddemo/.MyService}
    intent={cmp=com.catsuo.servicerecorddemo/.MyService}
    packageName=com.catsuo.servicerecorddemo
    processName=com.catsuo.servicerecorddemo
    baseDir=/data/app/com.catsuo.servicerecorddemo-2/base.apk
    dataDir=/data/user/0/com.catsuo.servicerecorddemo
    app=ProcessRecord{bf0af93 24820:com.catsuo.servicerecorddemo/u0a102}
    createTime=-7s622ms startingBgTimeout=--
    lastActivity=-7s620ms restartTime=-7s623ms createdFromFg=true
    Bindings:
    * IntentBindRecord{3d3f4df CREATE}:
      intent={cmp=com.catsuo.servicerecorddemo/.MyService}
      binder=null
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{955652c ProcessRecord{bf0af93 24820:com.catsuo.servicerecorddemo/u0a102}}
        Per-process Connections:
          ConnectionRecord{76b34e8 u0 CR com.catsuo.servicerecorddemo/.MyService:@efc7e0b}
    * IntentBindRecord{a37abf5 CREATE}:
      intent={dat=file://xxxx cmp=com.catsuo.servicerecorddemo/.MyService}
      binder=null
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{4970d8a ProcessRecord{bf0af93 24820:com.catsuo.servicerecorddemo/u0a102}}
        Per-process Connections:
          ConnectionRecord{5f2efe7 u0 CR com.catsuo.servicerecorddemo/.MyService:@77859a6}
    All Connections:
      ConnectionRecord{5f2efe7 u0 CR com.catsuo.servicerecorddemo/.MyService:@77859a6}
      ConnectionRecord{76b34e8 u0 CR com.catsuo.servicerecorddemo/.MyService:@efc7e0b}
    Client:
      nothing to dump

  Connection bindings to services:
  * ConnectionRecord{5f2efe7 u0 CR com.catsuo.servicerecorddemo/.MyService:@77859a6}
    binding=AppBindRecord{4970d8a com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@77859a6 flags=0x1
  * ConnectionRecord{76b34e8 u0 CR com.catsuo.servicerecorddemo/.MyService:@efc7e0b}
    binding=AppBindRecord{955652c com.catsuo.servicerecorddemo/.MyService:com.catsuo.servicerecorddemo}
    conn=android.os.BinderProxy@efc7e0b flags=0x1

說明:
結果與栗子二差不多,區(qū)別在于此例在兩次綁定服務時使用了不同的ServiceConnection實例驼鹅。
ConnectionRecord{5f2efe7 u0 CR com.catsuo.servicerecorddemo/.MyService:@77859a6}
ConnectionRecord{76b34e8 u0 CR com.catsuo.servicerecorddemo/.MyService:@efc7e0b}
此時看到除了保存兩個不同的ConnectionRecord實例外微谓,其內(nèi)部包裝的ServiceConnection的實例也不一致(@77859a6 vs @efc7e0b)。雖然這在dump的內(nèi)容上看好像區(qū)別不大输钩,但實際上在ServiceRecord的connections成員變量中保存ConnectionRecord時的數(shù)據(jù)結構是有所差別的:
當ServiceConnection是同一個對象實例時豺型,其保存形式是ServiceConnection -> [ConnectionRecord1, ConnectionRecord2]
當ServiceConnection時不同的對象實例時,其保存形式是ServiceConnection1 -> [ConnectionRecord1], ServiceConnection2 -> [ConnectionRecord2]

  • 栗子四
    • 兩個進程作為綁定服務的客戶端(clientA, clientB)买乃。另一個進程作為服務宿主進程(serviceDemo)姻氨。
    • 使用相同的Intent(注意這里雖然是不同的Intent實例對象,但是Intent的比較是根據(jù)內(nèi)部數(shù)據(jù)內(nèi)容來比較的为牍,詳見Intent源碼)哼绑。
    • 使用不同的ServiceConnection實例對象(兩個ServiceConnection分別位于兩個客戶端進程中岩馍,肯定不同)碉咆。

ClientA
MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i("TEST", "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i("TEST", "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.catsuo.servicedemo", "com.catsuo.servicedemo.MyService"));
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }
}

ClientB
MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i("TEST", "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i("TEST", "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.catsuo.servicedemo", "com.catsuo.servicedemo.MyService"));
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }
}

ServiceDemo
MyService.java

public class MyService extends Service {

    private IBinder mBinder = new InnerBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("TEST", "onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("TEST", "onBind");
        return mBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("TEST", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    class InnerBinder extends android.os.Binder {

    }

}

adb shell dumpsys activity -p com.catsuo.servicedemo services 查看dump信息:

ACTIVITY MANAGER SERVICES (dumpsys activity services)
  User 0 active services:
  * ServiceRecord{70a0769 u0 com.catsuo.servicedemo/.MyService}
    intent={cmp=com.catsuo.servicedemo/.MyService}
    packageName=com.catsuo.servicedemo
    processName=com.catsuo.servicedemo
    baseDir=/data/app/com.catsuo.servicedemo-bpklwUdunrtXN_w8_gBxsg==/base.apk
    dataDir=/data/user/0/com.catsuo.servicedemo
    app=ProcessRecord{cd6b6f3 21197:com.catsuo.servicedemo/u0a207}
    createTime=-27m20s775ms startingBgTimeout=--
    lastActivity=-26m57s884ms restartTime=-27m20s775ms createdFromFg=true
    Bindings:
    * IntentBindRecord{4266527 CREATE}:
      intent={cmp=com.catsuo.servicedemo/.MyService}
      binder=android.os.BinderProxy@a5523d4
      requested=true received=true hasBound=true doRebind=false
      * Client AppBindRecord{123097d ProcessRecord{e70c1a2 21466:com.catsuo.clientdemoa/u0a208}}
        Per-process Connections:
          ConnectionRecord{11af5f0 u0 CR com.catsuo.servicedemo/.MyService:@64dfe33}
      * Client AppBindRecord{c7d5072 ProcessRecord{fa86549 21680:com.catsuo.clientdemob/u0a209}}
        Per-process Connections:
          ConnectionRecord{70d8b6f u0 CR com.catsuo.servicedemo/.MyService:@9058e4e}
    All Connections:
      ConnectionRecord{11af5f0 u0 CR com.catsuo.servicedemo/.MyService:@64dfe33}
      ConnectionRecord{70d8b6f u0 CR com.catsuo.servicedemo/.MyService:@9058e4e}
    Client:
      nothing to dump

說明:
1.ClientA和ClientB代碼基本相同,就是通過顯示的Intent去綁定服務(在Android 5.0之后綁定服務時不允許通過action等隱示綁定蛀恩,否則拋出異常)疫铜。
2.ServiceDemo進程定義了一個服務,客戶端通過包名和服務類名綁定該服務双谆。
3.與之前的區(qū)別主要在IntentBindRecord結構中壳咕,此時看到IntentBindRecord的apps成員變量中保存了兩個AppBindRecord結構,分別表示兩個綁定到該服務的進程:
AppBindRecord{123097d ProcessRecord{e70c1a2 21466:com.catsuo.clientdemoa/u0a208}}
AppBindRecord{c7d5072 ProcessRecord{fa86549 21680:com.catsuo.clientdemob/u0a209}}
其他dump內(nèi)容與之前類似顽馋。

  • 兼容性問題
    本身Android碎片化嚴重谓厘,跨進程綁定服務這種操作屬于敏感操作,在不同Android版本或者不同的ROM上表現(xiàn)可能有所差異寸谜,我在寫栗子四時一開始跑在魅族基于Android OS 5.1.1的Flyme 5.1.11.1A上竟稳,發(fā)現(xiàn)死活綁定不了服務,客戶端調用bindService后不報錯熊痴,也沒什么異常反應他爸,唯獨返回false,后面參照AOSP代碼排查了種種原因果善,最終發(fā)現(xiàn)基于此版本的跨進程服務綁定需要客戶端和服務提供方共享uid(sharedUserId)诊笤。配置同樣的sharedUserId后再此版本上可正常運行。
    同時不配置sharedUserId時在Android 8.1.0原生系統(tǒng)及華為EMUI上可正常運行巾陕。

And Then

了解系統(tǒng)管理Service的數(shù)據(jù)結構讨跟,能幫助我們更容易的理解系統(tǒng)管理Service的思想纪他。特別在某些具體case去查閱源碼時會事半功倍。比如:

  1. 進程被kill或者force-stop時系統(tǒng)怎樣處理被kill進程中的Service许赃。
  2. 被kill進程并沒有定義Service止喷,但是持有了遠端某個進程中Service的連接,這時候怎么處理混聊。
  3. 系統(tǒng)怎么樣去管理服務保證服務的重啟弹谁。
  4. 等等等...
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市句喜,隨后出現(xiàn)的幾起案子预愤,更是在濱河造成了極大的恐慌,老刑警劉巖咳胃,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件植康,死亡現(xiàn)場離奇詭異,居然都是意外死亡展懈,警方通過查閱死者的電腦和手機销睁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來存崖,“玉大人冻记,你說我怎么就攤上這事±淳澹” “怎么了冗栗?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長供搀。 經(jīng)常有香客問我隅居,道長,這世上最難降的妖魔是什么葛虐? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任胎源,我火速辦了婚禮,結果婚禮上屿脐,老公的妹妹穿的比我還像新娘涕蚤。我一直安慰自己,他們只是感情好摄悯,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布赞季。 她就那樣靜靜地躺著,像睡著了一般奢驯。 火紅的嫁衣襯著肌膚如雪申钩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天瘪阁,我揣著相機與錄音撒遣,去河邊找鬼邮偎。 笑死,一個胖子當著我的面吹牛义黎,可吹牛的內(nèi)容都是我干的禾进。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼廉涕,長吁一口氣:“原來是場噩夢啊……” “哼泻云!你這毒婦竟也來了?” 一聲冷哼從身側響起狐蜕,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宠纯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后层释,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婆瓜,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年贡羔,在試婚紗的時候發(fā)現(xiàn)自己被綠了廉白。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡乖寒,死狀恐怖猴蹂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宵统,我是刑警寧澤晕讲,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布覆获,位于F島的核電站马澈,受9級特大地震影響,放射性物質發(fā)生泄漏弄息。R本人自食惡果不足惜痊班,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摹量。 院中可真熱鬧涤伐,春花似錦、人聲如沸缨称。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睦尽。三九已至器净,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間当凡,已是汗流浹背山害。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工纠俭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浪慌。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓冤荆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親权纤。 傳聞我的和親對象是個殘疾皇子钓简,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容