加固宝 适配android q,适配问题 - Android10(Q 29) 适配

在 Android10.0 以上通过绝对路径加载非沙盒目录下的本地图片不显示,需要通过文件 Uri 来访问。App 沙盒目录有两个位置 /sdcard/Android/data/包名/ 和 /data/data/包名/,这两个目录 App 可以直接通过绝对路径来访问,后面 App 卸载,这两个目录也会一起被删除。

//通过图片地址查询Media Uri

public static Uri getMediaUriFromPath(Context context, String path) {

Uri mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

Cursor cursor = context.getContentResolver().query(

mediaUri,

null,

MediaStore.Images.Media.DISPLAY_NAME + "= ?",

new String[] {path.substring(path.lastIndexOf("/") + 1)},

null);

Uri uri = null;

if(cursor.moveToFirst()) {

// 通过Media Id组装Uri,也可以通过Uri.withAppendedPath(mediaUri, id)这种方式组装

uri = ContentUris.withAppendedId(mediaUri,

cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)));

}

cursor.close();

return uri;

}

通过 Uri 来获取 Bitmap,通过 Bitmap 显示在 ImageView 上

@RequiresApi(api = Build.VERSION_CODES.N)

public static Bitmap getBitmapFromUri(Context context, Uri fileUri){

Bitmap bitmap = null;

ParcelFileDescriptor pfd = null;

try {

// 先获取ParcelFileDescriptor,第二个参数有 r: 读, w: 写,rw:读写

pfd = context.getContentResolver().openFileDescriptor(fileUri, "r");

} catch (FileNotFoundException e) {

e.printStackTrace();

}

try {

if(pfd != null){

// 然后通过BitmapFactory把ParcelFileDescriptor转换成Bitmap

bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(),null, null);

//旋转图片

int photoDegree = readPictureDegree(pfd.getFileDescriptor());

if(photoDegree != 0){

Matrix matrix = new Matrix();

matrix.postRotate(photoDegree);

// 创建新的图片

bitmap = Bitmap.createBitmap(bitmap, 0, 0,

bitmap.getWidth(), bitmap.getHeight(), matrix, true);

}

}

} finally{

if(pfd != null) {

try {

pfd.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

return bitmap;

}

// 通过FileDescriptor获取图片旋转角度

@RequiresApi(api = Build.VERSION_CODES.N)

public static int readPictureDegree(FileDescriptor fileDescriptor) {

int degree = 0;

try {

ExifInterface exifInterface = new ExifInterface(fileDescriptor);

int orientation = exifInterface.getAttributeInt(

ExifInterface.TAG_ORIENTATION,

ExifInterface.ORIENTATION_NORMAL);

switch (orientation) {

case ExifInterface.ORIENTATION_ROTATE_90:

degree = 90;

break;

case ExifInterface.ORIENTATION_ROTATE_180:

degree = 180;

break;

case ExifInterface.ORIENTATION_ROTATE_270:

degree = 270;

break;

}

} catch (IOException e) {

e.printStackTrace();

}

return degree;

}

压缩图片, 通过上面 getBitmapFromUri 方法获取 Bitmap, 再通过 FileInputStream 获取文件大小,看看是否需要压缩,然后进行尺寸压缩和质量压缩,最后把 bitmap 保存到 App 沙盒私有目录下,后面就可以直接通过地址获取文件。

private static int defaultImageMaxHeight = 1920; // 图片最大尺寸 1920*1080

private static int defaultImageMaxWidth = 1080;

private static int defaultImageMaxSize = 1024 * 1024; // 图片最大1M 单位Byte

// 10.0以上调用, 返回null 不压缩或压缩失败

@RequiresApi(api = Build.VERSION_CODES.Q)

private static String compress(Uri fileUri) {

Bitmap image = BitmapUtils.getBitmapFromUri(SSTApplication.getContext(), fileUri);

if (image == null)

return null;

long imageFileSize = 0L;

try {

ParcelFileDescriptor pfd = SSTApplication.getContext().getContentResolver().openFileDescriptor(fileUri, "r");

if (pfd != null) {

// 通过 FileInputStream.available()获取文件大小

FileInputStream fileInputStream = new FileInputStream(pfd.getFileDescriptor());

imageFileSize = fileInputStream.available();

fileInputStream.close();

}

// 1M 以下不压缩

if (imageFileSize <= defaultImageMaxSize) {

return null;

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

// 尺寸压缩

int ratio = getRatioSize(image.getWidth(),image.getHeight(), defaultImageMaxWidth, defaultImageMaxHeight);

// 压缩Bitmap到对应尺寸

Bitmap result = Bitmap.createBitmap(image.getWidth() / ratio,image.getHeight() / ratio, Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(result);

Rect rect = new Rect(0, 0, image.getWidth() / ratio, image.getHeight() / ratio);

canvas.drawBitmap(image,null,rect,null);

// 创建沙盒私有目录下压缩要保存的文件

File tempFile = new File(SSTApplication.getContext().getExternalCacheDir(), UUID.randomUUID().toString()+".jpg");

try {

//质量压缩

ByteArrayOutputStream baos = new ByteArrayOutputStream();

//这里100表示不压缩,把压缩后的数据存放到baos中

int quality = 100;

result.compress(Bitmap.CompressFormat.JPEG, quality, baos);

// 循环判断如果压缩后图片是否大于100kb, 大于继续压缩

while (baos.toByteArray().length > defaultImageMaxSize) {

// 重置baos即清空baos

baos.reset();

// 每次都减少10

quality -= 10;

// 这里压缩quality,把压缩后的数据存放到baos中

result.compress(Bitmap.CompressFormat.JPEG, quality, baos);

if (quality <= 10) {

break;

}

}

// 保存本地

FileOutputStream fos = new FileOutputStream(tempFile);

fos.write(baos.toByteArray());

fos.flush();

fos.close();

// 释放Bitmap

if (!result.isRecycled()) {

result.recycle();

}

return tempFile.getAbsolutePath();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

// 10.0 以下调用

private static String compress(String imagePath) {

Bitmap image = BitmapUtils.getBitmapFromFile(imagePath);

if (image == null)

return imagePath;

long imageFileSize = new File(imagePath).length();

if (imageFileSize <= defaultImageMaxSize) {

return imagePath;

}

// 尺寸压缩

int ratio = getRatioSize(image.getWidth(),image.getHeight(), defaultImageMaxWidth, defaultImageMaxHeight);

// 压缩Bitmap到对应尺寸

Bitmap result = Bitmap.createBitmap(image.getWidth() / ratio,image.getHeight() / ratio, Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(result);

Rect rect = new Rect(0, 0, image.getWidth() / ratio, image.getHeight() / ratio);

canvas.drawBitmap(image,null,rect,null);

String tempFile = SSTApplication.getContext().getFilesDir() + "/" + System.currentTimeMillis() + ".jpg";

try {

//质量压缩

ByteArrayOutputStream baos = new ByteArrayOutputStream();

//这里100表示不压缩,把压缩后的数据存放到baos中

int quality = 100;

result.compress(Bitmap.CompressFormat.JPEG, quality, baos);

// 循环判断如果压缩后图片是否大于100kb, 大于继续压缩

while (baos.toByteArray().length > defaultImageMaxSize) {

// 重置baos即清空baos

baos.reset();

// 每次都减少10

quality -= 10;

// 这里压缩quality,把压缩后的数据存放到baos中

result.compress(Bitmap.CompressFormat.JPEG, quality, baos);

if (quality <= 10) {

break;

}

}

// 保存本地

FileOutputStream fos = new FileOutputStream(tempFile);

fos.write(baos.toByteArray());

fos.flush();

fos.close();

// 释放Bitmap

if (!result.isRecycled()) {

result.recycle();

}

} catch (Exception e) {

e.printStackTrace();

return imagePath;

}

return tempFile;

}

// 获取尺寸压缩比

private static int getRatioSize(int bitWidth, int bitHeight, int maxWidth, int maxHeight) {

// 缩放比

int ratio = 1;

// 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可

if (bitWidth > bitHeight && bitWidth > maxWidth) {

// 如果图片宽度比高度大,以宽度为基准

ratio = bitWidth / maxWidth;

} else if (bitWidth < bitHeight && bitHeight > maxHeight) {

// 如果图片高度比宽度大,以高度为基准

ratio = bitHeight / maxHeight;

}

return ratio;

}

拍照保存图片, 先判断系统是否有相机功能,如果不判断缺少相机会直接报错,然后判断是否是 Android10以上,如果是则创建 Uri, 否就创建 File , 然后保存 全局变量 mCameraFile 和 mCameraUri,最后在 onActivityForResult 回调函数中拿这两个全局变量处理相应逻辑即可。

public void takePicture(Activity activity, int requestCode) {

boolean isAndroidQ = Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

// 检查是否有相机

if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {

Uri photoUri = null;

File photoFile = null;

if (isAndroidQ) {

// 适配android 10

photoUri = createImageUri(activity.getApplicationContext());

} else {

photoFile = createFile(activity.getApplicationContext());

if (photoFile != null) {

mCameraFile = photoFile;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

photoUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileProvider", photoFile);

} else {

photoUri = Uri.fromFile(photoFile);

}

}

}

if (photoUri != null) {

mCameraUri = photoUri;

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);

takePictureIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

activity.startActivityForResult(takePictureIntent, requestCode);

}

}

}

/**

* 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法

*/

private Uri createImageUri(Context context) {

String status = Environment.getExternalStorageState();

// 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储

if (status.equals(Environment.MEDIA_MOUNTED)) {

return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());

} else {

return context.getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());

}

}

/** 创建临时文件 */

public static File createFile(Context context) {

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA);

String filename = "IMG_" + dateFormat.format(new Date(System.currentTimeMillis())) + ".jpg";

File storageDir;

// 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

storageDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/");

} else {

storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);

}

if (storageDir!= null && !storageDir.exists()) {

storageDir.mkdir();

}

File tempFile = new File(storageDir, filename);

if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {

return null;

}

return tempFile;

}

Android10.0 上传图片,同样通过 Uri 将图片复制到 App 的沙盒目录下再上传.

public static File writeExternalCacheFile(Context context, Uri fileUri) throws IOException{

if (context == null) {

return null;

}

FileOutputStream outs = null;

FileInputStream ins = null;

File externalCacheFile = null;

try {

// 先获取ParcelFileDescriptor

ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(fileUri, "r");

if (pfd == null) {

return null;

}

// 然后通过这种方式创建文件

externalCacheFile = new File(context.getExternalCacheDir(), UUID.randomUUID().toString()+".jpg");

// 创建文件输出流,用于写入文件

outs = new FileOutputStream(externalCacheFile);

// 把ParcelFileDescriptor转成输入流,用于文件读取

ins = new FileInputStream(pfd.getFileDescriptor());

byte[] bytes = new byte[1024];

int index;

while ((index = ins.read(bytes)) != -1) {

// 循环获取文件流

outs.write(bytes,0 , index);

}

} finally {

if (outs != null) {

outs.close();

}

if (ins != null) {

ins.close();

}

}

return externalCacheFile;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值