Event level 2

本文深入探讨了不同浏览器中事件处理机制的区别,特别是事件捕获与冒泡的过程,并提供了如何使用addEventListener进行事件绑定的具体实例,同时介绍了如何阻止事件冒泡及取消默认行为的方法。

写在前面的话:

      lte IE8的浏览器不支持事件捕获,只支持事件冒泡,是这样来绑定事件:element.attachEvent('onclick',doSomething),注意事件名字有前缀on。

      阻止事件冒泡,在lte IE8的浏览器里通过设置window.event.cancelBubble = true;在标准浏览器里通过e.stopPropagation()来实现。

      从ie9开始符合标准事件机制,任何发生在w3c事件模型中的事件,首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段。在使用addEventListener()绑定事件时你可以通过最后一个参数 (useCapture)true 或 false 来设定事件发生在捕获阶段还是冒泡阶段。例如:

document.getElementById("red").addEventListener("click",function(){console.log("capture")},true);
document.getElementById("red").addEventListener("click",function(){console.log("bubble")},false);
//在标准浏览器里,点击 id是red的元素后 会依次输出 capture bubble,从而说明事件的发生顺序。


var event = new Event(type [, eventInitDict]);不提倡使用document.createEvent(),来创建事件对象

      返回一个event对象,它的属性type的值等于参数type, 可选项参数 eventInitDicy允许你通过同名的对象成员设置 event对象的bubbles属性和cancelable属性。

event.type
      返回event对象的type值,例如: "click", "hashchange",or"submit"

event.target

      返回发生事件的对象

event.currentTarget

      事件侦听器的回调被调用的对象(也就是说绑定事件的那个对象)

event.eventPhase

      返回event的阶段,分别是 :

      NONE(数值是0),   表示事件还没发生
      CAPTURING_PHASE(数值是1),  表示捕获阶段
      AT_TARGET(数值是2),  表示正处在发生事件的对象上
      BUBBLES_PHASE(数值是3),  表示冒泡阶段

event.stopPropagation()

      停止冒泡

event.stopImmediatePropagation()

      假如你绑定了好几个回调函数,在其中一个回调函数里调用了stopImmediatePropagation(),那么该回调函数之后绑定的回调函数将不再执行,当前事件也回被阻止冒泡。

event.bubbles

      布尔值 表示事件是否沿着DOM tree冒泡

event.cancelable

      决定是否可以通过调用event.preventDefault()来取消默认行为

event.preventDefault()

      当 event.cancelable是true的时候,调用event.preventDefault()通知有当前事件引起的默认动作被取消。

event.defaultPrevented

      如果event.cancelable的值是true,并且event.preventDefault()被调用了,那么event.defaultPrevented就返回true,否则就是false。

event.isTrusted

      如果事件是由浏览器触发的,那么就返回true,否则就false.

event.timeStamp

      返回事件创建时间到1970的毫秒数


参考:

      http://dom.spec.whatwg.org/#event

      https://developer.mozilla.org/en-US/docs/Web/API/Event


