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

Android外部存储权限异常求助:重装后无法写入文件

Android 外部存储权限问题:卸载重装后文件保存失败

已在Manifest中添加存储权限,首次安装时应用可正常请求权限并保存Assets文件夹中的文件,但卸载重装(代码无改动)后,文件保存功能失效,调试时触发权限拒绝错误。需要实现运行时权限申请解决该问题。

现有Manifest配置

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

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />
    <uses-permission android:name="android.permission.MANAGE_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/Theme.HCGHelp">
        <activity android:name=".NP_all"></activity>
        
        <activity android:name=".GKL18_pre" />
        <activity android:name=".GKL17_in" />
        <activity android:name=".GKL17_pre" />
        <activity android:name=".GKL16_in" />
        <activity android:name=".GKL14_in" />
        <activity android:name=".GKL10_in" />
        <activity android:name=".GKL16_pre" />
        <activity android:name=".GKL14_pre" />
        <activity android:name=".GKL10_pre" />
        <activity android:name=".GKL07_in" />
        <activity android:name=".GKL07_pre" />
        <activity android:name=".GKL02_in" />
        <activity android:name=".GKL02_pre" />
        <activity android:name=".GKL01_pre" />
        <activity android:name=".GKL01_in" />
        <activity android:name=".GKL_all" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

现有Activity代码

public class GKL10_pre extends AppCompatActivity {


    private Button button1;
    private Button button; 


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



        button1 = (Button) findViewById(R.id.B_GKL10_KEIMENO);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { opengkl10_pk();
            }
        });


        button = (Button) findViewById(R.id.B_GKL10_FILES);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                copyAsset("file1.pdf");
                copyAsset("file2.pdf");
                copyAsset("file3.pdf");
                copyAsset("file4.pdf");

            }
        });
    }

    private void copyAsset(String filename){
        String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download";
        File dir = new File(dirPath);
        if(!dir.exists()) {
            dir.mkdirs();
        }
        AssetManager assetManager = getAssets();
        InputStream in = null;
        OutputStream out = null;
        try {
            in = assetManager.open(filename);
            File outFile = new File(dirPath, filename);
            out = new FileOutputStream(outFile);
            copyFile(in, out);
            Toast.makeText(this, "Saved!", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "Failed!", Toast.LENGTH_SHORT).show();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    private void copyFile(InputStream in, OutputStream out) throws IOException{
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
    }
}

错误日志

W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:492)
W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:236)
W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
W/System.err:     at com.example.hcghelp.GKL10_pre.copyAsset(GKL10_pre.java:84)
W/System.err:     at com.example.hcghelp.GKL10_pre.access$000(GKL10_pre.java:28)
W/System.err:     at com.example.hcghelp.GKL10_pre$2.onClick(GKL10_pre.java:56)
W/System.err:     at android.view.View.performClick(View.java:7448)
W/System.err:     at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
W/System.err:     at android.view.View.performClickInternal(View.java:7425)
W/System.err:     at android.view.View.access$3600(View.java:810)
W/System.err:     at android.view.View$PerformClick.run(View.java:28305)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:938)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err:     at android.os.Looper.loop(Looper.java:223)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:7656)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
W/System.err:     at libcore.io.Linux.open(Native Method)
W/System.err:     at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:254)
W/System.err:     at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
W/System.err:     at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7542)
W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:478)
W/System.err:   ... 17 more
D/CompatibilityChangeReporter: Compat change id reported: 147798919; UID 10157; state: ENABLED

问题原因

卸载重装后,系统会保留应用之前的权限状态记忆,而代码未实现运行时权限申请逻辑,直接操作外部存储时触发权限拒绝。同时不同Android版本的存储权限机制存在差异,需要针对性适配。

解决方案

1. 适配Manifest配置

  • 针对Android 10(API 29),在application标签添加兼容旧存储模式的属性:
    android:requestLegacyExternalStorage="true"
    
  • MANAGE_EXTERNAL_STORAGE仅适用于Android 11+且需要全存储访问的场景,若只是保存到下载目录,可暂时保留但无需主动申请(除非有特殊需求)。

2. 实现运行时权限申请逻辑

修改GKL10_preActivity代码,添加权限检查和申请流程:

步骤1:定义常量

在Activity类中添加:

private static final int REQUEST_STORAGE_PERMISSION = 100;
private static final String[] STORAGE_PERMISSIONS = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

步骤2:修改按钮点击事件

替换原按钮点击逻辑,先检查权限再执行复制:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // 检查写入权限是否已授予
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            copyAssets();
        } else {
            // 申请权限
            requestPermissions(STORAGE_PERMISSIONS, REQUEST_STORAGE_PERMISSION);
        }
    }
});

// 封装复制方法
private void copyAssets() {
    copyAsset("file1.pdf");
    copyAsset("file2.pdf");
    copyAsset("file3.pdf");
    copyAsset("file4.pdf");
}

步骤3:处理权限申请结果

重写onRequestPermissionsResult方法:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_STORAGE_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限通过,执行文件复制
            copyAssets();
        } else {
            // 权限被拒绝,提示用户
            Toast.makeText(this, "需要存储权限才能保存文件", Toast.LENGTH_LONG).show();
        }
    }
}

3. 优化存储路径(可选)

针对Android 10+,推荐使用系统提供的公开下载目录路径,替代手动拼接:

// 替换copyAsset方法中的dirPath定义
String dirPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();

如果不需要文件在应用卸载后保留,可使用应用专属外部存储(无需权限):

String dirPath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();

4. 测试验证

卸载重装应用后,点击保存按钮会弹出权限申请弹窗,用户同意后即可正常保存Assets中的文件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:36:17