Android Q(10) 파일 스토리지 어댑터

Android Q 공식 문서
Android Q 파티션 스토리지
  • 안드로이드 Q 파일 저장 메커니즘이 샌드박스 모드로 수정되어 IOS와 유사
  • 애플리케이션은 자신의 샌드박스 아래의 파일과 공공 미디어 파일만 접근할 수 있음
  • Android Q 이하 또는 오래된 파일 저장 방식
  • 사용 권한
    Android Q는 파일 읽기와 쓰기 권한을 요청할 필요가 없으며 기본적으로 자신의 샌드박스 파일과 공공 미디어 파일을 읽을 수 있습니다.따라서 Q 이상에서는 서류 읽기와 쓰기 권한을 더 이상 동적 신청할 필요가 없다.
    샌드박스 스토리지/읽기/쓰기
    샌드박스 지정 폴더 가져오기
    //    
    //               
    Environment.getExternalStorageDirectory();
    Environment.getExternalStoragePublicDirectory();
    
    //          
    //         
    File filePictures = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    
    //Environment         
    //     ,     ,             ,        
    //                
     /**
         * Standard directory in which to place any audio files that should be
         * in the regular list of music for the user.
         * This may be combined with
         * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
         * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
         * of directories to categories a particular audio file as more than one
         * type.
         */
        public static String DIRECTORY_MUSIC = "Music";
    
        /**
         * Standard directory in which to place any audio files that should be
         * in the list of podcasts that the user can select (not as regular
         * music).
         * This may be combined with {@link #DIRECTORY_MUSIC},
         * {@link #DIRECTORY_NOTIFICATIONS},
         * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
         * of directories to categories a particular audio file as more than one
         * type.
         */
        public static String DIRECTORY_PODCASTS = "Podcasts";
    
        /**
         * Standard directory in which to place any audio files that should be
         * in the list of ringtones that the user can select (not as regular
         * music).
         * This may be combined with {@link #DIRECTORY_MUSIC},
         * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
         * {@link #DIRECTORY_ALARMS} as a series
         * of directories to categories a particular audio file as more than one
         * type.
         */
        public static String DIRECTORY_RINGTONES = "Ringtones";
    
        /**
         * Standard directory in which to place any audio files that should be
         * in the list of alarms that the user can select (not as regular
         * music).
         * This may be combined with {@link #DIRECTORY_MUSIC},
         * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
         * and {@link #DIRECTORY_RINGTONES} as a series
         * of directories to categories a particular audio file as more than one
         * type.
         */
        public static String DIRECTORY_ALARMS = "Alarms";
    
        /**
         * Standard directory in which to place any audio files that should be
         * in the list of notifications that the user can select (not as regular
         * music).
         * This may be combined with {@link #DIRECTORY_MUSIC},
         * {@link #DIRECTORY_PODCASTS},
         * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
         * of directories to categories a particular audio file as more than one
         * type.
         */
        public static String DIRECTORY_NOTIFICATIONS = "Notifications";
    
        /**
         * Standard directory in which to place pictures that are available to
         * the user.  Note that this is primarily a convention for the top-level
         * public directory, as the media scanner will find and collect pictures
         * in any directory.
         */
        public static String DIRECTORY_PICTURES = "Pictures";
    
        /**
         * Standard directory in which to place movies that are available to
         * the user.  Note that this is primarily a convention for the top-level
         * public directory, as the media scanner will find and collect movies
         * in any directory.
         */
        public static String DIRECTORY_MOVIES = "Movies";
    
        /**
         * Standard directory in which to place files that have been downloaded by
         * the user.  Note that this is primarily a convention for the top-level
         * public directory, you are free to download files anywhere in your own
         * private directories.  Also note that though the constant here is
         * named DIRECTORY_DOWNLOADS (plural), the actual file name is non-plural for
         * backwards compatibility reasons.
         */
        public static String DIRECTORY_DOWNLOADS = "Download";
    
        /**
         * The traditional location for pictures and videos when mounting the
         * device as a camera.  Note that this is primarily a convention for the
         * top-level public directory, as this convention makes no sense elsewhere.
         */
        public static String DIRECTORY_DCIM = "DCIM";
    
        /**
         * Standard directory in which to place documents that have been created by
         * the user.
         */
        public static String DIRECTORY_DOCUMENTS = "Documents";
    
        /**
         * Standard directory in which to place screenshots that have been taken by
         * the user. Typically used as a secondary directory under
         * {@link #DIRECTORY_PICTURES}.
         */
        public static String DIRECTORY_SCREENSHOTS = "Screenshots";
    
        /**
         * Standard directory in which to place any audio files which are
         * audiobooks.
         */
        public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
    
    

    샌드박스에 새 폴더와 새 파일 만들기
    private String signImage = "signImage";
    //         
    //  :
    //1.                
    //2.                        
    public void saveSignImageBox(String fileName, Bitmap bitmap) {
        try {
            //       
            File PICTURES = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            File imageFileDirctory = new File(PICTURES + "/" + signImage);
            if (imageFileDirctory.exists()) {
                File imageFile = new File(PICTURES + "/" + signImage + "/" + fileName);
                FileOutputStream fileOutputStream = new FileOutputStream(imageFile);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
                fileOutputStream.flush();
                fileOutputStream.close();
            } else if (imageFileDirctory.mkdir()) {//         ,   
                //new    
                File imageFile = new File(PICTURES + "/" + signImage + "/" + fileName);
                //        
                FileOutputStream fileOutputStream = new FileOutputStream(imageFile);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
                fileOutputStream.flush();
                fileOutputStream.close();
            }
        } catch (Exception e) {
        }
    }
    

    샌드박스에서 지정한 파일 조회
    //          
    //           ,     
    public Bitmap querySignImageBox(String environmentType,String fileName) {
        if (TextUtils.isEmpty(fileName)) return null;
        Bitmap bitmap = null;
        try {
            //       
            File picturesFile = getExternalFilesDir(environmentType);
            if (picturesFile != null && picturesFile.exists() && picturesFile.isDirectory()) {
                File[] files = picturesFile.listFiles();
                if (files != null) {
                    for (File file : files) {
                        if (file.isDirectory() && file.getName().equals(signImage)) {
                            File[] signImageFiles = file.listFiles();
                            if (signImageFiles != null) {
                                for (File signImageFile : files) {
                                    String signFileName = signImageFile.getName();
                                    if (signImageFile.isFile() && fileName.equals(signFileName)) {
                                        bitmap = BitmapFactory.decodeFile(signImageFile.getPath());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
        }
        return bitmap;
    }
    

    공문서의 증가, 삭제, 수정, 조사
    공용 파일의 작업에는 Content Resolver 및 Cursor가 필요합니다.
    공용 폴더에 파일 추가
    //              
    //   filepath      ,              ,         
    //   filename       ,     
    public void saveSignImage(String filePath,String fileName, Bitmap bitmap) {
        try {
            //       ContentValues 
            ContentValues contentValues = new ContentValues();
            //     
            contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
            //  Android Q     
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                //android Q     DATA  ,  RELATIVE_PATH  
                //RELATIVE_PATH           
                //DCIM      ,                       ,          
                contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/signImage");
                //contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Music/signImage");
            } else {
                contentValues.put(MediaStore.Images.Media.DATA, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath());
            }
            //      
            contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG");
            //  insert  ,           
            //EXTERNAL_CONTENT_URI       ,    
            Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            if (uri != null) {
                //    uri,          
                //         uri   
                OutputStream outputStream = getContentResolver().openOutputStream(uri);
                if (outputStream != null) {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
                    outputStream.flush();
                    outputStream.close();
                }
            }
        } catch (Exception e) {
        }
    }
    

    공용 폴더 아래의 파일 조회
    //           
    //   filepath androidQ       
    // androidQ       
    public Bitmap querySignImage(String filePath) {
        try {
            //  androidQ     
            String queryPathKey = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q ? MediaStore.Images.Media.RELATIVE_PATH : MediaStore.Images.Media.DATA;
            //       
            String selection = queryPathKey + "=? ";
            //   sql
            //Uri:      Uri
            //projection:      
            //selection:   where  
            //sortOrder:  
            Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[]{MediaStore.Images.Media._ID, queryPathKey, MediaStore.Images.Media.MIME_TYPE, MediaStore.Images.Media.DISPLAY_NAME},
                    selection,
                    new String[]{filePath},
                    null);
    
            //      
            if (cursor != null && cursor.moveToFirst()) {
                //            
                do {
                    //         
                    int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));//uri id,      
                    String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH));//       
                    String type = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));//    
                    String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));//    
                    //    id  uri,        uri
                    Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id);
                    //    :
                     Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
                    if (uri != null) {
                        //      bitmap  
                        InputStream inputStream = getContentResolver().openInputStream(uri);
                        return BitmapFactory.decodeStream(inputStream);
                    }
                } while (cursor.moveToNext());
            }
            if (cursor != null)
                cursor.close();
        } catch (Exception e) {
        }
        return null;
    }
    

    기타 작업
    추가/삭제/수정 작업: Content Resolver
    공식 문서에서도 공공 디렉터리를 조작하려면 Content Resolver를 사용하여 모든 첨삭과 수정을 진행해야 한다고 설명했다.
    Android 9(API 수준 28) 이하 버전에서는 외부 스토리지 디바이스에 저장된 모든 파일이 external라는 단일 볼륨에 표시됩니다.그러나 Android Q는 각 외부 스토리지 디바이스에 대해 고유한 볼륨 이름을 제공합니다.이 새 명명 시스템은 컨텐츠를 효율적으로 정리하고 인덱싱할 수 있으며 새 컨텐츠의 저장 위치를 제어할 수 있습니다.
    기본 공유 스토리지 디바이스는 항상 VOLUME_EXTERNAL_PRIMARY라고 합니다.호출 MediaStore.getExternalVolumeNames() 을 통해 다른 볼륨을 발견할 수 있습니다.
    특정 볼륨을 조회, 삽입, 업데이트 또는 삭제하려면 다음 코드 세그먼트와 같이 MediaStore API의 모든 방법getContentUri()에 볼륨 이름을 전달합니다.
    //Publish an audio file onto a specific external storage device. val values = ContentValues().apply { put(MediaStore.Audio.Media.RELATIVE_PATH, “Music/My Album/My Song”) put(MediaStore.Audio.Media.DISPLAY_NAME, “My Song.mp3”) }//Assumes that the storage device of interest is the 2nd one//that your app recognizes. val volumeNames = MediaStore.getExternalVolumeNames(context) val selectedVolumeName = volumeNames[1] val collection = MediaStore.Audio.Media.getContentUri(selectedVolumeName) val item = resolver.insert(collection, values)

    좋은 웹페이지 즐겨찾기