<think>我们正在解决两个问题: 1. mAudioManager.setStreamVolume 不起作用 2. 理解日志信息 "vol.Events: writeEvent level_changed STREAM_MUSIC 2" 的含义及解决办法 首先,分析第二个问题:日志信息 "vol.Events: writeEvent level_changed STREAM_MUSIC 2" 含义: - 这是Android系统记录的一个事件日志,表示音量发生了变化。 - "vol.Events": 音量事件。 - "writeEvent": 写入事件(日志记录)。 - "level_changed": 音量级别改变。 - "STREAM_MUSIC": 音乐流类型(对应AudioManager.STREAM_MUSIC)。 - "2": 表示音乐流的音量级别被设置为2(注意,这个值可能是在0到最大音量级别之间的整数)。 这个日志信息本身是正常的,它只是记录了一次音量改变事件。但是,如果你在调用`setStreamVolume`后看到这个日志,但实际音量没有变化,那么可能是系统底层的问题。 接下来,针对第一个问题:`mAudioManager.setStreamVolume`不起作用。 在Android中,使用`AudioManager`的`setStreamVolume`方法设置音量无效,可能由多种原因引起。以下是一些常见原因及解决方法: ### 1. 权限问题 确保应用已获得`android.permission.MODIFY_AUDIO_SETTINGS`权限(在AndroidManifest.xml中声明)。 注意:从Android 6.0(API 23)开始,此权限是普通权限,不需要动态申请。 ### 2. 音量范围错误 在设置音量之前,需要查询当前流类型的最大音量级别: ```java int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); ``` 然后,设置的值必须在0到`maxVolume`之间(包含端点)。如果设置的值超出范围,系统会将其调整到边界值,但不会报错。 ### 3. 音频焦点或播放状态的影响 - 如果应用程序没有获得音频焦点,某些系统可能会忽略音量的改变(但这种情况较少见)。 - 对于STREAM_MUSIC,通常只有在有媒体正在播放时,音量的调整才会被系统接受。尝试在播放音频时调整音量。 ### 4. 设备特定问题(如MTK平台) 如之前所述,有些设备制造商(如MTK)可能有自定义的音频策略,限制了音量的设置。例如: - 耳机未正确检测到,导致音量设置被忽略。 - 系统音量同步机制重置了音量。 解决办法(需要系统源码修改,针对设备制造商): - 修改音频策略文件(如`AudioPolicyManager.cpp`)中关于耳机音量限制的部分。 - 调整音量曲线配置文件(如`volumes_mtk.xml`)。 但作为应用开发者,无法修改系统源码,因此可以尝试以下规避方法: - **延迟设置音量**:在检测到耳机插入事件后,延迟一段时间再设置音量。 - **重试机制**:如果第一次设置失败,尝试多次设置(但注意不要过于频繁)。 ### 5. 系统版本差异 在Android 8.0(API 26)及以上,`setStreamVolume`方法的行为可能受到“音量组”概念的影响。建议使用新的音量控制API:`AudioManager.adjustStreamVolume`或`adjustSuggestedStreamVolume`。 ### 6. 使用`adjustVolume`代替 如果只是想调整音量(增大、减小或静音),可以使用: ```java // 增大音量 mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0); // 减小音量 mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0); ``` ### 7. 检查是否处于静音模式 如果设备处于静音模式,设置音量可能无效(但通常静音模式只会影响铃声和通知,不影响媒体音量)。可以使用以下方法检查: ```java int ringerMode = mAudioManager.getRingerMode(); if (ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE) { // 在静音或震动模式下,媒体音量通常仍可调节(除非系统策略限制) } ``` ### 8. 使用媒体会话控制 如果应用正在播放媒体,建议使用媒体会话(MediaSession)来控制音量。通过媒体会话,系统会优先处理应用自身的音量控制请求。 ```java MediaSession mediaSession = new MediaSession(context, "MyApp"); mediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC); mediaSession.setActive(true); // 然后通过媒体会话的控制器调整音量 VolumeProvider volumeProvider = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_ABSOLUTE, maxVolume, currentVolume) { @Override public void onSetVolumeTo(int volume) { // 设置音量 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); } }; mediaSession.setPlaybackState(new PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, 0, 1.0f) .build()); mediaSession.setPlaybackToRemote(volumeProvider); ``` ### 验证步骤 1. 检查权限:确保在AndroidManifest.xml中有`<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />`。 2. 打印最大音量和当前音量: ```java int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int current = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); Log.d("Volume", "Max=" + max + ", Current=" + current); ``` 3. 在调用`setStreamVolume`后,再次获取当前音量并打印,看是否改变。 4. 检查日志:如果在设置后看到日志`level_changed STREAM_MUSIC <设置的值>`,说明系统已经接收到了设置请求,但实际没有变化,可能是系统策略导致(如上述MTK平台的限制)。 如果上述方法都无法解决,可能需要考虑设备特定的问题,并尝试在目标设备上抓取完整的系统日志进行分析。 ### 日志信息的含义再说明 日志"vol.Events: writeEvent level_changed STREAM_MUSIC 2"的含义是:音乐流的音量级别被设置为2。这个日志是由系统在音量改变时记录的事件,它表明音量设置请求已经被处理,并且系统记录了这个改变。但是,这并不一定意味着用户实际听到了音量变化。例如: - 如果耳机没有正确连接,系统可能记录了这个改变,但声音实际上是从扬声器发出的(而扬声器的音量可能是不同的值)。 - 系统策略可能阻止了音量的实际改变(比如在设备静音模式下不允许媒体声音),但事件日志仍然记录。 因此,需要确保音频路由(Audio Routing)正确,即音频确实通过你设置的流类型输出。 ### 生成相关问题 §§相关问题§§ 1. 如何获取Android设备的当前音量级别? 2. 如何监听Android系统的音量变化事件? 3. 为什么在Android设备上插入耳机后音量设置无效? 4. Android中不同音频流类型(如铃声、媒体、闹钟)的区别是什么? 5. 如何为应用单独控制音量而不影响系统音量? </think>### 解决`mAudioManager.setStreamVolume`无效问题及日志分析 #### 一、`vol.Events: level_changed STREAM_MUSIC 2` 含义解析 该日志表示**系统已记录音量变更事件**: - `vol.Events`:音量事件模块 - `level_changed`:音量级别发生变更 - `STREAM_MUSIC`:音乐音频流类型(Android音频流常量) - `2`:目标音量级别值(范围0~15) **关键结论**: 此日志证明您的音量设置指令 **已被系统接收**,问题出在音量变更 **执行阶段受阻**。 #### 二、`setStreamVolume()` 失效的解决方案 ##### 1. 权限问题(最常见) ```xml <!-- AndroidManifest.xml 必须声明 --> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> ``` - 在Android 6.0+设备上需动态申请: ```java if (ContextCompat.checkSelfPermission(this, Manifest.permission.MODIFY_AUDIO_SETTINGS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.MODIFY_AUDIO_SETTINGS}, REQUEST_CODE); } ``` ##### 2. 音量范围验证 ```java int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // 确保目标音量在[0, maxVolume]区间 if (targetVolume >= 0 && targetVolume <= maxVolume) { mAudioManager.setStreamVolume( AudioManager.STREAM_MUSIC, targetVolume, AudioManager.FLAG_SHOW_UI ); } ``` ##### 3. 音频焦点策略 ```java // 请求音频焦点(避免被系统策略拦截) AudioManager.OnAudioFocusChangeListener focusListener = ...; mAudioManager.requestAudioFocus( focusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT ); // 设置音量后释放焦点 mAudioManager.abandonAudioFocus(focusListener); ``` ##### 4. 设备特定兼容方案(针对MTK/特定厂商) ```java // 方案1:添加延迟(绕过系统音频服务同步机制) new Handler(Looper.getMainLooper()).postDelayed(() -> { mAudioManager.setStreamVolume(...); }, 100); // 100ms延迟 // 方案2:通过ADB命令验证硬件支持 // adb shell media volume --show --stream 3 --set 10 ``` ##### 5. 媒体会话控制(Android 5.0+最佳实践) ```java MediaSession mediaSession = new MediaSession(context, "AudioSession"); mediaSession.setCallback(new MediaSession.Callback() { @Override public void onSetVolumeTo(int volume) { mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); } }); mediaSession.setActive(true); ``` #### 三、调试诊断步骤 1. **检查当前音量状态**: ```bash adb shell dumpsys audio | grep "STREAM_MUSIC" -A 10 ``` 2. **实时监控音量事件**: ```bash adb logcat -b events | grep "volume_changed" ``` 3. **系统策略检测**: ```java // 检查是否存在静音策略限制 int ringerMode = mAudioManager.getRingerMode(); if (ringerMode == AudioManager.RINGER_MODE_SILENT) { // 静音模式下需特殊处理 } ``` 4. **耳机状态验证**: ```java // 检测耳机连接状态是否影响策略 AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); for (AudioDeviceInfo device : devices) { if (device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) { // 存在耳机连接时的特殊逻辑 } } ``` > 若问题仅出现在特定设备(如MTK平台),需检查系统层`AudioPolicyManager`策略限制[^1]。 #### 四、日志事件分析框架 当出现 `level_changed STREAM_MUSIC 2` 时: ```mermaid sequenceDiagram App->>AudioService: setStreamVolume(STREAM_MUSIC, 2) AudioService->>AudioSystem: 写入音量变更请求 AudioSystem->>Kernel: 下发音频控制指令 Note right of Kernel: 驱动程序执行失败时<br>仍会记录事件日志 AudioSystem-->>AudioService: 返回执行状态 AudioService-->>App: 回调onVolumeChanged() AudioService->>Logger: 记录vol.Events ``` #### 相关问题 1. 如何在Android系统音量变更时实时监听回调事件? 2. 不同品牌设备(如MTK/高通)音量控制策略有何差异? 3. 蓝牙设备连接时`setStreamVolume()`失效该如何解决? 4. Android 13的`setStreamVolume()`方法有何行为变更?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值