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

NDK Native-Activity示例启动崩溃求助及Vulkan触摸事件处理咨询

问题解决:Native-Activity启动崩溃与Vulkan C++触摸事件处理

一、解决Native-Activity示例启动崩溃问题

崩溃原因分析

抛出的ClassNotFoundException显示找不到androidx.startup.InitializationProvider,说明项目缺少AndroidX App Startup组件依赖,或构建配置中该组件被错误排除。

解决方案

  1. 添加App Startup依赖
    在模块级build.gradle(或build.gradle.kts)的dependencies块中添加:

    implementation "androidx.startup:startup-runtime:1.1.1"
    

    添加完成后点击Sync Now同步项目,确保依赖正确引入。

  2. 禁用App Startup(无需该组件时)
    若你的Native Activity应用不需要App Startup功能,可在AndroidManifest.xml中移除对应Provider声明:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.native_activity">
    
        <application>
            <provider
                android:name="androidx.startup.InitializationProvider"
                android:authorities="${applicationId}.androidx-startup"
                tools:node="remove" />
        </application>
    </manifest>
    

    注意需在manifest根标签中添加tools命名空间。

  3. 清理构建缓存
    点击Android Studio菜单栏File -> Invalidate Caches...,勾选Invalidate and Restart,清理旧构建缓存并重启IDE,避免缓存导致的依赖加载问题。

  4. 检查NDK版本兼容性
    确保使用的NDK版本与示例兼容,建议使用r25及以上稳定版本。可通过File -> Project Structure -> SDK Location设置NDK路径和版本。


二、Vulkan应用C++侧触摸事件处理方法

Native Activity通过android_app结构体提供事件回调机制,触摸事件属于AINPUT_EVENT_TYPE_MOTION类型,处理步骤如下:

1. 设置事件回调

在android_main函数中,为android_app绑定输入事件处理函数:

#include <android/native_activity.h>
#include <android/input.h>
#include <android/log.h>

// 存储触摸状态,可根据需求扩展
struct TouchState {
    float x = 0.0f;
    float y = 0.0f;
    bool is_touched = false;
} g_touch_state;

static int handle_input_event(struct android_app* app, AInputEvent* event);
static void handle_app_cmd(struct android_app* app, int32_t cmd);

void android_main(struct android_app* app) {
    // 初始化Vulkan、窗口等资源...

    // 绑定事件回调
    app->onInputEvent = handle_input_event;
    app->onAppCmd = handle_app_cmd;

    // 主循环
    int events;
    struct android_poll_source* source;
    while (!app->destroyRequested) {
        while (ALooper_pollAll(0, nullptr, &events, (void**)&source) >= 0) {
            if (source != nullptr) {
                source->process(app, source);
            }
        }

        // 执行Vulkan渲染逻辑,可使用g_touch_state中的触摸数据更新场景
        // render_frame();
    }

    // 清理Vulkan等资源...
}

2. 实现触摸事件处理函数

在handle_input_event中解析触摸动作类型、坐标等信息:

static int handle_input_event(struct android_app* app, AInputEvent* event) {
    if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION) {
        return 0; // 非触摸事件交给系统处理
    }

    int action = AMotionEvent_getAction(event);
    int action_type = action & AMOTION_EVENT_ACTION_MASK;
    int pointer_idx = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
    int pointer_id = AMotionEvent_getPointerId(event, pointer_idx);

    // 获取Android坐标系下的触摸坐标(左上角为原点)
    float x = AMotionEvent_getX(event, pointer_idx);
    float y = AMotionEvent_getY(event, pointer_idx);

    switch (action_type) {
        case AMOTION_EVENT_ACTION_DOWN:
        case AMOTION_EVENT_ACTION_POINTER_DOWN:
            g_touch_state.is_touched = true;
            g_touch_state.x = x;
            g_touch_state.y = y;
            __android_log_print(ANDROID_LOG_INFO, "Touch", "Pointer %d down at (%.2f, %.2f)", pointer_id, x, y);
            break;
        case AMOTION_EVENT_ACTION_MOVE:
            // 遍历所有触摸点处理多点触摸
            int pointer_count = AMotionEvent_getPointerCount(event);
            for (int i = 0; i < pointer_count; ++i) {
                int pid = AMotionEvent_getPointerId(event, i);
                float px = AMotionEvent_getX(event, i);
                float py = AMotionEvent_getY(event, i);
                __android_log_print(ANDROID_LOG_INFO, "Touch", "Pointer %d moved to (%.2f, %.2f)", pid, px, py);
                if (pid == pointer_id) {
                    g_touch_state.x = px;
                    g_touch_state.y = py;
                }
            }
            break;
        case AMOTION_EVENT_ACTION_UP:
        case AMOTION_EVENT_ACTION_POINTER_UP:
        case AMOTION_EVENT_ACTION_CANCEL:
            g_touch_state.is_touched = false;
            __android_log_print(ANDROID_LOG_INFO, "Touch", "Pointer %d up", pointer_id);
            break;
    }

    return 1; // 表示已处理该事件
}

static void handle_app_cmd(struct android_app* app, int32_t cmd) {
    // 处理应用生命周期事件
    switch (cmd) {
        case APP_CMD_DESTROY:
            app->destroyRequested = true;
            break;
        // 其他生命周期命令处理...
    }
}

3. 转换触摸坐标到Vulkan坐标系

Android触摸坐标系以屏幕左上角为原点,Vulkan默认以窗口左下角为原点,需进行转换:

// 假设window_width和window_height是Vulkan交换链的宽高
float vulkan_x = x;
float vulkan_y = app->window->height - y;

// 转换到NDC(归一化设备坐标,范围[-1,1])
float ndc_x = (vulkan_x / app->window->width) * 2.0f - 1.0f;
float ndc_y = (vulkan_y / app->window->height) * 2.0f - 1.0f;

转换后的坐标可用于更新Vulkan场景交互逻辑,比如旋转3D模型、移动UI元素等。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 17:35:53