Summary of Android version and permission adaptation

  • 2021-12-13 09:11:50
  • OfStack

Directory request store permissions Version adaptation Before Android 7.0
After Android 7.0
Android 10.0
What is a scope
Take a chestnut

Request store permission

You first need to declare permissions in the AndroidManifest. xml file:


<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Request user rights in code:


    //  Authorization request code 
    private static final int PERMISSION_REQ_ID = 0;  
    //  Request permission 
    private static final String[] REQUESTED_PERMISSIONS = {
            Manifest.permission.READ_EXTERNAL_STORAGE
    };

    ...

     //  Determine whether there is storage permission 
    if (checkSelfPermission(REQUESTED_PERMISSIONS[0],PERMISSION_REQ_ID)){
        //YSE 
    }else {
        //NO
    }
    private boolean checkSelfPermission(String permissions,int requestCode){
        if (ContextCompat.checkSelfPermission(this,permissions) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
            return false;
        }
        return true;
    }

    //  Override this method to receive user authorization callbacks 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.i(TAG, "onRequestPermissionsResult: requestCode =" + requestCode
                +"\n,permissions =" + Arrays.toString(permissions)
                +"\n,grantResults =" + Arrays.toString(grantResults));
        if (requestCode == PERMISSION_REQ_ID){
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                // User consent permissions 
            }else {
                // User denies permission 
            }
        }
    

Version adaptation

Many changes have taken place from Android 6.0 to Android 10 to store/access files.

Before Android 7.0

Before Android 7.0, we can access files in memory and convert File into Uri object through Uri. fromFile, which represents the local real path. Access 1 picture:


String fileName = "default_Image.jpg";
File file = new File("file_path", fileName);
Uri uri = Uri.fromFile(file);

After Android 7.0

After 7.0, this Uri obtained through the real path is considered to be insecure, so a new solution is provided, that is, FileProvide is a special content provider, which uses a mechanism similar to content provider to protect data. Access 1 picture:


    File file = new File(CACHE_IMG, "file_name");
    Uri imageUri = FileProvider.getUriForFile(activity,"com.sandan.fileprovider", file); // Replace here uri The way to obtain 

However, the above is really good, for developers and this is good, but for users, the above is undoubtedly a rogue role, because developers can access all the locations in the memory and make some changes, resulting in the space in the SD card becoming very messy. Even if app is uninstalled, some junk files are still in the memory.

Android 10.0

In Android 10.0, in order to solve the above problems, Google added scope function in Android 10.0.

What is a scope

Android system has made great restrictions on SD card. Starting from Android 10.0, each program can only have the right to read and create corresponding files in the directory associated with its own external storage space, which is also called sandbox. The code to get this directory is: getExternalFilesDir (), and the associated directory path is roughly as follows:


 Html CSS JavaScript Vb vbs Asp PHP Perl Python Ruby C# C++ SQL Delphi Diff Groovy Java JavaFX ActionScript3 Bash/shell powershell Plain Text Scala XML Display language name   Show line number   Allow folding  

By putting the data in this directory, you can read and write files using the previous methods without making any changes or adaptations. However, the files in this folder will be deleted as the application uninstalls. What if you need to access other directories? For example, get the pictures in the photo album and add 1 picture to the photo album. For this reason, the Android system classifies the system file types: Pictures, audio, Video files can be accessed through MediaStore API. This is called shared space, Other system files need to be accessed using the system's file selector, In addition, if the program writes pictures, videos and audio to the media library, it will be automatically used for reading and writing permissions, and there is no need to apply for additional permissions. If you want to read pictures, videos and audio contributed by other programs to the media, you must apply for READ_EXTERNAL_STORAGE permissions, and WRITE_EXTERNAL_STORAGE permissions will be discarded in future versions.

Take a chestnut

Example: There is a local picture, add watermark to this picture and save it to the photo album.

