Android 跨应用广播通信全攻略

在 Android 开发中,广播(Broadcast) 是一种非常常用的组件间通信机制,既可以在应用内部解耦模块,也可以在多个应用之间传递消息。

但是随着 Android 版本的演进,特别是 8.0+(API 26) 之后,广播的使用方式有了很多限制和坑。

今天我们结合一个实际场景,来系统梳理一下 跨应用广播 的正确用法。


1. 广播的分类

Android 中的广播按发送/接收方式主要有两类:

分类 注册方式 特点
静态注册 Manifest 配置 进程未启动时也能接收(部分系统广播受限),App 会被系统唤醒
动态注册 代码中注册 只能在进程运行时接收,生命周期跟随注册者,灵活但需手动管理

2. Android 8.0+ 的限制

Android 8.0(API 26) 开始,大部分隐式广播(未指定包名/组件的广播)禁止静态注册。

比如:

java 复制代码
sendBroadcast(new Intent("custom.start.schoolfinance.MODE_CHANGE"));

在 8.0+ 中,Manifest 静态注册的自定义接收器将收不到此广播。

但以下情况不受限制:

  1. 显式广播(指定包名或组件名)
  2. 系统允许的部分广播(如 BOOT_COMPLETEDPACKAGE_ADDED 等)
  3. 应用内部广播(LocalBroadcast)

3. 显式广播的正确用法

显式广播就是明确指定接收方,例如指定包名:

java 复制代码
Intent intent = new Intent("custom.start.schoolfinance.MODE_CHANGE");
intent.setPackage("com.appB.package"); // 只发给 B 应用
sendBroadcast(intent);

优点:

  • 不受 Android 8.0+ 静态注册限制
  • 安全性高,不会被第三方应用接收
  • 投递效率高,只发给目标应用

缺点:

  • 只能发给指定的应用,广播范围受限

4. Application 中动态注册

如果只要求在应用运行时接收广播,可以在 Application 中动态注册:

java 复制代码
public class MyApplication extends Application {
    private final BroadcastReceiver modeChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int mode = intent.getIntExtra("mode", -1);
            Log.d("桌面APP", "收到模式变更广播: mode=" + mode);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        registerReceiver(modeChangeReceiver, new IntentFilter("custom.start.schoolfinance.MODE_CHANGE"));
    }
}

优点

  • 不受 Android 8.0 隐式广播限制
  • 灵活,可在运行时按需注册

缺点

  • 应用进程未启动时无法接收

5. 跨应用发送广播示例(A → B)

A 应用发送:

java 复制代码
Intent intent = new Intent("custom.start.schoolfinance.MODE_CHANGE");
intent.putExtra("mode", 1);
intent.setPackage("com.appB.package"); // 显式指定
sendBroadcast(intent);

B 应用接收(Application 中动态注册):

java 复制代码
@Override
public void onCreate() {
    super.onCreate();
    registerReceiver(modeChangeReceiver, new IntentFilter("custom.start.schoolfinance.MODE_CHANGE"));
}

📌 适用:B 已启动或后台常驻时实时接收


6. 让未启动的应用也能接收

如果希望 B 即使没启动也能收到广播,必须:

  • Manifest 中静态注册接收器
  • 广播必须是显式广播

B 应用:

xml 复制代码
<receiver android:name=".ModeChangeReceiver" android:exported="true">
    <intent-filter>
        <action android:name="custom.start.schoolfinance.MODE_CHANGE"/>
    </intent-filter>
</receiver>

7. 系统广播中转给其他应用

有时 A 需要在收到系统广播(如 BOOT_COMPLETED)后,把信息转发给 B:

java 复制代码
public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            Intent sendToB = new Intent("custom.start.schoolfinance.MODE_CHANGE");
            sendToB.putExtra("mode", 1);
            sendToB.setPackage("com.appB.package");
            context.sendBroadcast(sendToB);
        }
    }
}

8. 选择方案的参考表

需求 推荐方案
进程未启动也能接收 静态注册 + 显式广播
应用已运行,实时接收 Application 中动态注册
只发给一个应用 显式广播(setPackage/setComponent)
多个应用都要接收 隐式广播(注意 8.0+ 限制)

9.为什么 setIntent() 必须调用

singleTask + FLAG_ACTIVITY_CLEAR_TOP 场景下的 Intent 参数传递流程图 ,为什么 setIntent() 必须调用?

css 复制代码
┌─────────────────────────┐
│ 启动 Activity A (首次)   │
│ onCreate(Intent old)     │
│ mIntent = oldIntent      │
└────────────┬────────────┘
             │
             ▼
        用户停留在 A
             │
             ▼
┌─────────────────────────┐
│ 再次启动 A               │
│  Intent: machineMode=1  │
│  FLAG_ACTIVITY_CLEAR_TOP │
└────────────┬────────────┘
             │
  A 已经存在 → 不走 onCreate()
             │
             ▼
┌─────────────────────────────┐
│ 调用 onNewIntent(newIntent) │
│  (machineMode=1)            │
└─────────────────────────────┘
             │
             │ (系统并不会更新 mIntent)
             ▼
  mIntent 还是旧的 → getIntent() 读不到新参数 ❌
             │
             ▼
   调用 setIntent(newIntent) ✔
             │
             ▼
  mIntent 更新 → getIntent() 拿到最新参数

总结

  • 不调用 setIntent()getIntent() 永远是第一次启动时的旧数据。
  • 调用 setIntent()getIntent() 会返回最新一次启动传进来的参数。

所以

java 复制代码
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent); // 必须,保证 getIntent() 是最新
    handleIntent(intent);
}

总结

  • Android 8.0+ 对静态注册隐式广播限制很大,跨应用通信优先用 显式广播
  • 动态注册灵活,但依赖进程常驻
  • 静态注册 + 显式广播是唤醒未启动应用的唯一通用方式
  • 系统广播可以作为触发器,把消息中转给目标应用
相关推荐
参宿四南河三1 天前
Android Compose SideEffect(副作用)实例加倍详解
android·app
火柴就是我1 天前
mmkv的 mmap 的理解
android
没有了遇见1 天前
Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比
android
shenshizhong1 天前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强1 天前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸1 天前
【底层机制】 Android ION内存分配器深度解析
android·面试
你听得到111 天前
肝了半个月,我用 Flutter 写了个功能强大的图片编辑器,告别image_cropper
android·前端·flutter
KevinWang_1 天前
Android 原生 app 和 WebView 如何交互?
android
用户69371750013841 天前
Android Studio中Gradle、AGP、Java 版本关系:不再被构建折磨!
android·android studio
杨筱毅1 天前
【底层机制】Android低内存管理机制深度解析
android·底层机制