準備
本文以bindService為例來說明問題,因為bindService的流程比startService復雜的多淮摔。在看圖前需要一些準備浑玛。
Framework中,ServiceRecord對應到應用層的一個Service噩咪。
包括ServiceRecord在內(nèi)的幾個與Service管理相關的數(shù)據(jù)結構在下面簡要說明(列出的成員變量忽略了其他與本文分析無關的變量)顾彰。
- ServiceRecord :一個表示應用層的Service的數(shù)據(jù)結構。
因為bindService是可以跨進程的胃碾,在A進程綁定的服務可以與A運行在同一進程涨享,也可以運行在另一個B進程中。所以為了方便理解我將其內(nèi)部主要成員分為兩類仆百。
第一類成員描述請求綁定服務的客戶端進程:
- bindings :ArrayMap<Intent.FilterComparison, IntentBindRecord> 綁定一個服務一定會在客戶端進程綁定時傳入一個Intent厕隧,IntentBindRecord可以簡單理解為對應客戶端進程的Intent。這里的集合表示對客戶端綁定服務時傳入的Intent的一個集合(因為同一個服務可能會有多個不同的Intent與它對應)。
- connections :ArrayMap<IBinder, ArrayList< ConnectionRecord >> 綁定一個服務一定會在客戶端傳入一個ServiceConnection吁讨,ConnectionRecord可以簡單理解為對應客戶端的ServiceConnection髓迎。這里的集合表示對客戶端綁定服務時傳入的ServiceConnection在系統(tǒng)中的一個集合(因為同一個服務可能會有多個ServiceConnection與它對應)。
第二類成員描述服務本身宿主進程相關的信息:
- serviceInfo :ServiceInfo 通過PMS查到的Service信息建丧。
- appInfo :ApplicationInfo 該Service宿主的進程信息排龄。
- packageName :String 該Service宿主的進程的包名。
- processName :String 該Service宿主的進程的進程名翎朱。
- name :ComponentName 該Service的ComponentName橄维。
從該結構得到信息:
- 一個應用層的Service對應一個Framework的ServiceRecord。
- 一個應用層的ServiceConnection對應一個Framework的ConnectionRecord拴曲。
- 一個ServiceRecord宿主于一個進程争舞。
- 一個ServiceRecord可以對應多個不同的Intent(比如指定不同的data的不同Intent可以對應同一個服務)。在ServiceRecord中將他們以IntentBindRecord的數(shù)據(jù)結構保存在成員變量bindings中澈灼。
- 一個ServiceRecord有可能存在多個ServiceConnection竞川,這些ServiceConnection有可能屬于同一個進程,也可能屬于不同的進程叁熔。在ServiceRecord中直接以客戶端進程綁定服務時傳入的ServiceConnnectio所對應的IBinder為key(實際上并不是直接的對應流译,中間還經(jīng)過一個ServiceDispatcher類),以connections成員變量來保存所有綁定到該ServiceRecord的ConnectionRecord者疤。
- IntentBindRecord :一個表示表示待綁定的服務對應的Intent的數(shù)據(jù)結構福澡。
- service :ServiceRecord 要綁定的服務。
- Intent : Intent.FilterComparison 用于區(qū)別不同的Intent驹马。
- apps : ArrayMap<ProcessRecord, AppBindRecord> 記錄綁定服務的進程(以ProcessRecord為鍵革砸,對應一個數(shù)據(jù)結構AppBindRecord)。
- binder : IBinder 綁定的服務(宿主進程的Service的onBind方法返回)糯累。
從該結構得到信息:
系統(tǒng)對ServiceRecord的管理優(yōu)先針對Intent歸類算利,然后在同一個Intent發(fā)起的同一個服務下再根據(jù)進程歸類。
- AppBindRecord :一個表示請求綁定服務的客戶端進程的數(shù)據(jù)結構泳姐。
- service :ServiceRecord 要綁定的服務效拭。
- intent :IntentBindRecord 表示客戶端進程綁定服務時的Intent。
- client :ProcessRecord 表示綁定該服務的客戶端進程胖秒。
- connections :ArraySet< ConnectionRecord > 表示在當前請求綁定服務的進程中綁定當前服務時的ServiceConnection列表(有可能存在多個缎患,因為一個客戶端進程內(nèi)可能構造了多個ServiceConnection在不同的地方綁定服務。區(qū)別于ServiceRecord中的connections阎肝,這里保存的僅僅是當前進程內(nèi)綁定當前服務的ConnectionRecord挤渔,而前者保存的是所有進程中綁定到該服務的ConnectionRecord)。
- ConnectionRecord :一個表示應用層ServiceConnection的數(shù)據(jù)結構风题,用于與應用層實現(xiàn)的ServiceConnection通信判导。
- binding :AppBindRecord 表示當前ConnectionRecord所屬的客戶端進程嫉父。
- activity :ActivityRecord 表示客戶端進程綁定服務時的Activity上下文(當綁定的上下文不為Activity時為null)。
- conn :IServiceConnection 表示客戶端的ServiceConnection對應的IServiceConnection對象眼刃。通過Binder間進程通信該成員可以實現(xiàn)與客戶端ServiceConnection的通信绕辖。
從該結構得到信息:
ConnectionRecord結構大致對應客戶端進程的ServiceConnection。
上面是對這些數(shù)據(jù)結構內(nèi)部關鍵屬性的簡要分析擂红。如果你覺得看不懂(暗罵這傻X寫的啥呀)仪际,請息怒,我堅信下面上完圖加完料后你應該可能大概會清楚些(對自己的表達能力還是不太自信啊 -_-')篮条。
上圖
此處略去n行源碼,小二吩抓,上圖:
圖中矩形代表一個類涉茧,圓角矩形代表其父節(jié)點所代表的類中的成員變量。
簡要總結:
- 圖中根節(jié)點ActiveServices是服務于AMS的一個類疹娶,主要負責管理Service伴栓。
- ActiveServices中兩個集合分別用于保存當前系統(tǒng)中啟動的服務和當前系統(tǒng)中有綁定到任何服務的ConnectionRecord。
- 系統(tǒng)中保存的服務以ServiceRecord結構表示雨饺,其中兩個重要的成員钳垮,bindings是一個容器,用于保存啟動該服務時使用的Intent额港。connections也是一個容器饺窿,用于保存系統(tǒng)中綁定到當前服務的ConnectionRecord。
- bindings中保存表示Intent的IntentBindRecord結構類移斩,前面分析過肚医,當前ServiceRecord有可能會在綁定時被傳入不同的Intent,系統(tǒng)中用IntentBindRecord結構來表示向瓷。
- 在當前ServiceRecord對Intent歸類后再利用IntentBindRecord內(nèi)部的apps成員來對綁定當前服務的客戶端進程歸類肠套。也就是說如果不同的兩個進程用相同的Intent綁定服務時,這兩個不同的進程會保存在同一個IntentBindRecord的apps成員中猖任。
- 最終在表示客戶端進程的結構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去查閱源碼時會事半功倍。比如:
- 進程被kill或者force-stop時系統(tǒng)怎樣處理被kill進程中的Service许赃。
- 被kill進程并沒有定義Service止喷,但是持有了遠端某個進程中Service的連接,這時候怎么處理混聊。
- 系統(tǒng)怎么樣去管理服務保證服務的重啟弹谁。
- 等等等...