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

Android权限ACTION_OPEN_DOCUMENT报错:Firebase图片无法在RecyclerView展示

解决Firebase Storage图片在RecyclerView中加载的权限错误问题

问题根源

你用ImageView.setImageURI()加载Firebase Storage的网络URL是错误的,这个方法仅适用于本地文件Uri(比如设备存储里的文件路径),系统会误以为你要访问本地文件,所以抛出ACTION_OPEN_DOCUMENT权限错误。而你申请的READ_EXTERNAL_STORAGE权限完全没必要——因为图片存储在云端,不在本地设备里。

解决方案:用图片加载库加载网络图片

推荐使用Glide(或Picasso)这类成熟的图片加载库,它们专门处理网络图片加载,无需本地存储权限,还支持缓存、占位图等优化。

步骤1:添加Glide依赖

在你的Module级build.gradle(通常是app/build.gradle)的dependencies块中添加:

implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'

同步项目后即可使用。

步骤2:修改Adapter的图片加载逻辑

替换setImageURI为Glide的加载代码:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    Tr_Receipt receipt = Tr_Rec_List.get(position);

    holder.nameTextView.setText(receipt.getTr_rec_name());
    // 用Glide加载Firebase Storage的网络URL
    Glide.with(holder.imageView.getContext())
         .load(receipt.getTr_rec_url())
         .placeholder(R.drawable.placeholder) // 可选:加载时的占位图
         .error(R.drawable.error_image) // 可选:加载失败时的图
         .into(holder.imageView);
}

若没有占位图,可直接去掉placeholder和error相关代码。

步骤3:移除不必要的权限代码

既然不需要访问本地存储,把Tr_Receipt_Retrieve中所有和READ_EXTERNAL_STORAGE相关的代码删掉,同时修正初始化顺序(原代码先加载数据再初始化Adapter会导致空指针):

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

    FirebaseAuth mAuth = FirebaseAuth.getInstance();
    currentUser = mAuth.getCurrentUser();

    // 先初始化RecyclerView和Adapter
    recyclerView = findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    adapter = new Tr_Receipt_Adapter();
    recyclerView.setAdapter(adapter);

    // 再加载用户数据
    if (currentUser != null) {
        String userID = currentUser.getUid();
        loadDataFromFirestore(userID);
    } else {
        // 处理用户未登录的情况,比如跳转到登录页
    }
}

完整修改后的代码示例

修改后的Adapter

import com.bumptech.glide.Glide;

public class Tr_Receipt_Adapter extends RecyclerView.Adapter<Tr_Receipt_Adapter.ViewHolder>{
    private List<Tr_Receipt> Tr_Rec_List = new ArrayList<>();

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Tr_Receipt receipt = Tr_Rec_List.get(position);

        holder.nameTextView.setText(receipt.getTr_rec_name());
        // 使用Glide加载网络图片
        Glide.with(holder.imageView.getContext())
             .load(receipt.getTr_rec_url())
             .into(holder.imageView);
    }

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

    public void addData(Tr_Receipt newData) {
        Tr_Rec_List.add(newData);
        notifyDataSetChanged();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView nameTextView;
        ImageView imageView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            nameTextView = itemView.findViewById(R.id.tr_receipt_name_et);
            imageView = itemView.findViewById(R.id.tr_receipt_imgv);
        }
    }
}

修改后的Display类

public class Tr_Receipt_Retrieve extends AppCompatActivity {
    private static final String LOG_TAG = MainActivity.class.getName();

    private RecyclerView recyclerView;
    private Tr_Receipt_Adapter adapter;
    FirebaseUser currentUser;

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

        FirebaseAuth mAuth = FirebaseAuth.getInstance();
        currentUser = mAuth.getCurrentUser();

        // 初始化RecyclerView和Adapter
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new Tr_Receipt_Adapter();
        recyclerView.setAdapter(adapter);

        // 加载用户数据
        if (currentUser != null) {
            String userID = currentUser.getUid();
            loadDataFromFirestore(userID);
        } else {
            // 处理用户未登录的情况,比如跳转到登录页
        }
    }

    private void loadDataFromFirestore(String userID) {
        FirebaseFirestore db = FirebaseFirestore.getInstance();

        db.collection("tr_receipt")
                .whereEqualTo("userID", userID)
                .get()
                .addOnSuccessListener(queryDocumentSnapshots -> {
                    for (DocumentSnapshot document : queryDocumentSnapshots) {
                        Tr_Receipt object = document.toObject(Tr_Receipt.class);
                        Log.d("RetrievedObject", object.toString());
                        adapter.addData(object);
                    }
                })
                .addOnFailureListener(e -> {
                    Log.d(LOG_TAG, "加载数据失败:" + e.getMessage());
                });
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:50:05