java.io.FileNotFoundException:/sdcard/testwrite.txt:打开失败:EACCES(权限被拒绝)

java.io.FileNotFoundException: /sdcard/testwrite.txt: open failed: EACCES (Permission denied)

请帮忙。

我正在开发 Android 应用程序。

问题来了。 我无法在 /sdcard 写入文件。 怎么了?

开发工具:Android studio 3.5.2

设备:AVD Pixel 2 API29

我编码了Permission Request。 我可以用旧的 android 设备写一个文件。 为什么我不能用这个设备写字?

请帮忙。

错误日志

E: Unknown bits set in runtime_flags: 0x8000
E: --> WRITE_EXTERNAL_STORAGE=true
E: --> READ_EXTERNAL_STORAGE=true
E: ====================================================================
E: --> TEST: Writing.. /sdcard/testwrite.txt
E: --> Writing..
E: --> Error..
E: java.io.FileNotFoundException: /sdcard/testwrite.txt: open failed: EACCES (Permission denied)

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapp.TestWrite">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

MainActivity.java

package com.myapp.TestWrite;

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

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        Log.wtf( "TAG", "--> WRITE_EXTERNAL_STORAGE=" + hasPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) );
        Log.wtf( "TAG", "--> READ_EXTERNAL_STORAGE=" + hasPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) );

        if( Build.VERSION.SDK_INT >= 23 )
        if( hasPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) )
        if( hasPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) ) {
            writeTest( "/sdcard/testwrite.txt" );
            return;
        }

        Log.wtf( "TAG", "--> Request permission\n" );
        ActivityCompat.requestPermissions(
                this,
                new String[]{
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.READ_EXTERNAL_STORAGE
                },
                1
        );
    }

    void writeTest( String fn ) {
        Log.wtf( "TAG", "====================================================================" );
        Log.wtf( "TAG", "--> TEST: Writing.. " + fn );
        File file = new File( fn );
        try {
            Log.wtf( "TAG", "--> Writing.." );;
            FileWriter fw = new FileWriter(file, false);
            fw.write( "Test Doc !!!!!" );
            fw.close();
            Log.wtf( "TAG", "--> Success.." );
        } catch( IOException e) {
            e.printStackTrace();
            Log.wtf( "TAG", "--> Error.." );
            Log.wtf( "TAG", e.toString() );
        }
    }

    public boolean hasPermission( String strPerm ) {
        if ( ContextCompat.checkSelfPermission( this, strPerm ) == PackageManager.PERMISSION_GRANTED )
            return true;
        return false;
    }

    @Override
    public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults ) {
        Log.wtf( "TAG", ":onRequestPermissionsResult\n" );
        switch ( requestCode ) {
            case 1:
            {
                // If request is cancelled, the result arrays are empty.
                if ( grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED ) {
                    Log.wtf( "TAG", "--> Permission granted.\n" );
                    finish();
                    startActivity( getIntent() );
                }
                else {
                    Log.wtf( "TAG", "--> Permission denied. Quitting.\n" );
                    finish();
                }
            }
        }
    }
}

您 运行 您的应用在 Android 10 中,但清单中没有包含旧版支持属性。按照以下建议编辑您的清单:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10 or higher. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

(source)

请注意,这只是一个临时修复,因为随着下一个 android 版本的发布,这将默认为 false。如果这些文件对您的应用程序是私有的,请考虑使用 getExternalFilesDir() 动态获取路径。在 developers' website.

中阅读有关范围存储更改的更多信息

您应该先尝试在设备中创建文件并检查 pwd 的路径。
它可能不会 /sdcard/testwrite.txt.

使用以下代码之一如何?

        /** internal: start with /data/user/0/{applicationId}/ */
        String cacheDirPath = mStorageManager.getCachedDir().getPath();

        String filesDirPath = mStorageManager.getFileDir().getAbsolutePath();

        String dirNamePath = mStorageManager.getDir("dirname").getPath();

        String codeCacheDirPath = mStorageManager.getCodeCacheDir().getPath();

        String noBackupFilesDir = mStorageManager.getNoBackupFilesDir().getPath();

        /** external start with /storage/emulated/0/Android/data/ */
        String externalCacheDirPath = mStorageManager.getExternalCacheDir().getPath();

        String externalFilesDirPath = mStorageManager.getExternalFilesDir().getPath();

        /** obb start with /storage/emulated/0/Android/obb/ */
        String obbDirPath = mStorageManager.getObbDir().getPath();