从 Android 静音看正确的查找 bug 的姿势
0、写在前面
1、实现个静音的功能
PM:『我这里有个需求,很简单很简单那种』RD:『哦,需要做三天』PM:『真的很简单很简单那种』RD:『哦,现在需要做六天了』
private void setMuteEnabled( boolean enabled){
AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, enabled);
}
|
2、『您好,我是京东快递,您有一个bug签收一下』
QA:『如果我先开启静音,然后退出我们的app再进来,尽管页面显示静音状态,但我无法取消静音啊』RD:『一定是你的用法有问题!』
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);
}
|
3、『你可以告诉我该静音或者不静音,但听不听那是我的事儿』
我这么无辜,寥寥几行代码,能犯什么错误呢?所以问题一定出在官方的API上。
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);
}
}
|
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.
好像找到答案了。不对呀,我以你的人格担保,我只发了一次静音请求啊,怎么取消静音就这么费劲呢!
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、『其实,刚才不是我』
对呀,有名片啊,问题是我这是同一个app啊,同一个啊……问题出在哪里了呢。
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);
}});
|
等会儿让我想会儿静静。它在这里new了一个AudioManager。它怎么能new了一个AudioManager呢。
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代码跳转方面的支持已经非常厉害了。
转载于:https://www.cnblogs.com/krislight1105/p/5203164.html
从 Android 静音看正确的查找 bug 的姿势相关推荐
- 从 Android 静音看正确的查bug的姿势?
0.写在前面 没抢到小马哥的红包,无心回家了,回公司写篇文章安慰下自己TT..话说年关难过,bug多多,时间久了难免头昏脑热,不辨朝暮,难识乾坤...艾玛,扯远了,话说谁没踩过坑,可视大家都是如何从坑 ...
- Android 8.1 频频被曝 Bug,是要赶超苹果吗?
点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! Android 8.1被曝严重 Bug,这下看你还想不想吃奥利奥了. 虽然很多采用 Andr ...
- Android Studio之正确导入SO库
Android Studio之正确导入SO相关文件 之前一直没有做过第三方的推送消息,这次公司要求采用国内极光第三方推送服务,由于项目还在原型图构思中,因而还可以花点时间看看这方面的东西. 看了下相关 ...
- Android 入门eclipse+ADT配置,bug处理及附件下载(一)
Android 入门eclipse+ADT配置,bug处理及附件下载(一) 由于时代的变化科技的发展手机也发生了飞跃的变换,从以前的只能音频通话的"大哥大"到现在流行的3G手机:具 ...
- android 静音与振动
android 静音与振动 1,设置静音和振动 静音和振动都属于来电后的动作.所以在设置静音和振动时都只是设置一些标识,并往数据库写入相应标识. 文件:packages/apps/settings/s ...
- 基于Android的看小说APP源码Android本科毕业设计Android小说阅读器、小说APP源码
基于kotlin + 协程 + MVVM 模式来编写的看小说APP. 完整代码下载地址:基于Android的看小说APP源码Android本科毕业设计Android小说阅读器.小说APP源码 主要框架 ...
- 解决bug问题,查找bug的方法
想告别"写代码2分钟,找bug两小时"吗?,就必须掌握查找bug的能力 下面这些方法都是我这几年解决bug常用的 一.定位bug:如果在控制台中看不出bug,可以使用以下方法帮助查 ...
- android 静音接口,android 静音方法
android 静音实现方法 类似语音app实现静音与取消静音 1. 模拟按键 模拟静音键 2.调用静音接口 取消静音时,音量条UI显示音量进度与进度值 private AudioManager mA ...
- android:ellipsize=end 不起作用,Android应用开发Android TextView关于android:ellipsize=end的一个神奇bug解决方案...
本文将带你了解Android应用开发Android TextView关于android:ellipsize=end的一个神奇bug解决方案,希望本文对大家学Android有所帮助. 疑惑 今天在开发过 ...
最新文章
- 植树节特别活动:合种樟子松/华山松/云杉/胡杨
- CentOS5.4下安装和配置Apache、PHP、MySql、PHPMyAdmin
- eclipse创建多模块maven工程小结
- 从零写一个编译器(完结):总结和系列索引
- linux 信号量锁 内核,Linux内核信号量互斥锁应用
- Ubuntu安装Samba实现跟windows文件共享
- 输出一个整数的逆序数
- 戴尔XPS-13超级本赏析
- 某公司PIX 520防火墙系统和NAT的实施
- opencv裁剪图像(不规则裁剪)
- SEO常用辅助工具整合
- IntelliJ IDEA快速入门 | 第九篇:IntelliJ IDEA中的常用配置(一)——设置IntelliJ IDEA的主题
- 技嘉主板+AMD CPU开启CPU虚拟化方法
- SMART 监控项研究以及存储健康分级机制
- 激活函数(阶跃,sigmoid,relu,恒等,softmax)
- python趣味编程入门与实战技巧_Python趣味编程入门与实战
- UBT16:ubuntu安装Listen1
- 【电气专业知识问答】问:电网调度部门对各主要发电厂涉网部分电气设备的监控内容是什么?如何实现?
- 点阵字库显示系列之二:GB2312点阵字库显示
- 轻博客类Web原型制作分享——Tumblr