Directly on the code:


    /**
     * 保存图片到相册
     *
     * @param context 上下文
     * @param text 水印文字
     */
    private void savePhotoAlbum(final Context context, final String text) {
        //这里开启子线程,防止堵塞。
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //从本地获取1张图片,转成Bitmap
                    Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_info);
                    //在沙箱中创建文件,名称:info.jpg
                    File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "info.jpg");
                    //判断文件是否存在,不存在创建文件。
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    // 向图片添加水印
                    Bitmap newBitmap = addInfoWatermark(context, bitmap, text);
                    // 更新相册
                    updatePhotoAlbum(context, newBitmap, file);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start(); //开始线程
    }

   /**
     * 保存到相册
     *
     * @param context 上下文
     * @param src  源图片
     * @param text 水印文字
     */
    private Bitmap addInfoWatermark(final Context context, Bitmap src, String text) {
        //判断图片/水印文字 是否为空
        if (isEmptyBitmap(src) || text == null ) {
            return null;
        }
        // 从源图片复制1份
        Bitmap ret = src.copy(src.getConfig(), true);
        // 初始化画笔
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
         // 初始化画布
        Canvas canvas = new Canvas(ret);
        // 水印文字:黑色
        paint.setColor(Color.BLACK);
        // 文字大小:19dp
        paint.setTextSize(dip2px(context, 19));
         // 开始绘画
        canvas.drawText(text, 10, 10 , paint);
        // 循环利用资源
        if (!src.isRecycled()) {
            src.recycle(); 
        }
        return ret;
    }

    /**
     * 保存到相册
     *
     * @param context 上下文
     * @param src  源图片
     * @param file 要保存到的文件
     */
    private void savePhotoAlbum(final Context context, Bitmap src, final File file) {
        //判断图片 是否为空
        if (isEmptyBitmap(src)) {
            return;
        }
        // 保存文件
        OutputStream outputStream;
        try {
            //输出这个文件
            outputStream = new BufferedOutputStream(new FileOutputStream(file));
            // 压缩
            src.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            // 循环利用资源
            if (!src.isRecycled()) {
                src.recycle();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 更新图库,这个在 Android 6.0 和 Android 10.0 更新图库,存在差异。
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  
            // Android 10.0 及以上
            // 创建 ContentValues 对象,准备插入数据
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
            values.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(file));
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
            ContentResolver contentResolver = context.getContentResolver();
            // 插入数据,返回所插入数据对应的Uri
            Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            if (uri == null) {
                return;
            }
            try {
                // 获取刚插入的数据的Uri对应的输出流
                outputStream = contentResolver.openOutputStream(uri);
                FileInputStream fileInputStream = new FileInputStream(file);
                // 从1个流复制到另1个流上
                FileUtils.copy(fileInputStream, outputStream);
                //关闭流
                fileInputStream.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // android 6.0 - 10.0
            // 扫描文件
            MediaScannerConnection.scanFile( context.getApplicationContext(), new String[]{file.getAbsolutePath()}, new String[]{"image/jpeg"},
                    new MediaScannerConnection.OnScanCompletedListener() {
                        @Override
                        public void onScanCompleted(String path, Uri uri) {
                            //通知相册更新
                            // 插入图片
                            MediaStore.Images.Media.insertImage( context.getContentResolver(),  BitmapFactory.decodeFile(path), file.getName(), null);
                            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                            Uri u = Uri.fromFile(file);
                            intent.setData(u);
                            context.sendBroadcast(intent);  // 发广播通知,更新相册
                        }
                    });
        }
    }

    /**
     * Bitmap对象是否为空。
     */
    private static boolean isEmptyBitmap(Bitmap src) {
        return src == null || src.getWidth() == 0 || src.getHeight() == 0;
    }

    /**
     * 获取 Mime 类型
     *
     * @param file 文件
     * @return Mime 类型
     */
    private static String getMimeType(File file) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String type = fileNameMap.getContentTypeFor(file.getName());
        return type;
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

The above is the Android version, permissions adaptation related summary of the details, more about Android version, permissions adaptation information please pay attention to other related articles on this site!


Related articles: