赞
踩
最近查看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);
- }
- }
- ......
- }
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;
- }
它们的流程图如下(markdown画的图,不怎么美观)
通过上面的代码我们可以得知VolumeDeathHandler继承IBinder.DeathRecipient.它可以监听申请静音的客户端的存活状态变化。
好吧,下面进入主题。
- /**
- * Interface for receiving a callback when the process hosting an IBinder has gone away.
- *
- * @see #linkToDeath
- */
- public interface DeathRecipient {
- public void binderDied();
- }
它是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);
注释说的很清楚,通过一个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();
- }
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;
- }
-
-
- }
可以看到,它会在被启动时过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>
好了,以上是服务器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);
- }
- }
接下来就是验证时间,把两个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}
I/frank (17475): binder is died
这个被成功打印,所以代表我们实现成功了,我们能够准确运用IBinder.DeathRecipient接口去监听服务端的消失变动。当然实际开发中,我们要根据业务是否存在这样的需求而去编码,这里只作为学习探讨之用,反正我觉得涉及到服务之间的交互这个功能是很有用武之地的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。