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

Android应用写入文件时崩溃问题排查求助

问题分析与解决方案

从你提供的Logcat日志可以直接定位到崩溃根源:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File android.content.Context.getExternalFilesDir(java.lang.String)' on a null object reference

简单来说,你的WriteToFile类继承了AppCompatActivity,但你应该是直接通过new WriteToFile()来实例化它的——这种方式不会触发Activity的初始化流程,导致内部的Context对象为null,调用getExternalFilesDir时自然抛出空指针异常。

核心修复步骤

1. 重构WriteToFile为普通工具类

它只是负责文件写入,完全不需要继承Activity。我们将它改成依赖外部传入Context的工具类:

public class WriteToFile {
    private final Context mContext;

    // 通过构造函数传入有效的Context
    public WriteToFile(Context context) {
        this.mContext = context;
    }

    public void write(String fileName, String textToWrite) {
        // 检查外部存储状态
        String state = Environment.getExternalStorageState();
        if (!Environment.MEDIA_MOUNTED.equals(state)) {
            Toast.makeText(mContext, "外部存储不可用,无法写入文件", Toast.LENGTH_SHORT).show();
            return;
        }

        // 文件写入不需要互联网权限,修正为检查存储权限
        if (!PermissionChecker.checkStoragePermission(mContext)) {
            PermissionChecker.requestFilePermission((Activity) mContext, mContext);
            return;
        }

        // 获取外部文件目录,先判空
        File dir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
        if (dir == null) {
            Toast.makeText(mContext, "无法获取文件存储目录", Toast.LENGTH_SHORT).show();
            return;
        }

        // 用mkdirs创建多级目录(mkdir只能创建一级)
        if (!dir.exists()) {
            dir.mkdirs();
        }

        File file = new File(dir, fileName);
        // 使用try-with-resources自动管理流资源,避免泄漏
        try (FileOutputStream os = new FileOutputStream(file)) {
            os.write(textToWrite.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(mContext, "文件写入失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
}

2. 修正调用方式

在你的validateCredentials方法中,实例化WriteToFile时传入当前Activity的Context:

private void validateCredentials(){
    if (PermissionChecker.checkInternetPermission(this)) {
        if (!EmptyChecker.isEmpty(usernameTextInputLayout) && !EmptyChecker.isEmpty(passwordTextInputLayout)) {
            password = Objects.requireNonNull(passwordTextInputLayout.getEditText()).getText().toString();
            userName = Objects.requireNonNull(usernameTextInputLayout.getEditText()).getText().toString();
            logInValidator(userName, (success) -> {
                if (success) {
                    String textToWrite = getTextToWrite();
                    // 传入当前Activity的Context实例化工具类
                    WriteToFile writeToFile = new WriteToFile(LogInActivity.this);
                    writeToFile.write("UserData.txt",textToWrite);
                    //Intent mainIntent = new Intent(LogInActivity.this, MainActivity.class);
                    //startActivity(mainIntent);
                } else {
                    showResult.setText(getString(userNotFound));
                }
            });
        } else {
            showResult.setText(getString(string.emptyField));
        }
    } else PermissionChecker.requestInternetPermission(TestActivity.this,ContextGetter.getAppContext());
}

额外注意事项

  • 权限逻辑修正:原代码中错误地在文件写入前检查互联网权限,文件操作需要的是存储权限(Android 10及以上版本无需申请,系统自动授予),请确保PermissionChecker中的checkStoragePermission和requestFilePermission逻辑正确。
  • 空指针防护:对getExternalFilesDir的返回值一定要判空,部分特殊设备或存储异常场景下会返回null。
  • 资源自动管理:使用try-with-resources语法可以自动关闭文件流,避免手动关闭时可能出现的异常或泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:49:11