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

如何在Android NDK中使用C++创建弹出窗口?

Android NDK C++ 实现弹出窗口方案

Android NDK本身没有提供直接创建弹出窗口的专用API,窗口管理核心逻辑属于Android Framework层(Java),但可以通过JNI封装Java层调用结合NDK的ANativeWindow系列API,实现纯C++层面的弹窗控制,无需单独编写Java代码。

核心实现方案(悬浮窗示例)

1. 配置权限

在AndroidManifest.xml中添加悬浮窗权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Android 6.0+需动态申请该权限,Android 8.0+悬浮窗类型需使用TYPE_APPLICATION_OVERLAY。

2. C++代码实现

以下代码通过JNI调用WindowManager创建悬浮窗,并利用ANativeWindow绘制内容:

#include <android/native_activity.h>
#include <android/native_window.h>
#include <android/log.h>
#include <jni.h>

#define LOG_TAG "NDK_POPUP"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

static ANativeActivity* g_activity = nullptr;
static ANativeWindow* g_popup_window = nullptr;
static jobject g_surface_view = nullptr; // 全局保存SurfaceView对象,用于销毁

// 初始化悬浮弹窗
void init_popup() {
    if (!g_activity) {
        LOGD("NativeActivity instance not available");
        return;
    }

    JNIEnv* env = nullptr;
    if (g_activity->vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
        LOGD("Failed to attach thread to JVM");
        return;
    }

    // 获取Activity实例
    jobject activity_obj = g_activity->clazz;
    jclass activity_cls = env->GetObjectClass(activity_obj);

    // 获取WindowManager
    jmethodID get_service_mid = env->GetMethodID(activity_cls, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
    jstring window_service_str = env->NewStringUTF("window");
    jobject window_manager = env->CallObjectMethod(activity_obj, get_service_mid, window_service_str);
    env->DeleteLocalRef(window_service_str);

    // 创建WindowManager.LayoutParams
    jclass layout_params_cls = env->FindClass("android/view/WindowManager$LayoutParams");
    jfieldID type_field = env->GetStaticFieldID(layout_params_cls, "TYPE_APPLICATION_OVERLAY", "I");
    jint window_type = env->GetStaticIntField(layout_params_cls, type_field);

    jfieldID flag_field = env->GetStaticFieldID(layout_params_cls, "FLAG_NOT_FOCUSABLE", "I");
    jint window_flags = env->GetStaticIntField(layout_params_cls, flag_field);

    jfieldID format_field = env->GetStaticFieldID(layout_params_cls, "FORMAT_TRANSLUCENT", "I");
    jint window_format = env->GetStaticIntField(layout_params_cls, format_field);

    jmethodID lp_ctor = env->GetMethodID(layout_params_cls, "<init>", "(III)V");
    jobject layout_params = env->NewObject(layout_params_cls, lp_ctor, window_type, window_flags, window_format);

    // 设置窗口宽高与位置
    env->SetIntField(layout_params, env->GetFieldID(layout_params_cls, "width", "I"), 400);
    env->SetIntField(layout_params, env->GetFieldID(layout_params_cls, "height", "I"), 200);

    jclass gravity_cls = env->FindClass("android/view/Gravity");
    jfieldID center_gravity = env->GetStaticFieldID(gravity_cls, "CENTER", "I");
    env->SetIntField(layout_params, env->GetFieldID(layout_params_cls, "gravity", "I"), env->GetStaticIntField(gravity_cls, center_gravity));
    env->DeleteLocalRef(gravity_cls);

    // 创建SurfaceView作为弹窗载体
    jclass surface_view_cls = env->FindClass("android/view/SurfaceView");
    jmethodID sv_ctor = env->GetMethodID(surface_view_cls, "<init>", "(Landroid/content/Context;)V");
    g_surface_view = env->NewGlobalRef(env->NewObject(surface_view_cls, sv_ctor, activity_obj));

    // 将Surface转换为ANativeWindow
    jmethodID get_surface_mid = env->GetMethodID(surface_view_cls, "getSurface", "()Landroid/view/Surface;");
    jobject surface = env->CallObjectMethod(g_surface_view, get_surface_mid);
    g_popup_window = ANativeWindow_fromSurface(env, surface);
    env->DeleteLocalRef(surface);

    // 添加View到WindowManager
    jclass wm_cls = env->FindClass("android/view/WindowManager");
    jmethodID add_view_mid = env->GetMethodID(wm_cls, "addView", "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V");
    env->CallVoidMethod(window_manager, add_view_mid, g_surface_view, layout_params);

    // 绘制红色背景示例
    ANativeWindow_Buffer buffer;
    if (ANativeWindow_lock(g_popup_window, &buffer, nullptr) == 0) {
        uint32_t* pixels = static_cast<uint32_t*>(buffer.bits);
        for (int y = 0; y < buffer.height; y++) {
            for (int x = 0; x < buffer.width; x++) {
                pixels[y * buffer.stride + x] = 0xFFFF0000; // RGBA红色
            }
        }
        ANativeWindow_unlockAndPost(g_popup_window);
    }

    // 释放局部引用
    env->DeleteLocalRef(layout_params);
    env->DeleteLocalRef(window_manager);
    env->DeleteLocalRef(layout_params_cls);
    env->DeleteLocalRef(surface_view_cls);
    env->DeleteLocalRef(activity_cls);

    g_activity->vm->DetachCurrentThread();
}

// 销毁弹窗
void destroy_popup() {
    if (!g_activity || !g_popup_window || !g_surface_view) return;

    JNIEnv* env = nullptr;
    if (g_activity->vm->AttachCurrentThread(&env, nullptr) != JNI_OK) return;

    // 从WindowManager移除View
    jobject activity_obj = g_activity->clazz;
    jmethodID get_service_mid = env->GetMethodID(env->GetObjectClass(activity_obj), "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
    jstring window_service_str = env->NewStringUTF("window");
    jobject window_manager = env->CallObjectMethod(activity_obj, get_service_mid, window_service_str);
    env->DeleteLocalRef(window_service_str);

    jclass wm_cls = env->FindClass("android/view/WindowManager");
    jmethodID remove_view_mid = env->GetMethodID(wm_cls, "removeView", "(Landroid/view/View;)V");
    env->CallVoidMethod(window_manager, remove_view_mid, g_surface_view);

    // 释放资源
    ANativeWindow_release(g_popup_window);
    env->DeleteGlobalRef(g_surface_view);

    g_popup_window = nullptr;
    g_surface_view = nullptr;

    env->DeleteLocalRef(window_manager);
    env->DeleteLocalRef(wm_cls);

    g_activity->vm->DetachCurrentThread();
}

// NativeActivity生命周期回调
void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) {
    g_activity = activity;
    init_popup();
}

void ANativeActivity_onDestroy(ANativeActivity* activity) {
    destroy_popup();
}

关键NDK API说明

  • ANativeWindow:用于操作Android窗口Surface,核心函数:
    • ANativeWindow_fromSurface:将Java层Surface转换为Native层窗口对象
    • ANativeWindow_lock/ANativeWindow_unlockAndPost:锁定Surface缓冲区进行绘制,完成后提交更新
    • ANativeWindow_release:释放窗口资源
  • JNIEnv:Native与Java层交互的核心接口,用于调用Java类、方法和属性
  • ANativeActivity:提供Native程序的上下文,通过它可获取JVM实例和Activity的Java对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 21:15:13