Android NDK C++中JNI文件读写崩溃问题求助
Android NDK C++ 文件读写崩溃与JNI调用问题排查
一、权限配置核心检查
- 静态权限声明:确认
AndroidManifest.xml中已添加对应权限,针对不同Android版本做适配:
针对Android 10,需在<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" />application标签中添加:android:requestLegacyExternalStorage="true" - 动态权限申请: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调用逻辑错误排查
- 方法签名必须完全匹配:
若Java侧tata方法为public void tata(String content),C++中获取方法ID时的签名需写为(Ljava/lang/String;)V,签名错误会直接导致NoSuchMethodError崩溃。 - JNI环境与对象引用管理:
- 子线程调用JNI时,不能复用主线程的
JNIEnv,需通过AttachCurrentThread获取新环境,调用完成后DetachCurrentThread。 - 避免使用局部引用持有
MainActivity实例,改用全局引用(NewGlobalRef)防止对象被GC回收。
- 子线程调用JNI时,不能复用主线程的
- 强制异常检查:每次JNI调用后必须检查异常,避免崩溃扩散:
jthrowable exc = env->ExceptionOccurred(); if (exc) { env->ExceptionClear(); // 此处添加错误处理逻辑 return; }
三、文件读写代码问题修正
- 避免硬编码路径:C中不要直接使用绝对路径,通过Java获取合法存储路径(如
getExternalFilesDir(null))后传递给C,确保路径有权限访问。 - C++侧权限预检查:文件操作前用
access函数验证权限:const char* filePath = env->GetStringUTFChars(jPath, nullptr); if (access(filePath, W_OK) != 0) { // 无写入权限,直接返回 env->ReleaseStringUTFChars(jPath, filePath); return; } - 字符串转换内存管理:
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
相关产品推荐
相关产品推荐

