You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android NDK原生C++程序中接收系统广播?

核心原因

Android 触发关机/重启流程时,不会给所有进程按标准Linux逻辑发送可被sigaction捕获的SIGTERM:系统会优先分发关机、重启类广播,随后在短时间内通过lmk、进程强杀逻辑清理所有运行中进程,多数场景下进程根本等不到SIGTERM就被SIGKILL直接终止,这也是你之前写的信号回调不触发的根本原因。
纯Native C++环境没有Java层的Context类,无法直接调用SDK暴露的registerReceiver接口,但有两种稳定的实现路径。


实现方案

方案1:通过Native Binder接口直接注册系统广播(无Java依赖)

Android系统服务的Binder接口对native层完全开放,你可以直接调用ActivityManager的广播注册接口,不需要写任何Java代码:

  1. 编译配置添加依赖:Android.mk/Android.bp中链接libbinder、libutils两个库
  2. 实现广播接收的Binder回调:
#include <binder/BBinder.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <utils/String16.h>
#include <atomic>

// 全局标记,避免重复触发退出逻辑
std::atomic<bool> is_shutdown_triggered{false};
void do_graceful_shutdown() {
    if (is_shutdown_triggered.exchange(true)) return;
    // 这里写你的优雅退出逻辑:刷磁盘缓存、关闭文件句柄、上报埋点等
}

class ShutdownReceiver : public BBinder {
public:
    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
        // onReceive回调对应的transact code固定为1
        if (code == 1) {
            do_graceful_shutdown();
            return NO_ERROR;
        }
        return BBinder::onTransact(code, data, reply, flags);
    }
};
  1. 程序启动时注册广播:
void register_shutdown_broadcast() {
    // 必须启动binder线程池,否则收不到系统回调
    ProcessState::self()->startThreadPool();
    sp<IBinder> am = defaultServiceManager()->getService(android::String16("activity"));
    if (!am) return;

    Parcel data, reply;
    data.writeInterfaceToken(android::String16("android.app.IActivityManager"));
    data.writeStrongBinder(sp<BBinder>::make<ShutdownReceiver>());
    // 写入要监听的广播Action
    data.writeString16(android::String16("android.intent.action.ACTION_SHUTDOWN"));
    data.writeString16(android::String16("android.intent.action.REBOOT"));
    // 标记为前台广播接收器,提高回调优先级
    data.writeInt32(0x10000000); // FLAG_RECEIVER_FOREGROUND
    // 调用registerReceiver接口,Android 10-14版本对应transact code为23,低版本需要对照对应版本的IActivityManager.aidl调整
    am->transact(23, data, &reply, 0);
}

注意:该方案用到的是Android隐藏Binder接口,不同大版本的transact序号可能有变动,适合内置到系统镜像的原生程序使用。

方案2:系统属性轮询兜底(全版本兼容,零适配成本)

如果不想适配版本差异,可以直接用系统原生进程通用的检测逻辑:系统触发关机/重启时会第一时间设置sys.powerctl系统属性,开一个低优先级线程轮询该属性即可,从Android 5到Android 14全版本通用,surfaceflinger、logd等系统核心进程都用这个逻辑做关机前处理:

#include <sys/system_properties.h>
#include <pthread.h>
#include <cstring>

void* powerctl_watcher(void*) {
    char last_val[PROP_VALUE_MAX] = {0};
    while (!is_shutdown_triggered.load()) {
        char cur_val[PROP_VALUE_MAX] = {0};
        __system_property_get("sys.powerctl", cur_val);
        if (cur_val[0] != '\0' && strcmp(cur_val, last_val) != 0) {
            if (strstr(cur_val, "shutdown") || strstr(cur_val, "reboot")) {
                do_graceful_shutdown();
                break;
            }
            strncpy(last_val, cur_val, PROP_VALUE_MAX - 1);
        }
        usleep(200 * 1000); // 200ms轮询一次,几乎无功耗影响
    }
    return nullptr;
}

void start_shutdown_watcher() {
    pthread_t watcher_thread;
    pthread_create(&watcher_thread, nullptr, powerctl_watcher, nullptr);
    pthread_detach(watcher_thread);
}

注意事项
  • 优雅退出逻辑总耗时不要超过3s,系统触发关机流程后5s左右就会向所有进程发送SIGKILL,过长的逻辑会被直接打断。
  • 如果你的native程序是普通三方应用通过JNI拉起的,最稳妥的方案还是在Java层注册BroadcastReceiver,收到关机广播后通过JNI回调native的退出逻辑,不需要适配隐藏接口。
  • 不要把SIGTERM作为唯一的兜底触发条件,多数厂商定制ROM在关机流程中会跳过SIGTERM环节直接强杀进程。

内容的提问来源于stack exchange,提问作者Bigcat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 23:57:18