Android IBinder的linkToDeath介绍及情景模拟
2017-06-08 13:35
323 查看
最近查看Framework源码的时候,读到了AudioService处理音量的流程,在这里碰到了IBinder的linkToDeath()这个知识点,比较感兴趣,所以记录下来,并自己写demo尝试了一下。
我们简单来看下AudioService处理静音这一块。
/frameworks/base/media/Java/Android/media/AudioManager.java
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
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.它可以监听申请静音的客户端的存活状态变化。
好吧,下面进入主题。
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
[/code]
它是IBinder的内部接口,接口方法void binderDied()。注释的意思大概是这是一个接受Binder所在的宿主进程消失时的回调,并且建议我们去查看linkToDeath。
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对象异常终止时可以做一些资源释放的处理。
接下来的Demo中我会创建两个App应用,一个作为服务端,一个作为客户端。客户端通过IBinder.DeathRecipient来监听服务端的异常终止情况。
服务端
ITest.aidl
2
3
4
5
1
2
3
4
5
[/code]
TestService.java
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秒后自杀。
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]
好了,以上是服务器App的部分代码。
接下我新建一个客户端的App
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
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
[/code]
I/frank (17475): binder is died
这个被成功打印,所以代表我们实现成功了,我们能够准确运用IBinder.DeathRecipient接口去监听服务端的消失变动。当然实际开发中,我们要根据业务是否存在这样的需求而去编码,这里只作为学习探讨之用,反正我觉得涉及到服务之间的交互这个功能是很有用武之地的。
我们简单来看下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接口去监听服务端的消失变动。当然实际开发中,我们要根据业务是否存在这样的需求而去编码,这里只作为学习探讨之用,反正我觉得涉及到服务之间的交互这个功能是很有用武之地的。
相关文章推荐
- Android IBinder的linkToDeath介绍及情景模拟
- IBinder中linkToDeath的介绍
- 使用IBinder的linkToDeath机制来释放资源
- android 之 linkToDeath和unlinkToDeath。(死亡代理)
- 安卓中的Model-View-Presenter模式介绍[Introduction to Model View Presenter on Android]
- Android的SwipeToDismiss第三方开源框架模拟QQ对话列表侧滑删除,置顶,将头像图片圆形化处理。
- Android 第三方 Jar 的 Link 与问题:Unable to execute dex: Java heap space的解决方法
- (API GUIDE 1)Introduction to Android 介绍安卓
- Android 第三方 Jar 的 Link 与问题:Unable to execute dex: Java heap space的解决方法
- Android学习笔记——Android PullToRefresh (ListView,GridView 下拉刷新) 使用介绍
- Intro to the three Android Audio APIs 介绍 Android的三种音频API。
- AIDL基本使用4—- 4、linkToDeath和unlinkToDeath
- Link Android Source Code to Eclipse
- IOS应用网络数据包拦截之三 网络环境模拟软件Network Link Conditioner使用介绍
- 【Android 开源项目】下拉刷新Android-PullToRefresh介绍
- 介绍Text-To-Speech在Android中的用法
- Link Android Source Code to Eclipse
- 介绍Text-To-Speech在Android中的用法
- LINK: WinRT, Xamarin.Android, Xamarin.iOS - How to implement a "pull-to-refresh" list
- Android IBinder机制简单介绍