0、写在前面
没抢到小马哥的红包,无心回家了,回公司写篇文章安慰下自己TT。。话说年关难过,bug多多,时间久了难免头昏脑热,不辨朝暮,难识乾坤。。。艾玛,扯远了,话说谁没踩过坑,可视大家都是如何从坑里爬出来的呢?
1、实现个静音的功能
话说,有那么一天,
0、写在前面
1、实现个静音的功能
private void setMuteEnabled( boolean enabled){ AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, enabled); } |
2、『您好,我是京东快递,您有一个bug签收一下』
boolean persistedMute = mute.getContext().getSharedPreferences( "volume" , Context.MODE_PRIVATE).getBoolean( "Volume.Mute" , false ); muteButton.setChecked(persistedMute); |
private void setMuteEnabled( boolean enabled){ AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, enabled); } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /** * Mute or unmute an audio stream. * <p> * The mute command is protected against client process death: if a process * with an active mute request on a stream dies, this stream will be unmuted * automatically. * <p> * The mute requests for a given stream are cumulative: the AudioManager * can receive several mute requests from one or more clients and the stream * will be unmuted only when the same number of unmute requests are received. * <p> * For a better user experience, applications MUST unmute a muted stream * in onPause() and mute is again in onResume() if appropriate. * <p> * This method should only be used by applications that replace the platform-wide * management of audio settings or the main telephony application. * <p>This method has no effect if the device implements a fixed volume policy * as indicated by {@link #isVolumeFixed()}. * * @param streamType The stream to be muted/unmuted. * @param state The required mute state: true for mute ON, false for mute OFF * * @see #isVolumeFixed() */ 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); } } |
4、『这是我的名片』
1 2 3 4 5 6 7 8 | 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); } } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | /** @see AudioManager#setStreamMute(int, boolean) */ public void setStreamMute( int streamType, boolean state, IBinder cb) { if (mUseFixedVolume) { return ; } if (isStreamAffectedByMute(streamType)) { if (mHdmiManager != null ) { synchronized (mHdmiManager) { if (streamType == AudioSystem.STREAM_MUSIC && mHdmiTvClient != null ) { synchronized (mHdmiTvClient) { if (mHdmiSystemAudioSupported) { mHdmiTvClient.setSystemAudioMute(state); } } } } } mStreamStates[streamType].mute(cb, state); } } |
01 02 03 04 05 06 07 08 09 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 | public void mute( boolean state) { boolean updateVolume = false ; 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 ); } VolumeStreamState. this .mDeathHandlers.add( this ); // If the stream is not yet muted by any client, set level to 0 if (!VolumeStreamState. this .isMuted()) { updateVolume = true ; } } 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 VolumeStreamState. this .mDeathHandlers.remove( this ); // mICallback can be 0 if muted by AudioService if (mICallback != null ) { mICallback.unlinkToDeath( this , 0 ); } if (!VolumeStreamState. this .isMuted()) { updateVolume = true ; } } } } if (updateVolume) { sendMsg(mAudioHandler, MSG_SET_ALL_VOLUMES, SENDMSG_QUEUE, 0 , 0 , VolumeStreamState. this , 0 ); } } |
01 02 03 04 05 06 07 08 09 10 | 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; } …… } |
5、『其实,刚才不是我』
1 | private final IBinder mICallBack = new Binder(); |
1 | AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); |
1 2 3 4 5 | @Override public Object getSystemService(String name) { ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService( this ); } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public Object getService(ContextImpl ctx) { ArrayList<Object> cache = ctx.mServiceCache; Object service; synchronized (cache) { if (cache.size() == 0 ) { // Initialize the cache vector on first access. // At this point sNextPerContextServiceCacheIndex // is the number of potential services that are // cached per-Context. for ( int i = 0 ; i < sNextPerContextServiceCacheIndex; i++) { cache.add( null ); } } else { service = cache.get(mContextCacheIndex); if (service != null ) { return service; } } service = createService(ctx); cache.set(mContextCacheIndex, service); return service; } } |
1 2 3 4 | registerService(AUDIO_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new AudioManager(ctx); }}); |
1 | AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); |
6、『这事儿还是交给同一个人办比较靠谱』
1 | AudioManager mAudioManager = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); |
7、结语
侯捷先生在《STL源码剖析》一书的扉页上面写道『源码之前,了无秘密』。写程序的时候,我经常会因为运行结果与预期不一致而感到不悦,甚至抱怨这就是『命』,想想也是挺逗的。计算机总是会忠实地执行我们提供的程序,如果你发现它『不听』指挥,显然是你的指令有问题;除此之外,我们的指令还需要经过层层传递,才会成为计算机可以执行的机器码,如果你对系统api的工作原理不熟悉,对系统的工作原理不熟悉,你在组织自己的代码的时候就难免一厢情愿。
至于官方API文档,每次看到它都有看到『课本』一样的感觉。中学的时候,老师最爱说的一句话就是,『课本要多读,常读常新』。官方API呢,显然也是这样。没有头绪的时候,它就是我们救星啊。
作为Android开发者,尽管我不需要做Framework开发,但这并不能说明我不需要对Framework有一定的认识和了解。我们应该在平时的开发和学习当中经常翻阅这些系统的源码,了解它们的工作机制有助于我们更好的思考系统api的应用场景。
关于Android系统源码,如果不是为了深入的研究,我比较建议直接在网上直接浏览:
* [Androidxref](http://androidxref.com/),该站点提供了一定程度上的代码跳转支持,以及非常强大的检索功能,是我们查询系统源码的首选。
* [Grepcode](http://grepcode.com/)也可以检索Android系统源码,与前者不同的是,它只包含Java代码,不过也是尺有所长,grepcode在Java代码跳转方面的支持已经非常厉害了。