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

Android文档浏览App权限异常与文件加载失败问题求助

问题分析与解决方案

核心问题点

  • 权限请求为异步操作,原代码在调用requestPermissions后立即执行加载逻辑,此时权限尚未完成授予,导致读取失败
  • onCreate中重复调用多套文档加载方法,后执行的方法会覆盖之前的documentList,最终显示空列表
  • Android 10+(API 29)中MediaStore.Files.FileColumns.DATA字段已废弃,无法直接获取文件路径;且Uri.fromFile()在Android 7+会触发FileUriExposedException
  • 未适配Android 13+的细分媒体权限,导致高版本系统权限申请不完整

修正步骤

1. 调整权限请求逻辑,异步等待权限结果

移除onCreate中直接调用的加载方法,仅在权限授予后执行加载:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    recyclerView = findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // 适配Android 13+细分权限
        String[] permissions;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            permissions = new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_DOCUMENTS};
        } else {
            permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
        }
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    } else {
        // 低版本直接加载文档
        loadDocumentsWithMediaStore();
    }
}

2. 统一文档加载逻辑,适配Scoped Storage

废弃冗余的loadDocumentsWithScopedStorage方法,统一使用loadDocumentsWithMediaStore并修复高版本适配问题:

// 自定义Document类存储文件信息(替代直接使用File)
public class Document {
    private String name;
    private Uri contentUri;

    public Document(String name, Uri contentUri) {
        this.name = name;
        this.contentUri = contentUri;
    }

    public String getName() { return name; }
    public Uri getContentUri() { return contentUri; }
}

// 修正后的加载方法
private void loadDocumentsWithMediaStore() {
    ArrayList<Document> documentList = new ArrayList<>();
    Uri collection = MediaStore.Files.getContentUri("external");

    String[] projection = {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.DISPLAY_NAME,
            MediaStore.Files.FileColumns.MIME_TYPE
    };

    // 补充PPT、Excel的MIME类型,覆盖需求中的所有文档格式
    String selection = MediaStore.Files.FileColumns.MIME_TYPE + " IN (?, ?, ?, ?, ?, ?, ?, ?)";
    String[] selectionArgs = new String[]{
            "application/pdf",
            "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "text/plain",
            "application/vnd.ms-powerpoint",
            "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    };

    try (Cursor cursor = getContentResolver().query(collection, projection, selection, selectionArgs, null)) {
        if (cursor != null && cursor.moveToFirst()) {
            int idIndex = cursor.getColumnIndex(MediaStore.Files.FileColumns._ID);
            int nameIndex = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME);

            do {
                long fileId = cursor.getLong(idIndex);
                String fileName = cursor.getString(nameIndex);
                // 通过Content Uri访问文件,避免路径权限问题
                Uri contentUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), fileId);
                documentList.add(new Document(fileName, contentUri));
            } while (cursor.moveToNext());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 初始化Adapter并绑定数据
    DocumentAdapter documentAdapter = new DocumentAdapter(documentList, document -> {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(document.getContentUri(), getContentResolver().getType(document.getContentUri()));
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(intent);
    });
    recyclerView.setAdapter(documentAdapter);
}

3. 适配Adapter支持自定义Document类

修改DocumentAdapter以适配新的Document对象:

public class DocumentAdapter extends RecyclerView.Adapter<DocumentAdapter.DocumentViewHolder> {

    private final ArrayList<Document> documentList;
    private final OnDocumentClickListener listener;

    public DocumentAdapter(ArrayList<Document> documentList, OnDocumentClickListener listener) {
        this.documentList = documentList;
        this.listener = listener;
    }

    @NonNull
    @Override
    public DocumentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
        return new DocumentViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull DocumentViewHolder holder, int position) {
        Document document = documentList.get(position);
        holder.textView.setText(document.getName());
        holder.itemView.setOnClickListener(v -> listener.onDocumentClick(document));
    }

    @Override
    public int getItemCount() {
        return documentList.size();
    }

    public interface OnDocumentClickListener {
        void onDocumentClick(Document document);
    }

    static class DocumentViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public DocumentViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(android.R.id.text1);
        }
    }
}

4. 修正权限回调逻辑

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == PERMISSION_REQUEST_CODE) {
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            loadDocumentsWithMediaStore();
        } else {
            Toast.makeText(this, "权限被拒绝,无法读取文档", Toast.LENGTH_SHORT).show();
        }
    }
}

额外配置

在AndroidManifest.xml中添加权限声明:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33"/>
<uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" android:minSdkVersion="33"/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:15:55