4. 核心概念:AirplaneModeManager、Settings.Global.AIRPLANE_MODE_ON、广播机制与Intent

好,咱们正式开始动手之前,得先把几个核心概念理清楚。说白了,自定义飞行模式不是简单地调用一个开关API就完事了。它涉及到系统服务的调用、全局配置的读写,还有组件之间的通信。这三个东西,你搞懂了,后面的代码写起来就是水到渠成的事。

4.1 AirplaneModeManager:系统服务的“遥控器”

先说说 AirplaneModeManager。这个类,你可以把它理解成系统飞行模式的“遥控器”。它封装了所有和飞行模式相关的操作。

我记得在Android 4.2之前,大家操作飞行模式都是直接改 Settings.System 里的值。那时候代码写得很随意,但兼容性很差。后来Google觉得这样不行,就封装了 AirplaneModeManager,把底层逻辑藏起来了。

这个类主要提供了两个核心方法:

  • isAirplaneModeEnabled():查询当前飞行模式是否开启。
  • setAirplaneModeEnabled(boolean):设置飞行模式的开关状态。

你可能会问:“就这么简单?” 嗯,表面上看确实简单。但实际项目中,我遇到过一个问题:直接调用 setAirplaneModeEnabled(true) 后,系统并不会立刻响应。它需要广播通知其他组件,这个过程是异步的。所以,如果你紧接着去查询状态,很可能还是旧的。

小提示: 调用 setAirplaneModeEnabled() 后,最好等几百毫秒再查询状态,或者直接监听广播来获取最终结果。

4.2 Settings.Global.AIRPLANE_MODE_ON:藏在背后的“开关”

接下来是 Settings.Global.AIRPLANE_MODE_ON。这个常量,说白了就是飞行模式在系统数据库里的“身份证”。

你想想看,AirplaneModeManager 最终是怎么知道飞行模式开没开的?它其实也是去读这个值。系统把飞行模式的状态存成了一个全局的键值对,键就是 AIRPLANE_MODE_ON,值就是 0 或 1。

我刚开始做这个功能时,犯过一个低级错误:直接通过 Settings.Global.putInt() 去写这个值。结果发现,虽然状态变了,但系统UI没有更新,Wi-Fi和蓝牙也没有自动关闭。为什么?因为系统没有收到“状态已改变”的通知。

警告: 不要直接修改 Settings.Global.AIRPLANE_MODE_ON!请始终使用 AirplaneModeManager 的 API。否则,你会绕过系统的状态机,导致各种奇怪的问题。

这个常量,我们更多是用来做状态读取的备份校验。比如,你通过 AirplaneModeManager 获取状态后,再读一下这个值做双重确认,确保万无一失。

4.3 广播机制:系统在“喊话”

好,现在到了最关键的部分——广播机制。为什么说它关键?因为飞行模式的变化,不是只影响你一个App。Wi-Fi模块、蓝牙模块、蜂窝网络模块,它们都需要知道这个变化。

系统是怎么通知它们的?靠广播。

AirplaneModeManager.setAirplaneModeEnabled() 被调用后,系统会发送一条有序广播,Action 是 Intent.ACTION_AIRPLANE_MODE_CHANGED。所有注册了这条广播的组件,都会收到通知。

我个人习惯在 BroadcastReceiveronReceive() 方法里,通过 intent.getBooleanExtra("state", false) 来获取最新的状态。注意,这个 extra 的名字就是 "state",别写错了。

// 注册广播的示例代码
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        boolean isEnabled = intent.getBooleanExtra("state", false);
        // 在这里更新你的UI或逻辑
    }
}, filter);

这里有个坑,我曾经踩过:如果你在 AndroidManifest.xml 里静态注册这个广播,从 Android 8.0(API 26)开始,系统会限制隐式广播的接收。说白了,静态注册可能收不到。所以,我建议你动态注册,在 onResume() 里注册,在 onPause() 里注销。

4.4 Intent:传递消息的“信使”

最后聊聊 Intent。在广播机制里,Intent 就是那个信使。它不光告诉你是谁发出的广播,还带上了具体的数据。

对于飞行模式广播,Intent 里携带的 extra 数据就是 "state"。但如果你是自己开发自定义飞行模式,你可能需要扩展这个机制。

举个例子,我在一个项目中需要实现“飞行模式 + 省电模式”联动。当用户开启自定义飞行模式时,我希望同时关闭蓝牙和降低屏幕亮度。这时候,我就不能只依赖系统广播了。我会自己定义一个 Intent,Action 是 com.example.CUSTOM_AIRPLANE_MODE_CHANGED,然后在 extra 里放一个 Bundle,包含蓝牙状态、亮度值等参数。

// 自定义广播的发送示例
Intent customIntent = new Intent("com.example.CUSTOM_AIRPLANE_MODE_CHANGED");
Bundle extras = new Bundle();
extras.putBoolean("bluetooth_off", true);
extras.putInt("brightness", 30);
customIntent.putExtras(extras);
sendBroadcast(customIntent);

这样,你的App内部各个模块就能协同工作了。记住,Intent 不只是用来启动Activity的,它更是组件间通信的桥梁。

4.5 知识体系总览

为了让你更直观地理解这几个概念的关系,我画了一张图。你可以看到,AirplaneModeManager 是操作入口,它读写 Settings.Global 数据库,然后通过广播机制通知各个模块。

自定义飞行模式核心概念关系图 AirplaneModeManager setAirplaneModeEnabled() Settings.Global AIRPLANE_MODE_ON (0/1) 读写 系统广播 ACTION_AIRPLANE_MODE_CHANGED 触发 Intent extra: "state" = true/false 携带 BroadcastReceiver (Wi-Fi/蓝牙/蜂窝) 分发

这张图你看懂了吗?AirplaneModeManager 是操作入口,它去读写 Settings.Global 数据库里的值。一旦值发生变化,系统就通过 Intent 广播出去,通知所有注册了 BroadcastReceiver 的模块。这就是整个流程。

嗯,到这里,核心概念就讲完了。你可能会觉得有点抽象,没关系,下一章我们就会把这些概念用到实际的代码里。到时候你回头再看这张图,会更有感觉。


专注资料整理