您的位置:首页 > 移动开发 > Android开发

Android IBinder的linkToDeath介绍及情景模拟

2017-06-08 13:35 323 查看
最近查看Framework源码的时候,读到了AudioService处理音量的流程,在这里碰到了IBinder的linkToDeath()这个知识点,比较感兴趣,所以记录下来,并自己写demo尝试了一下。

我们简单来看下AudioService处理静音这一块。

/frameworks/base/media/Java/Android/media/AudioManager.java

public class AudioManager {
......
/**
* {@hide}
*/
private final IBinder mICallBack = new Binder();

public void setStreamMute(int streamType, boolean state) {
IAudioService service = getService();
try {
service.setStreamMute(streamType, state, mICallBack);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamMute", e);
}
}
......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[/code]
service是一个IAudioService,它的实现类是AudioService,AudioManager.setStreamMute()中会调用AudioService.setStreamMute(streamType, state, mICallBack);其中mICallBack是一个Binder用来记录申请静音的客户端。

/frameworks/base/media/java/android/media/AudioService.java

private VolumeStreamState[] mStreamStates;

//第1步
/** @see AudioManager#setStreamMute(int, boolean) */
public void setStreamMute(int streamType, boolean state, IBinder cb) {
if (isStreamAffectedByMute(streamType)) {
mStreamStates[streamType].mute(cb, state);
}
}

public class VolumeStreamState {
private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death

private VolumeStreamState(String settingName, int streamType) {

mVolumeIndexSettingName = settingName;
mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;

mStreamType = streamType;
mIndexMax = MAX_STREAM_VOLUME[streamType];
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;

// mDeathHandlers must be created before calling readSettings()
mDeathHandlers = new ArrayList<VolumeDeathHandler>();

readSettings();
}
}

//第2步
public synchronized void mute(IBinder cb, boolean state) {
VolumeDeathHandler handler = getDeathHandler(cb, state);
if (handler == null) {
Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
return;
}
handler.mute(state);
}

private class VolumeDeathHandler implements IBinder.DeathRecipient {
private IBinder mICallback; // To be notified of client's death
private int mMuteCount; // Number of active mutes for this client

VolumeDeathHandler(IBinder cb) {
mICallback = cb;
}
//第3步
// must be called while synchronized on parent VolumeStreamState
public void mute(boolean state) {
if (state) {
if (mMuteCount == 0) {
// Register for client death notification
try {
// mICallback can be 0 if muted by AudioService
if (mICallback != null) {
mICallback.linkToDeath(this, 0);
}
mDeathHandlers.add(this);
// If the stream is not yet muted by any client, set level to 0
if (muteCount() == 0) {
Set set = mIndex.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry entry = (Map.Entry)i.next();
int device = ((Integer)entry.getKey()).intValue();
setIndex(0, device, false /* lastAudible */);
}
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
SENDMSG_QUEUE,
0,
0,
VolumeStreamState.this, 0);
}
} catch (RemoteException e) {
// Client has died!
binderDied();
return;
}
} else {
Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
}
mMuteCount++;
} else {
if (mMuteCount == 0) {
Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
} else {
mMuteCount--;
if (mMuteCount == 0) {
// Unregister from client death notification
mDeathHandlers.remove(this);
// mICallback can be 0 if muted by AudioService
if (mICallback != null) {
mICallback.unlinkToDeath(this, 0);
}
if (muteCount() == 0) {
// If the stream is not muted any more, restore its volume if
// ringer mode allows it
if (!isStreamAffectedByRingerMode(mStreamType) ||
mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Set set = mIndex.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry entry = (Map.Entry)i.next();
int device = ((Integer)entry.getKey()).intValue();
setIndex(getIndex(device,
true  /* lastAudible */),
device,
false  /* lastAudible */);
}
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
SENDMSG_QUEUE,
0,
0,
VolumeStreamState.this, 0);
}
}
}
}
}
}

public void binderDied() {
Log.w(TAG, "Volume service client died for stream: "+mStreamType);
if (mMuteCount != 0) {
// Reset all active mute requests from this client.
mMuteCount = 1;
mute(false);
}
}
}

