Android Service數據結構

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

  • ServiceRecord :一個表示應用層的Service的數據結構。
    因爲bindService是可以跨進程的,在A進程綁定的服務可以與A運行在同一進程,也可以運行在另一個B進程中。所以爲了方便理解我將其內部主要成員分爲兩類。
    第一類成員描述請求綁定服務的客戶端進程:

  1. bindings :ArrayMap<Intent.FilterComparison, IntentBindRecord> 綁定一個服務一定會在客戶端進程綁定時傳入一個Intent,IntentBindRecord可以簡單理解爲對應客戶端進程的Intent。這裏的集合表示對客戶端綁定服務時傳入的Intent的一個集合(因爲同一個服務可能會有多個不同的Intent與它對應)。
  2. connections :ArrayMap<IBinder, ArrayList< ConnectionRecord >> 綁定一個服務一定會在客戶端傳入一個ServiceConnection,ConnectionRecord可以簡單理解爲對應客戶端的ServiceConnection。這裏的集合表示對客戶端綁定服務時傳入的ServiceConnection在系統中的一個集合(因爲同一個服務可能會有多個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的數據結構保存在成員變量bindings中。
  5. 一個ServiceRecord有可能存在多個ServiceConnection,這些ServiceConnection有可能屬於同一個進程,也可能屬於不同的進程。在ServiceRecord中直接以客戶端進程綁定服務時傳入的ServiceConnnectio所對應的IBinder爲key(實際上並不是直接的對應,中間還經過一個ServiceDispatcher類),以connections成員變量來保存所有綁定到該ServiceRecord的ConnectionRecord。
  • IntentBindRecord :一個表示表示待綁定的服務對應的Intent的數據結構。
  1. service :ServiceRecord 要綁定的服務。
  2. Intent : Intent.FilterComparison 用於區別不同的Intent。
  3. apps : ArrayMap<ProcessRecord, AppBindRecord> 記錄綁定服務的進程(以ProcessRecord爲鍵,對應一個數據結構AppBindRecord)。
  4. binder : IBinder 綁定的服務(宿主進程的Service的onBind方法返回)。

從該結構得到信息:
系統對ServiceRecord的管理優先針對Intent歸類,然後在同一個Intent發起的同一個服務下再根據進程歸類。

  • AppBindRecord :一個表示請求綁定服務的客戶端進程的數據結構。
  1. service :ServiceRecord 要綁定的服務。
  2. intent :IntentBindRecord 表示客戶端進程綁定服務時的Intent。
  3. client :ProcessRecord 表示綁定該服務的客戶端進程。
  4. connections :ArraySet< ConnectionRecord > 表示在當前請求綁定服務的進程中綁定當前服務時的ServiceConnection列表(有可能存在多個,因爲一個客戶端進程內可能構造了多個ServiceConnection在不同的地方綁定服務。區別於ServiceRecord中的connections,這裏保存的僅僅是當前進程內綁定當前服務的ConnectionRecord,而前者保存的是所有進程中綁定到該服務的ConnectionRecord)。
  • ConnectionRecord :一個表示應用層ServiceConnection的數據結構,用於與應用層實現的ServiceConnection通信。
  1. binding :AppBindRecord 表示當前ConnectionRecord所屬的客戶端進程。
  2. activity :ActivityRecord 表示客戶端進程綁定服務時的Activity上下文(當綁定的上下文不爲Activity時爲null)。
  3. conn :IServiceConnection 表示客戶端的ServiceConnection對應的IServiceConnection對象。通過Binder間進程通信該成員可以實現與客戶端ServiceConnection的通信。

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

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

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

  1. 圖中根節點ActiveServices是服務於AMS的一個類,主要負責管理Service。
  2. ActiveServices中兩個集合分別用於保存當前系統中啓動的服務和當前系統中有綁定到任何服務的ConnectionRecord。
  3. 系統中保存的服務以ServiceRecord結構表示,其中兩個重要的成員,bindings是一個容器,用於保存啓動該服務時使用的Intent。connections也是一個容器,用於保存系統中綁定到當前服務的ConnectionRecord。
  4. bindings中保存表示Intent的IntentBindRecord結構類,前面分析過,當前ServiceRecord有可能會在綁定時被傳入不同的Intent,系統中用IntentBindRecord結構來表示。
  5. 在當前ServiceRecord對Intent歸類後再利用IntentBindRecord內部的apps成員來對綁定當前服務的客戶端進程歸類。也就是說如果不同的兩個進程用相同的Intent綁定服務時,這兩個不同的進程會保存在同一個IntentBindRecord的apps成員中。
  6. 最終在表示客戶端進程的結構AppBindRecord的connections成員變量中保存了在AppBindRecord表示的進程中綁定到當前服務的ConnectionRecord(注意區別ServiceRecord中的connections)。

 

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

  • 栗子一
    • 綁定服務的客戶端進程與服務宿主進程爲同一個進程。
    • 使用同樣的Intent(注意這裏雖然是不同的Intent實例對象,但是Intent的比較是根據內部數據內容來比較的,詳見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被其自身綁定了,所以在系統中就保存了一個對應的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實例,但是在系統中每次綁定服務時都會包裝客戶端的ServiceConnection實例後新構造一個ConnectionRecord實例。但是ConnectionRecord的dump信息中將內部包裝的客戶端ServiceConnection實例的hashcode也打出來了,就是緊跟在冒號後面的@1515e72,所以從這裏說明當前雖然使用的是不同的ConnectionRecord實例,但是綁定服務時的ServiceConnection是同一個。
5.跟着的All Connections與Bindings並列,表示的是ServiceRecord結構中connections包含的ConnectionRecord,也就是綁定了當前服務的所有進程中的連接。因爲這個栗子中只有一個進程綁定了當前服務,所以這裏的內容與上一條一致。
6.最後面的Connection bindings to service在dump出來的格式化內容中是與第二行的User 0 active services並列的,表示的是ActiveServices類中mServiceConnections成員變量保存的ConnectionRecord,表示當前系統中所有綁定了服務的ConnectionRecord,但這裏我們只看到綁定了MyService服務的兩個ConnectionRecord,這是因爲dumpsys activity -p 指定了包名,所以這裏在dump時過濾了不在該包名的進程下綁定服務的連接。這裏也正好印證了圖中mServiceMap和mServiceConnections的關係。

  • 栗子二
    • 綁定服務的客戶端進程與服務宿主進程爲同一個進程。
    • 使用不同的Intent(注意這裏雖然是不同的Intent實例對象,但是Intent的比較是根據內部數據內容來比較的,詳見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}}
所以這裏看到系統在管理ServiceRecord的相關數據結構時,是優先對Intent歸類,再在每一個IntentBindRecord中對進程做歸類。

  • 栗子三
    • 綁定服務的客戶端進程與服務宿主進程爲同一個進程。
    • 使用不同的Intent(注意這裏雖然是不同的Intent實例對象,但是Intent的比較是根據內部數據內容來比較的,詳見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

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

  • 栗子四
    • 兩個進程作爲綁定服務的客戶端(clientA, clientB)。另一個進程作爲服務宿主進程(serviceDemo)。
    • 使用相同的Intent(注意這裏雖然是不同的Intent實例對象,但是Intent的比較是根據內部數據內容來比較的,詳見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.與之前的區別主要在IntentBindRecord結構中,此時看到IntentBindRecord的apps成員變量中保存了兩個AppBindRecord結構,分別表示兩個綁定到該服務的進程:
AppBindRecord{123097d ProcessRecord{e70c1a2 21466:com.catsuo.clientdemoa/u0a208}}
AppBindRecord{c7d5072 ProcessRecord{fa86549 21680:com.catsuo.clientdemob/u0a209}}
其他dump內容與之前類似。

  • 兼容性問題
    本身Android碎片化嚴重,跨進程綁定服務這種操作屬於敏感操作,在不同Android版本或者不同的ROM上表現可能有所差異,我在寫栗子四時一開始跑在魅族基於Android OS 5.1.1的Flyme 5.1.11.1A上,發現死活綁定不了服務,客戶端調用bindService後不報錯,也沒什麼異常反應,唯獨返回false,後面參照AOSP代碼排查了種種原因,最終發現基於此版本的跨進程服務綁定需要客戶端和服務提供方共享uid(sharedUserId)。配置同樣的sharedUserId後再此版本上可正常運行。
    同時不配置sharedUserId時在Android 8.1.0原生系統及華爲EMUI上可正常運行。

And Then

瞭解系統管理Service的數據結構,能幫助我們更容易的理解系統管理Service的思想。特別在某些具體case去查閱源碼時會事半功倍。比如:

  1. 進程被kill或者force-stop時系統怎樣處理被kill進程中的Service。
  2. 被kill進程並沒有定義Service,但是持有了遠端某個進程中Service的連接,這時候怎麼處理。
  3. 系統怎麼樣去管理服務保證服務的重啓。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章