一图解惑之android管理service数据结构

1,489 阅读15分钟

一图解惑之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写的啥呀),请息怒,我坚信下面上完图加完料后你应该可能大概会清楚些(对自己的表达能力还是不太自信啊 -_-‘)。

上图

此处略去n行源码,小二,上图:

Alt text
查看原图

图中矩形代表一个类,圆角矩形代表其父节点所代表的类中的成员变量。
简要总结:

  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. 系统怎么样去管理服务保证服务的重启。
  4. 等等等…