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

Android本地JSON文件读写路径适配问题求助

解决Android 11下外部存储JSON文件读写失败问题

问题根源

Android 11(API 30)引入了Scoped Storage机制,严格限制应用对外部存储公共目录的直接访问权限。你的代码依赖Environment.getExternalStorageDirectory()访问外部存储根目录或Download目录,在Android 11及以上系统中会被权限拦截;而Android 5.1没有该限制,因此模拟器能正常运行。

方案1:改用应用内部私有存储(推荐)

应用内部私有存储无需任何权限,文件仅当前应用可访问,适合存储应用专属的JSON文件。

修改读文件代码(fetchData类)

替换文件路径为应用内部存储目录:

class fetchData extends Thread {
    @Override
    public void run() {
        try {
            // 文件将保存在 /data/data/你的应用包名/files/TEST.json
            File yourFile = new File(getFilesDir(), "TEST.json");
            FileInputStream stream = new FileInputStream(yourFile);
            String data = null;
            try {
                FileChannel fc = stream.getChannel();
                MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
                data = Charset.defaultCharset().decode(bb).toString();
            } catch(Exception e){
                e.printStackTrace();
            } finally {
                stream.close();
            }
            JSONObject jsonObject = new JSONObject(data);
            JSONArray Data = jsonObject.getJSONArray("DATA");
            for (int i = 0; i < Data.length(); i++) {
                Urun urun = new Urun();
                //(CENSORED)
                uruns.add(urun);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        adapter = new Adapter(getApplicationContext(), uruns);
        recyclerView.setAdapter(adapter);
    }
}

修改写文件代码(fetchDataWrite类)

同样替换路径为内部存储,新增文件存在性判断:

class fetchDataWrite extends Thread {
    @Override
    public void run() {
        try {
            File yourFile = new File(getFilesDir(), "TEST.json");
            // 若文件不存在则创建
            if (!yourFile.exists()) {
                yourFile.createNewFile();
            }
            FileInputStream stream = new FileInputStream(yourFile);
            String data = null;
            try {
                FileChannel fc = stream.getChannel();
                MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
                data = Charset.defaultCharset().decode(bb).toString();
            } catch(Exception e){
                e.printStackTrace();
            } finally {
                stream.close();
            }
            JSONObject jsonObject = new JSONObject(data);
            JSONArray Data = jsonObject.getJSONArray("DATA");
            //(CENSORED)
            FileWriter fileWriter = new FileWriter(yourFile);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write(/*CENSORED STRING*/);
            bufferedWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

补充说明

  • 可通过Android Studio的Device File Explorer查看内部存储文件,路径为data/data/你的应用包名/files/
  • 若需迁移原外部存储的TEST.json,可手动通过Device File Explorer复制到内部存储目录,或在应用启动时添加自动迁移逻辑

方案2:适配Scoped Storage(需保留外部存储时使用)

如果必须将文件放在外部存储公共目录(如Download),需使用MediaStore或存储访问框架(SAF)操作文件:

写入Download目录示例

private void writeToDownloadDir(String fileName, String content) {
    ContentValues values = new ContentValues();
    values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
    values.put(MediaStore.Downloads.MIME_TYPE, "application/json");
    values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

    Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
    if (uri != null) {
        try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
            if (outputStream != null) {
                outputStream.write(content.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

读取Download目录的TEST.json示例

private String readFromDownloadDir(String fileName) {
    Uri uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
    String[] projection = {MediaStore.Downloads._ID, MediaStore.Downloads.DATA};
    String selection = MediaStore.Downloads.DISPLAY_NAME + " = ?";
    String[] selectionArgs = {fileName};

    Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, null);
    if (cursor != null && cursor.moveToFirst()) {
        int dataIndex = cursor.getColumnIndex(MediaStore.Downloads.DATA);
        String filePath = cursor.getString(dataIndex);
        cursor.close();

        try (FileInputStream stream = new FileInputStream(filePath)) {
            FileChannel fc = stream.getChannel();
            MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
            return Charset.defaultCharset().decode(bb).toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

注意事项

  • Android 10(API 29)可通过在AndroidManifest.xml中添加android:requestLegacyExternalStorage="true"暂时绕过Scoped Storage,但该属性在Android 11及以上系统无效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 13:20:49