大家都知道Android10最大的变化可能就是Scoped Storage(分区存储)。对于把图片保存到相册的应用,影响就大了,因为这个功能在Android10的手机上就会出现异常了,今天就来说说如何兼容Android10保存图片到相册。

1、要存储权限

 protected void save() {AndPermission.with(getContext()).runtime().permission(Permission.WRITE_EXTERNAL_STORAGE,Permission.READ_EXTERNAL_STORAGE).onGranted(permissions -> {Object uri = urls.get(isInfinite ? position % urls.size() : position);//图片地址saveNetPic(getContext(),uri);position));}).onDenied(permissions -> {ToastUtil.showLong("权限被拒绝!");}).start();}

2、保存到本应用的文件目录下,这个步骤不需要权限

 private void saveNetPic(final Context mContext,Object uri){final Handler mainHandler = new Handler(Looper.getMainLooper());final ExecutorService executor = Executors.newSingleThreadExecutor();executor.execute(new Runnable() {@Overridepublic void run() {File source = imageLoader.getImageFile(mContext, uri);try {//1. create pathString dirPath = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();File dirFile = new File(dirPath);if (!dirFile.exists()) dirFile.mkdirs();ImageType type = ImageHeaderParser.getImageType(new FileInputStream(source));String ext = getFileExt(type);final File target = new File(dirPath, System.currentTimeMillis() + "." + ext);if (target.exists()) target.delete();target.createNewFile();//2. save,保存到本应用目录writeFileFromIS(target, new FileInputStream(source));//3. notifyMediaScannerConnection.scanFile(mContext, new String[]{target.getAbsolutePath()},new String[]{"image/" + ext}, new MediaScannerConnection.OnScanCompletedListener() {@Overridepublic void onScanCompleted(final String path, Uri uri) {mainHandler.post(new Runnable() {@Overridepublic void run() {Toast.makeText(mContext, "已保存到相册!", Toast.LENGTH_SHORT).show();//4.保存到相册try {Bitmap bitmap = BitmapFactory.decodeFile(target.getAbsolutePath());saveBitmap(getContext(),bitmap);} catch (Exception e) {e.printStackTrace();}}});}});} catch (IOException e) {e.printStackTrace();}}});}

3、通过SAF的方式保存文件到任意位置

 public void saveBitmap(Context context, Bitmap  bitmap) {ContentValues values = new ContentValues();values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");values.put(MediaStore.Images.Media.TITLE, System.currentTimeMillis()+".png");values.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Camera");Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;ContentResolver resolver = context.getContentResolver();Uri insertUri = resolver.insert(external, values);OutputStream os = null;if (insertUri != null) {try {os = resolver.openOutputStream(insertUri);bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);} catch (IOException e) {e.printStackTrace();}finally {try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}}

其中写入文件流和判断图片格式的代码如下:
写入文件流

    private static boolean writeFileFromIS(final File file, final InputStream is) {OutputStream os = null;try {os = new BufferedOutputStream(new FileOutputStream(file));byte data[] = new byte[8192];int len;while ((len = is.read(data, 0, 8192)) != -1) {os.write(data, 0, len);}return true;} catch (IOException e) {e.printStackTrace();return false;} finally {try {is.close();} catch (IOException e) {e.printStackTrace();}try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}

判断图片类型:

private static String getFileExt(ImageType type) {switch (type) {case GIF:return "gif";case PNG:case PNG_A:return "png";case WEBP:case WEBP_A:return "webp";case JPEG:return "jpeg";}return "jpeg";}

还有个ImageHeaderParser来至 XPopup

import static com.lxj.xpopup.enums.ImageType.GIF;
import static com.lxj.xpopup.enums.ImageType.JPEG;
import static com.lxj.xpopup.enums.ImageType.PNG;
import static com.lxj.xpopup.enums.ImageType.PNG_A;
import static com.lxj.xpopup.enums.ImageType.UNKNOWN;/*** Date: 2020/3/24* author: SmallCake*/
public class ImageHeaderParser {private static final int GIF_HEADER = 0x474946;private static final int PNG_HEADER = 0x89504E47;static final int EXIF_MAGIC_NUMBER = 0xFFD8;// WebP-related// "RIFF"private static final int RIFF_HEADER = 0x52494646;// "WEBP"private static final int WEBP_HEADER = 0x57454250;// "VP8" null.private static final int VP8_HEADER = 0x56503800;private static final int VP8_HEADER_MASK = 0xFFFFFF00;private static final int VP8_HEADER_TYPE_MASK = 0x000000FF;// 'X'private static final int VP8_HEADER_TYPE_EXTENDED = 0x00000058;// 'L'private static final int VP8_HEADER_TYPE_LOSSLESS = 0x0000004C;private static final int WEBP_EXTENDED_ALPHA_FLAG = 1 << 4;private static final int WEBP_LOSSLESS_ALPHA_FLAG = 1 << 3;public static ImageType getImageType(InputStream is) throws IOException{Reader reader = new StreamReader(is);final int firstTwoBytes = reader.getUInt16();// JPEG.if (firstTwoBytes == EXIF_MAGIC_NUMBER) {return JPEG;}final int firstFourBytes = (firstTwoBytes << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);// PNG.if (firstFourBytes == PNG_HEADER) {// See: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha// -color-typereader.skip(25 - 4);int alpha = reader.getByte();// A RGB indexed PNG can also have transparency. Better safe than sorry!return alpha >= 3 ? PNG_A : PNG;}// GIF from first 3 bytes.if (firstFourBytes >> 8 == GIF_HEADER) {return GIF;}// WebP (reads up to 21 bytes). See https://developers.google.com/speed/webp/docs/riff_container// for details.if (firstFourBytes != RIFF_HEADER) {return UNKNOWN;}// Bytes 4 - 7 contain length information. Skip these.reader.skip(4);final int thirdFourBytes =(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);if (thirdFourBytes != WEBP_HEADER) {return UNKNOWN;}final int fourthFourBytes =(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);if ((fourthFourBytes & VP8_HEADER_MASK) != VP8_HEADER) {return UNKNOWN;}if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_EXTENDED) {// Skip some more length bytes and check for transparency/alpha flag.reader.skip(4);return (reader.getByte() & WEBP_EXTENDED_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;}if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_LOSSLESS) {// See chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt// for more info.reader.skip(4);return (reader.getByte() & WEBP_LOSSLESS_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;}is.close();return ImageType.WEBP;}private interface Reader {int getUInt16() throws IOException;short getUInt8() throws IOException;long skip(long total) throws IOException;int read(byte[] buffer, int byteCount) throws IOException;int getByte() throws IOException;}private static final class StreamReader implements Reader {private final InputStream is;// Motorola / big endian byte order.StreamReader(InputStream is) {this.is = is;}@Overridepublic int getUInt16() throws IOException {return (is.read() << 8 & 0xFF00) | (is.read() & 0xFF);}@Overridepublic short getUInt8() throws IOException {return (short) (is.read() & 0xFF);}@Overridepublic long skip(long total) throws IOException {if (total < 0) {return 0;}long toSkip = total;while (toSkip > 0) {long skipped = is.skip(toSkip);if (skipped > 0) {toSkip -= skipped;} else {// Skip has no specific contract as to what happens when you reach the end of// the stream. To differentiate between temporarily not having more data and// having finished the stream, we read a single byte when we fail to skip any// amount of data.int testEofByte = is.read();if (testEofByte == -1) {break;} else {toSkip--;}}}return total - toSkip;}@Overridepublic int read(byte[] buffer, int byteCount) throws IOException {int toRead = byteCount;int read;while (toRead > 0 && ((read = is.read(buffer, byteCount - toRead, toRead)) != -1)) {toRead -= read;}return byteCount - toRead;}@Overridepublic int getByte() throws IOException {return is.read();}}
}

参考:
OPPO - Android Q版本应用兼容性适配指导

作者:Small_Cake
链接:https://www.jianshu.com/p/cd9e58d1f8b9
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

android File和bitmap 相互转换 以及AsyncTask显示图片_龙之吻的博客-CSDN博客_android file转bitmap

安卓10(Android10\API29)保存图片到相册DCIM/Camera相关推荐

  1. Android10以上保存图片至相册

    一.Android Q的私有目录 这里的私有目录即是 App-specific 目录,具体路径是"内部存储设备/Android/data/com.项目包名/"下: (1)APP 卸 ...

  2. uni-app里面的打开相册,在安卓10上图片不允许访问

    uni-app里面的打开相册,在安卓10上图片不允许访问 问题:uni-app上传图片,安卓9可以查看手机相册 ,但是安卓10上查看相册显示如下 解决方案: 1.uni-app使用Android st ...

  3. 一加7pro系统更新android10,一加OnePlus7T Pro官方安卓10.0稳定版出厂系统固件升级更新包...

    咱们的这个一加OnePlus7T Pro手机的最新稳定版系统包也是在这里来分享一下了,这个稳定版本的系统包是安卓10稳定版的,也是第一个版本的,系统包大小是3.2G,系统方面主要是全新的UI设计,轻快 ...

  4. 小米android10升级版本,小米9 MIUI安卓10开发版已开始推送,建议大家不要去升级!...

    原标题:小米9 MIUI安卓10开发版已开始推送,建议大家不要去升级! 近期,有小米9用户接到了安卓10的开发版更新,此前Android Q开发版一直在内测中,并没有说明何时结束,这次意外接到系统更新 ...

  5. realme有升级到android10的,RealmeX/Q将升级安卓10底层刷机包

    Realme国内手机上很有Redmi的设计风格,无论是营销方式,還是系统软件的升級感受,都有点儿类似 OPPO先前而言,针对系统更新维护保养也并不那麼友善,大家最普遍的R9s冠军销售量的手机上官 其术 ...

  6. android10新功能,三星A80升级安卓10 更新One UI 2.0内容新功能介绍

    三星A80升级安卓10 更新One UI 2.0内容新功能介绍 据网友反馈,三星现已面向Galaxy A80用户推送One UI 2.0更新,升级Android 10. 此外,三星Galaxy A80 ...

  7. android10和11,安卓10与安卓11究竟差异在哪里?我们拿这两台新机试了一下

    原标题:安卓10与安卓11究竟差异在哪里?我们拿这两台新机试了一下 9 月 24 日,OPPO 带着全新的 ColorOS 11 与我们正式见面,这一基于安卓 11 底层深度打造的新系统给我们带来了不 ...

  8. Android10支持volte,Nemo_LG V35 安卓10.0解锁Volte(联通、电信、移动)教程_Nemo社区_LinkNemo_关于分享和探索的好地方...

    什么是volte? 早期的4g,只支持上网而不支持通话,这也就意味着当在通话的时候,网络会从4g会回落到3g或者2g上. 而后续随着技术的迭代,推出了volte,就是为了解决4g下通话的问题. LG ...

  9. android10官方支持机型,Andorid10.0支持哪些手机?附安卓10支持机型介绍

    Andorid10.0支持哪些手机?附安卓10支持机型介绍 2019-03-12 09:58:24  来源:qqtn.com 扫码可以: 1.在手机上浏览 2.分享给微信好友或朋友圈 摘要: Ando ...

最新文章

  1. [武道资料]《菲律宾短棍-单棍》(Edgar Sulite Lameco Escrima Single Stick)
  2. VS2008工程转为VS2005(C++)
  3. 操作系统习题8—设备管理
  4. 关于逆元的概念、用途和可行性的思考(附51nod 1013 和 51nod 1256)
  5. 八中计算机是学啥的,北京八中:居然有这么好玩的课
  6. LINUX下tar.gz包的安装方法
  7. c语言实现链表结构6,用c语言实现的链表结构--数据结构实验
  8. 我的世界python写游戏_快来试试Python写的游戏《我的世界》
  9. 爬虫-13-认识代理
  10. 表情识别(三)--基于几何与Gabor小波的多层感知
  11. maven中使用MySQL
  12. ks检验正态分布结果_SPSS实现Shapiro-Wilk正态分布检验
  13. 负反馈放大电路实验报告
  14. 100%解决GitHub打不开或者打开慢
  15. 大学排行引发大争议,谁在给大学排座次?
  16. Unity性能优化 :合批篇
  17. 手机投屏到电脑 -- 小黑超细日常教程
  18. 封杀这个公式,AI智商将为零
  19. 经典神经网络分类器之BP算法和自组织竞争网络
  20. 关于电脑已经成为肉鸡的六种现象

热门文章

  1. 对象及日期定时器、延时器
  2. 2020-12-02 微信JSAPIV3支付
  3. ABAP SE54 视图簇
  4. Python计算文件或字符串的MD5/SHA
  5. 【自动控制原理】【计算机控制技术】通俗易懂地理解Z变换
  6. 名企笔试真题精选 (六)
  7. 清亡之路(4):最受误解的东南互保
  8. asyne和await
  9. MIS WiFi安全相关-aircrack-ng破解WiFi密码
  10. 19年职业院校技能大赛总结