【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 )
文章目录
上一篇博客 【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 文件访问
文件存储相关官方参考资料 :
将图片压缩后 , 存储到 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 图片不能压缩 , 这里显示的大小是原图大小 , 非常大 ;
相关文章
- 代码加密 android,Android 开发怎样做代码加密或混淆「建议收藏」
- Android Hook技术详解
- strictmode android,Android中的StrictMode
- Android adb install 命令安装apk
- android退出app代码,Android应用退出代码各种方式
- delphi android 音乐播放器,Mcool音乐播放器
- android vlc 中文字幕,解决Android版vlc中文乱码问题
- android 获取收到短信验证码,Android自动获取短信验证码
- android 获取屏幕分辨率_安卓系统分辨率设置
- android toast显示时间,Android Toast自定义显示时间「建议收藏」
- android应用程序_chrome Android
- Android中各种Exception错误小结
- Android开发学习笔记之 获得Android 可使用内存
- 【Android 应用开发】 Fragment 详解
- 【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )
- 【Android 异步操作】线程池 ( Worker 简介 | 线程池中的工作流程 runWorker | 从线程池任务队列中获取任务 getTask )
- 【Android 进程保活】应用进程拉活 ( JobScheduler 拉活 | JobScheduler 使用流程 | JobService 服务 | 不同版本兼容 | 源码资源 )
- 【Android APT】编译时技术 ( ButterKnife 原理分析 )
- 【Android 逆向】代码调试器开发 ( ptrace 函数 | 读取进程内存数据 )
- 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )
- 【Groovy】Android Studio 中创建 Groovy 工程 ( 创建 Android Studio 工程 | 创建并改造 Java 依赖库 | 编写 Groovy 代码并运行 )
- Android获取设备屏幕宽高pix值的两个方法详解手机开发
- [android] 优酷环形菜单-相对布局练习详解手机开发
- [android] 字符乱码问题的处理详解手机开发
- Android Framework层看硬件加速详解手机开发
- 安卓编年史(29):Android 5.0 Lollipop——有史以来最重要的安卓版本(3)
- 深入了解Linux命令ADB,提高android开发效率(linux命令adb)
- Android异步获取网络图片并处理导致内存溢出问题解决方法
- 解析Android开发优化之:对Bitmap的内存优化详解
- Android中使用ViewFlipper进行手势切换实例
- Android中自定义WindowTitle样式实例