如何在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
相关产品推荐
相关产品推荐

