Android 11及以上机型WhatsApp状态加载异常,求适配Model与Adapter
问题根源分析
你的核心错误在于Android 11+(API 30+)不能通过File类直接处理SAF返回的content://类型Uri,new File(uri)完全无法正确解析这个Uri,导致后续的file.isDirectory()和listFiles()都不会执行,最终列表为空。必须使用DocumentFile来遍历SAF授权的目录。
1. StatusModel(数据模型)
存储SAF文件Uri、文件名、文件类型等核心信息:
public class StatusModel { private Uri fileUri; private String fileName; private boolean isVideo; public StatusModel(Uri fileUri, String fileName) { this.fileUri = fileUri; this.fileName = fileName; // 根据后缀判断是否为视频文件 this.isVideo = fileName != null && (fileName.endsWith(".mp4") || fileName.endsWith(".avi") || fileName.endsWith(".mov")); } public Uri getFileUri() { return fileUri; } public String getFileName() { return fileName; } public boolean isVideo() { return isVideo; } }
2. RecentAdapter(RecyclerView适配器)
使用Glide加载SAF Uri对应的媒体文件,包含基础点击事件:
public class RecentAdapter extends RecyclerView.Adapter<RecentAdapter.StatusViewHolder> { private Context context; private List<StatusModel> statusList; private OnItemActionListener actionListener; // 定义事件回调接口 public interface OnItemActionListener { void onItemClick(StatusModel model); void onSaveClick(StatusModel model); } public RecentAdapter(Context context, List<StatusModel> statusList, OnItemActionListener actionListener) { this.context = context; this.statusList = statusList; this.actionListener = actionListener; } @NonNull @Override public StatusViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item_status, parent, false); return new StatusViewHolder(view); } @Override public void onBindViewHolder(@NonNull StatusViewHolder holder, int position) { StatusModel model = statusList.get(position); // 加载媒体内容 if (model.isVideo()) { // 加载视频封面 Glide.with(context) .asBitmap() .load(model.getFileUri()) .into(holder.statusImageView); holder.videoTag.setVisibility(View.VISIBLE); } else { // 加载图片 Glide.with(context) .load(model.getFileUri()) .into(holder.statusImageView); holder.videoTag.setVisibility(View.GONE); } // 绑定点击事件 holder.itemView.setOnClickListener(v -> actionListener.onItemClick(model)); holder.saveBtn.setOnClickListener(v -> actionListener.onSaveClick(model)); } @Override public int getItemCount() { return statusList.size(); } // ViewHolder定义 public static class StatusViewHolder extends RecyclerView.ViewHolder { ImageView statusImageView; ImageView videoTag; Button saveBtn; public StatusViewHolder(@NonNull View itemView) { super(itemView); statusImageView = itemView.findViewById(R.id.iv_status); videoTag = itemView.findViewById(R.id.iv_video_tag); saveBtn = itemView.findViewById(R.id.btn_save); } } }
对应的item_status.xml布局示例:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="4dp"> <ImageView android:id="@+id/iv_status" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop"/> <ImageView android:id="@+id/iv_video_tag" android:layout_width="40dp" android:layout_height="40dp" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" android:layout_margin="8dp" android:src="@drawable/ic_video_play"/> <Button android:id="@+id/btn_save" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" android:layout_margin="8dp" android:text="保存"/> </RelativeLayout>
3. 修正后的RecyclerView数据加载逻辑
替换你原有的Android 11+加载代码,使用DocumentFile遍历并排序文件:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { SharedPreferences sharedPreferences = getContext().getSharedPreferences("app", MODE_PRIVATE); String uriStr = sharedPreferences.getString("uri", ""); // 校验Uri有效性 if (TextUtils.isEmpty(uriStr)) { emptyLay.setVisibility(View.VISIBLE); return; } Uri treeUri = Uri.parse(uriStr); DocumentFile rootDir = DocumentFile.fromTreeUri(getContext(), treeUri); if (rootDir == null || !rootDir.exists() || !rootDir.isDirectory()) { emptyLay.setVisibility(View.VISIBLE); return; } f = new ArrayList<>(); DocumentFile[] files = rootDir.listFiles(); if (files != null) { // 转换为List并按修改时间倒序排序 List<DocumentFile> fileList = Arrays.asList(files); Collections.sort(fileList, (file1, file2) -> Long.compare(file2.lastModified(), file1.lastModified()) ); // 过滤有效文件 for (DocumentFile file : fileList) { String fileName = file.getName(); if (file.isFile() && fileName != null && !fileName.equals(".nomedia")) { f.add(new StatusModel(file.getUri(), fileName)); } } } }
关键注意事项
- 异步处理:目录遍历和排序要放在子线程执行,避免阻塞UI线程,推荐用
Coroutine或AsyncTask替代Handler.postDelayed。 - 权限持久化:确保已通过
takePersistableUriPermission获取持久化读权限,否则重启APP后权限会失效。 - Glide兼容性:使用最新版Glide以支持SAF Uri加载,若仍有问题可添加自定义Uri解析配置。
内容的提问来源于stack exchange,提问作者Hamza khan
相关产品推荐
相关产品推荐

