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
相关产品推荐
相关产品推荐

