Android App-targetSDKVersion28升级为30

2023-12-13 05:07:54

为什么要修改targetSDKVersion?

1、应用开发平台要求(小米)
2、更好的兼容新版本的手机

有targetSDKVersion的位置:

  • App的targetSDKVersion
  • Module中的targetSDKVersion
  • 引入的第三方库中有targetSDKVersion

修改了App和Module中的targetSDKVersion。

存储权限和存储位置问题

修改原因:
Android10(targetSdkVersion29)以后加入了分区存储,针对App设置了外置私有存储空间,卸载App时会被删除,一定程度上解决了卸载App后外置内存仍占用问题。

Android10在获取存储权限上和之前版本有区别。

修改前:

  • targetSDKVersion28
  • 使用EasyPermission框架获取权限。(长时间不更新且不支持新版本Android)
  • 视频、音频、文件存储在外置存储卡【Environment.getExternalStorageDirectory()】中。

修改后:

  • targetSDKVersion30(Android 11)
  • 使用Permission替换EasyPermission获取权限
  • 视频、音频、文件存储存储在私有外置存储中【context.getExternalFilesDir()】。

修改内容:

  • 兼容Android不同版本获取权限。
  • App新版本要处理老版本存储在外置存储卡中的内容。

知识点:
使用创建、读写文件测试对Environment.getExternalStorageDirectory()和context.getExternalFilesDir()目录的使用。

Environment.getExternalStorageDirectory()

  • Android13:同Android11
  • Android11:
    • 动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后创建读写文件时显示permission denied。
    • 动态获取Manifest.permission.MANAGE_EXTERNAL_STORAGE权限后可以创建并读写文件,获取权限方式是通过Intent intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)跳转至设置页面,用户自行打开允许访问所有文件权限看后才可创建读写文件。
  • Android10:同Android11
  • Android9 动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后可以创建并读写文件。
  • Android6:动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后可以创建并读写文件。
  • Android5:不用获取权限可以创建并读写文件。

context.getExternalFilesDir();

  • Android13:不用获取权限可以创建并读写文件。
  • Android11:不用获取权限可以创建并读写文件。
  • Android10:不用获取权限可以创建并读写文件。
  • Android9:不用获取权限可以创建并读写文件。
  • Android6:不用获取权限可以创建并读写文件。
  • Android5:不用获取权限可以创建并读写文件。

修改方案:

  • 修改视频、音频和文件存储位置为外置私有存储空间且不需要获取权限。
  • 为兼容App旧版本存储在外置存储卡中的视频、音频和文件,在读取外置存储卡中的视频、音频和文件时需获取存储权限。

读取相册照片和视频

修改原因:

  • Android10之后读取相册的权限不同且方法也不同。

修改前:

  • 使用RxGalleryFinal框架(长时间不更新且不适配新版本Android)

修改后:

  • 使用PictureSelector框架(适配Android10以后版本-内置有获取权限功能)

保存图片至相册

修改原因:

  • Android10 添加至相册方式和位置不同。

修改后:

  • 兼容不同Android版本

处理方法:

  • Android9及之前版本,将图片保存至外置存储卡,然后通知将其添加到相册数据库,并刷新即可(需存储权限)。
  • Android10及以后版本,不能将图片放置在私有外置存储空间(ContentResolver不支持,会提示错误),将图片放在公共媒体存放位置,然后刷新,使用ContentResolver实现。

实现代码如下:

package com.ypkj.kfpt.http;


import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.widget.Toast;


import androidx.fragment.app.FragmentActivity;


import com.learn.base.permission.PermissionsUtil;
import com.ypkj.kfpt.R;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.UUID;


/**
 * 下载图片至相册的工具类
 */


public class DonwloadSaveImg {
    private static Context context;
    private static String filePath;
    private static Bitmap mBitmap;
    private static String mSaveMessage = "失败";
    private final static String TAG = "PictureActivity";


    public static void donwloadImg( Context contexts, String filePaths) {
        context = contexts;
        filePath = filePaths;
        PermissionsUtil.getInstance().requestStoragePerm((FragmentActivity) contexts, "", new PermissionsUtil.BaseRequestCallback() {
            @Override
            public void onCallBack() {
                new Thread(saveFileRunnable).start();
            }
        });
    }


    private static Runnable saveFileRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                if (!TextUtils.isEmpty(filePath)) { //网络图片
                    // 对资源链接
                    URL url = new URL(filePath);
                    //打开输入流
                    InputStream inputStream = url.openStream();
                    //对网上资源进行下载转换位图图片
                    mBitmap = BitmapFactory.decodeStream(inputStream);
                    inputStream.close();
                }
                saveBitmap(context, mBitmap);
                mSaveMessage = "图片保存成功!";
            } catch (IOException e) {
                mSaveMessage = "图片保存失败!";
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
            messageHandler.sendMessage(messageHandler.obtainMessage());
        }
    };


    private static Handler messageHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {


            Log.d(TAG, mSaveMessage);
            Toast.makeText(context, mSaveMessage, Toast.LENGTH_SHORT).show();
        }
    };




    // 保存bitmap到相册,并兼容AndroidQ
    public static boolean saveBitmap(Context context, Bitmap bitmap) {
        if (bitmap == null) {
            return false;
        }
        boolean isSaveSuccess;
        if (Build.VERSION.SDK_INT < 29) {
            isSaveSuccess = saveImageToGallery(context, bitmap);
        } else {
            isSaveSuccess = saveImageToGalleryQ(context, bitmap);
        }
        return isSaveSuccess;
    }


    /**
     * android 10 以下版本
     */
    private static boolean saveImageToGallery(Context context, Bitmap image) {
        // 首先保存图片
        String storePath = getBitmapFileDir();


        File appDir = new File(storePath);
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName = UUID.randomUUID().toString() + ".png";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            // 通过io流的方式来压缩保存图片
            boolean isSuccess = image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();


            // 保存图片后发送广播通知更新数据库
            Uri uri = Uri.fromFile(file);
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
            if (isSuccess) {
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * android 10 以上版本
     */
    private static boolean saveImageToGalleryQ(Context context, Bitmap image) {
        long mImageTime = System.currentTimeMillis();
        String mImageFileName = UUID.randomUUID().toString() + ".png";
        final ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, getBitmapFileDir());
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
        values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
        values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
        values.put(MediaStore.MediaColumns.IS_PENDING, 1);


        ContentResolver resolver = context.getContentResolver();
        final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        try {
            OutputStream out = resolver.openOutputStream(uri);
            if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
                return false;
            }
            values.clear();
            values.put(MediaStore.MediaColumns.IS_PENDING, 0);
            values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
            resolver.update(uri, values, null, null);
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                resolver.delete(uri, null);
            }
            return false;
        }
        return true;
    }


    public static String getBitmapFileDir() {
        String BITMAP_FILE_DIRECTORY = context.getString(R.string.appname);
        if (Build.VERSION.SDK_INT < 29) {
            // android 10 以下版本
            return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + BITMAP_FILE_DIRECTORY;
        } else {
            return Environment.DIRECTORY_PICTURES + File.separator + BITMAP_FILE_DIRECTORY;
        }
    }


}

参考文档:

  • 一篇文章搞定《Android权限问题(全版本)》https://blog.csdn.net/weixin_45112340/article/details/128905213
  • [ ]应用TargetSdkVersion 30升级适配指南 https://dev.mi.com/distribute/doc/details?pId=1738#_2

文章来源:https://blog.csdn.net/li1500742101/article/details/134835387
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。