美文网首页
安卓10(Android10\API29)保存图片到相册DCIM

安卓10(Android10\API29)保存图片到相册DCIM

作者: Small_Cake | 来源:发表于2020-04-16 18:06 被阅读0次

    大家都知道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() {
                @Override
                public void run() {
                    File source = imageLoader.getImageFile(mContext, uri);
                    try {
                        //1. create path
                        String 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. notify
                        MediaScannerConnection.scanFile(mContext, new String[]{target.getAbsolutePath()},
                                new String[]{"image/" + ext}, new MediaScannerConnection.OnScanCompletedListener() {
                                    @Override
                                    public void onScanCompleted(final String path, Uri uri) {
                                        mainHandler.post(new Runnable() {
                                            @Override
                                            public 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-type
                reader.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;
            }
    
            @Override
            public int getUInt16() throws IOException {
                return (is.read() << 8 & 0xFF00) | (is.read() & 0xFF);
            }
    
            @Override
            public short getUInt8() throws IOException {
                return (short) (is.read() & 0xFF);
            }
    
            @Override
            public 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;
            }
    
            @Override
            public 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;
            }
    
            @Override
            public int getByte() throws IOException {
                return is.read();
            }
        }
    }
    

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

    相关文章

      网友评论

          本文标题:安卓10(Android10\API29)保存图片到相册DCIM

          本文链接:https://www.haomeiwen.com/subject/objayhtx.html