Android IBinder的linkToDeath介绍及情景模拟

来源:互联网 发布:c语言英语怎么说 编辑:程序博客网 时间:2024/06/03 23:41

最近查看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

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

它们的流程图如下(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

它是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

注释说的很清楚,通过一个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

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

可以看到,它会在被启动时过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

好了,以上是服务器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

接下来就是验证时间,把两个App都装进了手机,先启动服务App,再启动客户端App,然后查看Log。我习惯于打开CMD,然后adb logcat | findstr frank。因为我在demo中将Log的TAG都设置为了frank.

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

I/frank   (17732): onBindI/frank   (17475): onServiceConnected {com.example.deathrecipientdemo/com.example.deathrecipientdemo.TestService}  com.example.deathrecipientdemo.ITestD/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 diedI/frank   (17475): onServiceDisconnected {com.example.deathrecipientdemo/com.example.deathrecipientdemo.TestService}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

I/frank (17475): binder is died
这个被成功打印,所以代表我们实现成功了,我们能够准确运用IBinder.DeathRecipient接口去监听服务端的消失变动。当然实际开发中,我们要根据业务是否存在这样的需求而去编码,这里只作为学习探讨之用,反正我觉得涉及到服务之间的交互这个功能是很有用武之地的。

原创粉丝点击