private synchronized int muteCount() {
int count = 0;
int size = mDeathHandlers.size();
for (int i = 0; i < size; i++) {
count += mDeathHandlers.get(i).mMuteCount;
}
return count;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
[/code]
它们的流程图如下(markdown画的图,不怎么美观)

AudioManagerAudioManagerAudioServiceAudioServiceVolumeStreamStateVolumeStreamStateVolumeDeathHandlerVolumeDeathHandlermute()mute()mute()mICallback.linkToDeath(this, 0)或者mICallback.unlinkToDeath(this, 0);
通过上面的代码我们可以得知VolumeDeathHandler继承IBinder.DeathRecipient.它可以监听申请静音的客户端的存活状态变化。

好吧,下面进入主题。

IBinder.DeathRecipient

/**
* Interface for receiving a callback when the process hosting an IBinder has gone away.
*
* @see #linkToDeath
*/
public interface DeathRecipient {
public void binderDied();
}
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
[/code]
它是IBinder的内部接口,接口方法void binderDied()。注释的意思大概是这是一个接受Binder所在的宿主进程消失时的回调,并且建议我们去查看linkToDeath。

/**
* Register the recipient for a notification if this binder
* goes away.  If this binder object unexpectedly goes away
* (typically because its hosting process has been killed),
* then the given {@link DeathRecipient}'s
* {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
* will be called.
*
* <p>You will only receive death notifications for remote binders,
* as local binders by definition can't die without you dying as well.
*
* @throws Throws {@link RemoteException} if the target IBinder's
* process has already died.
*
* @see #unlinkToDeath
*/
public void linkToDeath(DeathRecipient recipient, int flags)
throws RemoteException;

/**
* Remove a previously registered death notification.
* The recipient will no longer be called if this object
* dies.
*
* @return Returns true if the <var>recipient</var> is successfully
* unlinked, assuring you that its
* {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
* will not be called.  Returns false if the target IBinder has already
* died, meaning the method has been (or soon will be) called.
*
* @throws Throws {@link java.util.NoSuchElementException} if the given
* <var>recipient</var> has not been registered with the IBinder, and
* the IBinder is still alive.  Note that if the <var>recipient</var>
* was never registered, but the IBinder has already died, then this
* exception will <em>not</em> be thrown, and you will receive a false
* return value instead.
*/
public boolean unlinkToDeath(DeathRecipient recipient, int flags);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[/code]
注释说的很清楚,通过一个IBinder.linkToDeath()可以监听这个Binder本身的消失,并调用回调DeathRecipient.binderDied().IBinder.unlinkToDeath()可以取消监听。

Android的c/s服务架构中,难免会发生服务端或者客户端异常终止的情况,而通过IBinder.DeathRecipient可以很好处理这种情况,当IBinder对象异常终止时可以做一些资源释放的处理。

实战 情景模拟

之前说过在Framework代码中AudioService出现过IBinder.DeathRecipient,但是我内心蠢蠢欲动,我就是想自己实践看看效果。有没有方法呢?自然有。

接下来的Demo中我会创建两个App应用,一个作为服务端,一个作为客户端。客户端通过IBinder.DeathRecipient来监听服务端的异常终止情况。

服务端

ITest.aidl

package com.example.deathrecipientdemo;

interface ITest{
void test();
}
1
2
3
4
5
1
2
3
4
5
[/code]
TestService.java

package com.example.deathrecipientdemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class TestService extends Service {
private static final String TAG = "frank";

private Binder mBinder = new ITest.Stub() {

@Override
public void test() throws RemoteException {
// TODO Auto-generated method stub
Log.i(TAG, "server");
}
};

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.i(TAG, "onBind");
new Thread(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(TAG,e.toString());
}
//结束自己
android.os.Process.killProcess(android.os.Process.myPid());
//TestService.this.stopSelf();
//Log.i("test", "stopSelf");
}
}).start();
return mBinder;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[/code]
可以看到,它会在被启动时过10秒后自杀。

<service android:name="com.example.deathrecipientdemo.TestService"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="com.frank.test"/>
</intent-filter>
</service>
1
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]
好了,以上是服务器App的部分代码。

接下我新建一个客户端的App

package com.example.testdemo;

public class MainActivity extends Activity {

private static final String TAG = "frank";
private ServiceConnection mCon;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.deathrecipientdemo","com.example.deathrecipientdemo.TestService"));
intent.setAction("com.frank.test");
final DeathRecipient deathHandle = new DeathRecipient() {

@Override
public void binderDied() {
// TODO Auto-generated method stub
Log.i(TAG, "binder is died");
}
};

mCon = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.i(TAG, "onServiceDisconnected "+name.toShortString());

}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
try {
Log.i(TAG, "onServiceConnected "+name.toShortString()+"  "+service.getInterfaceDescriptor());
service.linkToDeath(deathHandle, 0);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};

bindService(intent,mCon,Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unbindService(mCon);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[/code]
接下来就是验证时间,把两个App都装进了手机,先启动服务App,再启动客户端App,然后查看Log。我习惯于打开CMD,然后adb logcat | findstr frank。因为我在demo中将Log的TAG都设置为了frank.

按照理想中的预期,服务端被启动后10秒就会自杀,而自杀时客户端能够监听得到,真实情况是不是这样的呢?看log

I/frank   (17732): onBind
I/frank   (17475): onServiceConnected {com.example.deathrecipientdemo/com.example.deathrecipientdemo
.TestService}  com.example.deathrecipientdemo.ITest
D/ActivityThread(17732): SVC-BIND_SERVICE handled : 0 / BindServiceData{token=android.os.BinderProxy
@5ebc3b5 intent=Intent { act=com.frank.test cmp=com.example.deathrecipientdemo/.TestService }}
I/frank   (17475): binder is died
I/frank   (17475): onServiceDisconnected {com.example.deathrecipientdemo/com.example.deathrecipientd
emo.TestService}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
[/code]
I/frank (17475): binder is died

这个被成功打印,所以代表我们实现成功了,我们能够准确运用IBinder.DeathRecipient接口去监听服务端的消失变动。当然实际开发中,我们要根据业务是否存在这样的需求而去编码,这里只作为学习探讨之用,反正我觉得涉及到服务之间的交互这个功能是很有用武之地的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: