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

Android NDK C++中JNI文件读写崩溃问题求助

Android NDK C++ 文件读写崩溃与JNI调用问题排查

一、权限配置核心检查

  1. 静态权限声明:确认AndroidManifest.xml中已添加对应权限,针对不同Android版本做适配:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- Android 13+ 需额外添加媒体权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    
    针对Android 10,需在application标签中添加:
    android:requestLegacyExternalStorage="true"
    
  2. 动态权限申请:Android 6.0+仅靠静态声明无效,必须在MainActivity中主动请求权限:
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
    }
    

二、JNI调用逻辑错误排查

  1. 方法签名必须完全匹配:
    若Java侧tata方法为public void tata(String content),C++中获取方法ID时的签名需写为(Ljava/lang/String;)V,签名错误会直接导致NoSuchMethodError崩溃。
  2. JNI环境与对象引用管理:
    • 子线程调用JNI时,不能复用主线程的JNIEnv,需通过AttachCurrentThread获取新环境,调用完成后DetachCurrentThread。
    • 避免使用局部引用持有MainActivity实例,改用全局引用(NewGlobalRef)防止对象被GC回收。
  3. 强制异常检查:每次JNI调用后必须检查异常,避免崩溃扩散:
    jthrowable exc = env->ExceptionOccurred();
    if (exc) {
        env->ExceptionClear();
        // 此处添加错误处理逻辑
        return;
    }
    

三、文件读写代码问题修正

  1. 避免硬编码路径:C中不要直接使用绝对路径,通过Java获取合法存储路径(如getExternalFilesDir(null))后传递给C,确保路径有权限访问。
  2. C++侧权限预检查:文件操作前用access函数验证权限:
    const char* filePath = env->GetStringUTFChars(jPath, nullptr);
    if (access(filePath, W_OK) != 0) {
        // 无写入权限,直接返回
        env->ReleaseStringUTFChars(jPath, filePath);
        return;
    }
    
  3. 字符串转换内存管理:GetStringUTFChars获取的char*必须调用ReleaseStringUTFChars释放,避免内存泄漏或野指针。

四、崩溃日志关键分析

一定要抓取logcat的崩溃栈轨迹,重点关注:

  • 是否存在NullPointerException(JNI引用为空)
  • 是否存在SecurityException(权限未授予)
  • 是否存在JNI DETECTED ERROR IN APPLICATION(JNI调用逻辑违规)

修正后代码参考

Java侧(MainActivity.java)

package com.psdk;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    private static MainActivity instance;
    private static final int PERMISSION_REQUEST_CODE = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        instance = this;
        requestStoragePermission();
    }

    private void requestStoragePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "权限被拒绝,无法写入文件", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 供JNI调用的写文件方法
    public void tata(String content) {
        try {
            File file = new File(getExternalFilesDir(null), "test.txt");
            FileWriter writer = new FileWriter(file);
            writer.write(content);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 提供全局实例给JNI
    public static MainActivity getInstance() {
        return instance;
    }
}

C++侧

#include <jni.h>
#include <string>
#include <unistd.h>

extern "C" JNIEXPORT void JNICALL
Java_com_psdk_MainActivity_nativeCallTata(JNIEnv* env, jobject thiz, jstring content) {
    // 获取MainActivity类与实例
    jclass activityCls = env->FindClass("com/psdk/MainActivity");
    if (activityCls == nullptr) return;

    jmethodID getInstanceMethod = env->GetStaticMethodID(activityCls, "getInstance", "()Lcom/psdk/MainActivity;");
    jobject activityInstance = env->CallStaticObjectMethod(activityCls, getInstanceMethod);
    if (activityInstance == nullptr) {
        env->DeleteLocalRef(activityCls);
        return;
    }

    // 获取tata方法ID
    jmethodID tataMethod = env->GetMethodID(activityCls, "tata", "(Ljava/lang/String;)V");
    if (tataMethod == nullptr) {
        env->DeleteLocalRef(activityInstance);
        env->DeleteLocalRef(activityCls);
        return;
    }

    // 调用方法并检查异常
    env->CallVoidMethod(activityInstance, tataMethod, content);
    jthrowable exc = env->ExceptionOccurred();
    if (exc) {
        env->ExceptionClear();
        // 可添加错误提示逻辑
    }

    // 释放局部引用
    env->DeleteLocalRef(activityInstance);
    env->DeleteLocalRef(activityCls);
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.psdk">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.PSDK">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:10:27