文章目录
一、 图片质量压缩
二、 图片尺寸压缩
三、 Android 10 文件访问
四、 完整源码示例
上一篇博客 【Android 内存优化】图片文件压缩 ( Android 原生 API 提供的图片压缩功能能 | 图片质量压缩 | 图片尺寸压缩 ) 简要介绍了 图片文件压缩格式 , 以及 Android 提供的图片质量 , 尺寸压缩 API , 本博客中使用该 API 进行图片压缩 ;
一、 图片质量压缩
图片质量压缩步骤 :
① 创建输出流 : 创建一个文件输出流 , 也可是是网络输出流 ;
FileOutputStream fos = new FileOutputStream(path);
② 加载文件 : 从 Assets , 资源文件 , SD 卡 , 中 解码图片文件为内存中的 Bitmap 对象 ; 这里从资源文件中加载 ;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
③ 压缩图片 : 调用 Bitmap 对象的 compress 方法 , 压缩图片 ;
bitmap.compress(compressFormat, quality, fos);
二、 图片尺寸压缩
图片尺寸压缩流程 :
① 加载文件 : 从 Assets , 资源文件 , SD 卡 , 中解码图片文件为内存中的 Bitmap 对象 ; 这里从资源文件中加载 ;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
② 图片尺寸压缩 : 调用 Bitmap 对象的 createScaledBitmap 方法 , 将目标宽高作为参数传入 , 并使用双线性滤波器算法 , 该算法能大幅度提供压缩后的图片质量 , 并且开销较少 , 官方建议开启该算法 ;
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
三、 Android 10 文件访问
文件存储相关官方参考资料 :
Android 11 中的存储机制更新
Android storage use cases and best practices
应用数据和文件
将图片压缩后 , 存储到 SD 卡中 , 这里 涉及到了在 Android 10 系统中动态申请权限 , 设置旧的存储访问策略 ( 该策略将在 Android 11 中无效 ) ;
这里简要介绍暂时性的解决方案 ;
1. AndroidManifest.xml 中配置 SD 卡权限 , 及旧存储策略 :
① SD 卡权限 : 配置 SD 卡读写权限 ;
<!-- SD 卡访问权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
② 旧存储策略 : 配置在 application 标签中 , 特别注意该策略将在 Android 11 中废弃 ;
android:requestLegacyExternalStorage="true"
③ 完整配置 :
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="kim.hsl.pc"> <!-- SD 卡访问权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- android:requestLegacyExternalStorage="true" 配置旧存储策略 , Android 11 将禁止该功能 --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2 . 在 Activity 中动态申请权限 : 在 Activity 中调用 initPermissions(); 方法 , 即可动态申请 SD 卡访问权限 ;
/** * 需要获取的权限列表 */ private String[] permissions = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; /** * 动态申请权限的请求码 */ private static final int PERMISSION_REQUEST_CODE = 888; /** * 动态申请权限 */ @RequiresApi(api = Build.VERSION_CODES.M) private void initPermissions() { if (isLacksPermission()) { //动态申请权限 , 第二参数是请求吗 requestPermissions(permissions, PERMISSION_REQUEST_CODE); } } /** * 判断是否有 permissions 中的权限 * @return */ @RequiresApi(api = Build.VERSION_CODES.M) public boolean isLacksPermission() { for (String permission : permissions) { if(checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED){ return true; } } return false; }
执行完上述三个步骤的操作
配置权限
设置旧存储策略
动态申请权限
即可在 Android 10 中访问 SD 卡 , 如果在 Android 11 访问 , 查看章节开始的文档 ;
四、 完整源码示例
图片压缩源码示例 :
压缩质量 : 下图中的图片压缩都压缩成最低质量的图片 ; package kim.hsl.pc; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.Manifest; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.widget.TextView; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends AppCompatActivity { static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); // 初始化权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { initPermissions(); } // 将图片压缩成 JPEG 格式, 不缩放 compressBitmap(R.drawable.blog, Bitmap.CompressFormat.JPEG, 0, Environment.getExternalStorageDirectory() + "/blog_jpeg.jpeg", 0, 0); // 将图片压缩成 WEBP 格式 compressBitmap(R.drawable.blog, Bitmap.CompressFormat.WEBP, 0, Environment.getExternalStorageDirectory() + "/blog_webp.webp", 0, 0); // 将图片压缩成 PNG 格式 compressBitmap(R.drawable.blog, Bitmap.CompressFormat.PNG, 0, Environment.getExternalStorageDirectory() + "/blog_png.png", 0, 0); // 将图片宽高各压缩一半 compressBitmap(R.drawable.blog, Bitmap.CompressFormat.PNG, 0, Environment.getExternalStorageDirectory() + "/blog_png_half.png", 995, 510); } /** * 压缩图片, 并将压缩结果保存到指定文件 * @param resId 图片资源 * @param compressFormat 图片压缩格式 * @param quality 压缩质量 * @param path 文件保存路径 */ public void compressBitmap(int resId, Bitmap.CompressFormat compressFormat, int quality, String path, int width, int height){ // 从资源文件中加载一张图片 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId); // 如果传入的尺寸参数大于 0, 那么压缩尺寸 if(width > 0 && height > 0){ bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); } // 用于写出压缩后的图片到文件中 FileOutputStream fos = null; try { // 打开文件输出流 fos = new FileOutputStream(path); // 图片压缩操作 // 如果图片格式是 PNG 格式, 会忽略 质量 参数 bitmap.compress(compressFormat, quality, fos); } catch (FileNotFoundException e) { e.printStackTrace(); Log.i("TAG", "文件输出流打开失败"); }finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); Log.i("TAG", "文件输出流关闭失败"); } } } } public native String stringFromJNI(); /** * 需要获取的权限列表 */ private String[] permissions = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; /** * 动态申请权限的请求码 */ private static final int PERMISSION_REQUEST_CODE = 888; /** * 动态申请权限 */ @RequiresApi(api = Build.VERSION_CODES.M) private void initPermissions() { if (isLacksPermission()) { //动态申请权限 , 第二参数是请求吗 requestPermissions(permissions, PERMISSION_REQUEST_CODE); } } /** * 判断是否有 permissions 中的权限 * @return */ @RequiresApi(api = Build.VERSION_CODES.M) public boolean isLacksPermission() { for (String permission : permissions) { if(checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED){ return true; } } return false; } }
压缩结果分析 :
① 压缩后的 PNG 格式 : 2.63 MB ;
② 压缩后的 JPEG 格式 : 119 KB ;
③ 压缩后的 WEBP 格式图片 : 102 KB ;
④ 尺寸压缩图片 : 219 KB ;
压缩格式中 PNG > JPEG > WEBP 格式 ;
PNG 图片不能压缩 , 这里显示的大小是原图大小 , 非常大 ;