Android Marshmallow下ImageView加载内部存储图片失败求助
解决Android Marshmallow中ImageView加载外部存储图片的问题
嘿,我来帮你搞定这个Marshmallow系统上ImageView加载图片的问题~
首先得澄清一个小误区:你代码里用的Environment.getExternalStorageDirectory()其实是外部存储路径(不是手机的内部存储哦),而Android 6.0(Marshmallow)开始对存储这类危险权限有了严格的运行时申请要求,这大概率是你代码失效的核心原因,下面一步步来解决:
1. 先搞定运行时权限
在Marshmallow及以上系统,访问外部存储的READ_EXTERNAL_STORAGE属于危险权限,不能只在Manifest里声明就完事,必须在代码里动态申请用户授权:
第一步:在AndroidManifest.xml里先加权限声明
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 如果之后需要写入文件,再加上WRITE_EXTERNAL_STORAGE权限就行,现在读的话READ足够 -->
第二步:在你的Activity/Fragment里动态申请权限
private static final int REQUEST_READ_STORAGE = 100; // 加载图片前先检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 权限没给,弹框申请 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_READ_STORAGE); } else { // 权限已经有了,直接加载图片 loadImageIntoImageView(); } // 处理用户的权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_READ_STORAGE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户同意了,加载图片 loadImageIntoImageView(); } else { // 用户拒了,提示一下 Toast.makeText(this, "得给存储权限才能加载图片哦", Toast.LENGTH_SHORT).show(); } } } // 把你原来的加载图片逻辑封装到这个方法里 private void loadImageIntoImageView() { String path = Environment.getExternalStorageDirectory()+ "/MyFolder/final_photo"; File imgFile = new File(path); if (imgFile.exists()) { imageView.setImageBitmap(decodeFile(imgFile)); } else { // 文件不存在的话可以加个提示 Toast.makeText(this, "图片文件找不到哦", Toast.LENGTH_SHORT).show(); } }
2. 优化你的decodeFile方法,避免OOM
如果你的decodeFile没做图片压缩,很容易搞出内存溢出(OOM),给你整个实用的压缩版:
private Bitmap decodeFile(File imgFile) { BitmapFactory.Options options = new BitmapFactory.Options(); // 先只拿图片尺寸,不加载到内存里 options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imgFile.getAbsolutePath(), options); // 根据ImageView的实际尺寸计算压缩比例 int reqWidth = imageView.getWidth(); int reqHeight = imageView.getHeight(); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 现在加载压缩后的图片 options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(imgFile.getAbsolutePath(), options); } private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // 找到最合适的压缩比例,保证压缩后的图片尺寸不小于ImageView的需求 while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
3. 最后排查几个小坑
- 文件后缀问题:你代码里的文件名是
final_photo,有没有可能漏掉了后缀?比如final_photo.jpg或者final_photo.png?如果没有后缀,BitmapFactory可能识别不了图片格式。 - 路径正确性:可以用Log打印
path的值,然后手动去文件管理器里看看这个路径下是不是真的有这个图片文件。
内容的提问来源于stack exchange,提问作者Manu
相关产品推荐
相关产品推荐

