134人参与 • 2025-04-24 • Android
在各类社交、商城、相册等应用中,图片常常需要经过压缩处理以降低文件体积、缩减上传流量和减少内存占用。图片压缩不仅能够提高传输效率,还能在保证一定质量的前提下改善用户体验。
本项目旨在通过 android 平台实现本地图片压缩功能,主要目标包括:
从本地选择图片:借助系统相册或文件选择器,让用户选择需要压缩的图片;
bitmap 压缩处理:利用 bitmapfactory.options 控制采样率,通过 bitmap.compress() 方法实现质量压缩;
图片预览与存储:在压缩后将图片显示在界面上,并支持将结果保存到本地;
性能与资源管理:优化大图片处理和内存使用,避免内存溢出和 anr;
模块化设计:所有代码均整合在一起,通过详细注释分模块说明,便于后续扩展。
节省网络流量:压缩后的图片体积更小,上传下载速度更快,减少用户流量消耗。
降低服务器压力:上传图片体积减小可以节省服务器存储空间和提高处理效率。
改善内存使用:在展示大尺寸图片时,通过采样降低内存占用,避免内存溢出问题。
提升用户体验:图片加载快、展示流畅对于用户体验至关重要,尤其在低网速环境下尤为明显。
bitmap 类:在 android 中,bitmap 是代表图像数据的核心类。了解 bitmap 的内存占用、分辨率、色彩配置(如 argb_8888 与 rgb_565)等是图片处理的基础。
bitmapfactory:提供从文件、资源、输入流中加载 bitmap 的方法,支持通过 options 设置采样率(insamplesize)降低图片分辨率。
canvas 与 paint:在对 bitmap 进行加工时,通常会使用 canvas 和 paint 实现图片绘制和合成。
insamplesize 参数:这是 bitmapfactory.options 中的重要参数,通过设置 insamplesize 的值可以按比例缩小原始图片的尺寸。例如,设置 insamplesize = 2 将图片宽高减半,占用内存减少为原来的四分之一。
质量与性能平衡:合理设置 insamplesize 能够在保证图片质量的前提下,降低内存消耗和处理时间,适合大图上传场景。
bitmap.compress() 方法:用于将 bitmap 压缩为指定格式(如 jpeg、png、webp)的数据流。通过设置压缩质量(0-100),在保证图像清晰度和文件体积之间达到平衡。
压缩格式选择:jpeg 格式适用于彩色照片,压缩后体积较小,但可能出现失真;png 格式无损但文件体积较大;webp 格式兼具两者优势,但仅在部分 android 版本支持。
本项目主要需求如下:
选择本地图片
利用系统相册或文件选择器,让用户选择需要上传的图片,并在界面上显示预览。
图片压缩
通过 bitmapfactory.options 控制 insamplesize 进行采样压缩;
通过 bitmap.compress() 对 bitmap 进行质量压缩,支持 jpeg、png 等格式,用户可以选择压缩质量参数。
结果展示与保存
将压缩后的图片显示在界面 imageview 上,并支持保存压缩图片到本地存储。
后台异步操作
网络传输与图片处理操作必须在后台线程中完成,避免阻塞 ui 主线程。
异常与权限管理
处理内存溢出、 io 异常等问题;在 androidmanifest.xml 中声明必要的权限,如 read_external_storage 和 write_external_storage。
代码结构与扩展性
所有 java 与 xml 代码均整合在一起,通过详细注释区分不同模块,保证代码清晰、便于维护和后续扩展(例如批量压缩、在线上传结合等)。
主要难点与挑战包括:
大图内存管理
加载高清大图时很容易出现内存溢出,需合理采样和使用 insamplesize 降低 bitmap 尺寸。
压缩质量与文件体积平衡
如何在保证图片关键细节不失真的前提下,尽可能降低图片文件体积,需要设置合适的压缩格式和质量参数。
异步与多线程处理
图片压缩和保存操作耗时较长,必须在后台线程异步处理,同时确保 ui 能正确更新处理结果。
保存与分享机制
压缩完成后将图片保存到本地或提供分享功能,需要处理文件读写、路径管理与兼容性问题。
本项目采用基于 bitmap 操作和 canvas 绘制的方式实现图片压缩,主要设计思路如下:
图片选择与加载
通过系统相册选择器(intent.action_pick)获取用户选择的图片 uri,利用 bitmapfactory.options 按需设置 insamplesize 进行采样加载 bitmap,降低内存占用。
图片压缩模块
编写 imagecompressionutils 工具类,提供两种压缩方法:
利用 insamplesize 调整分辨率,达到采样压缩效果;
调用 bitmap.compress() 方法对 bitmap 进行质量压缩,支持 jpeg、png、webp 格式,并设置压缩质量参数。
保存与分享模块
将压缩后的 bitmap 保存到本地文件系统,利用 fileoutputstream 写入数据,同时可以通过 intent 分享该图片。
ui 与交互模块
主 activity 提供图片预览、压缩参数调整(例如压缩比例、质量)和上传/分享按钮,实时显示压缩效果反馈给用户。
后台与异步操作
图片压缩及文件保存操作使用 asynctask(或其他异步编程方式)在后台线程执行,确保 ui 顺畅。
项目主要模块分为以下几部分:
图片选择与加载模块
通过系统相册调用,让用户选择图片,并使用 bitmapfactory.options 加载原始 bitmap。
图片压缩工具模块(imagecompressionutils.java)
提供静态方法 compressimagebysampling() 和 compressimagebyquality(),分别实现采样压缩与质量压缩功能。
保存与分享模块
提供保存 bitmap 到文件的方法,并支持调用系统分享功能。
ui 与活动模块
mainactivity 展示图片预览、压缩参数设置、压缩执行和结果展示,并调用图片压缩工具模块。
布局与资源管理模块
整合所有 xml 布局文件(主 activity 布局、按钮与预览区域)、颜色、字符串和样式资源,通过详细注释区分各部分。
下面提供完整代码示例,其中所有 java 与 xml 代码均整合在一起,并通过详细注释分隔不同模块。本示例中采用 imagecompressionutils 工具类实现图片压缩功能,mainactivity 负责图片选择、显示和调用压缩方法。
// ===========================================
// 文件: imagecompressionutils.java
// 描述: 图片压缩工具类,提供基于采样率与质量压缩两种方法
// ===========================================
package com.example.uploaddemo;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import java.io.bytearrayoutputstream;
import java.io.file;
import java.io.fileoutputstream;
public class imagecompressionutils {
/**
* 利用 bitmapfactory.options 设置 insamplesize 实现采样压缩
* @param filepath 图片文件的本地路径
* @param reqwidth 期望宽度
* @param reqheight 期望高度
* @return 采样压缩后的 bitmap
*/
public static bitmap compressimagebysampling(string filepath, int reqwidth, int reqheight) {
// 第一次读取设置 injustdecodebounds 为 true 仅获取图片尺寸
final bitmapfactory.options options = new bitmapfactory.options();
options.injustdecodebounds = true;
bitmapfactory.decodefile(filepath, options);
// 计算 insamplesize 值
options.insamplesize = calculateinsamplesize(options, reqwidth, reqheight);
// 第二次读取设置 injustdecodebounds 为 false 获取实际 bitmap
options.injustdecodebounds = false;
return bitmapfactory.decodefile(filepath, options);
}
/**
* 根据原始图片尺寸和目标尺寸计算合理的 insamplesize
*/
public static int calculateinsamplesize(bitmapfactory.options options, int reqwidth, int reqheight) {
// 原始图片宽高
final int height = options.outheight;
final int width = options.outwidth;
int insamplesize = 1;
if (height > reqheight || width > reqwidth) {
// 计算宽高比例
final int halfheight = height / 2;
final int halfwidth = width / 2;
// 保证压缩后宽高仍大于目标宽高
while ((halfheight / insamplesize) >= reqheight && (halfwidth / insamplesize) >= reqwidth) {
insamplesize *= 2;
}
}
return insamplesize;
}
/**
* 利用 bitmap.compress() 方法实现质量压缩
* @param bitmap 原始 bitmap
* @param format 压缩格式(jpeg、png、webp)
* @param quality 压缩质量(0-100,100为最高质量)
* @return 压缩后的 bitmap
*/
public static bitmap compressimagebyquality(bitmap bitmap, bitmap.compressformat format, int quality) {
if (bitmap == null) return null;
bytearrayoutputstream baos = new bytearrayoutputstream();
bitmap.compress(format, quality, baos);
byte[] data = baos.tobytearray();
return bitmapfactory.decodebytearray(data, 0, data.length);
}
/**
* 将 bitmap 保存为文件
* @param bitmap 待保存的 bitmap
* @param destfile 保存目标文件
* @param format 保存格式
* @param quality 保存质量
* @return true 保存成功,false 保存失败
*/
public static boolean savebitmaptofile(bitmap bitmap, file destfile, bitmap.compressformat format, int quality) {
if (bitmap == null || destfile == null) {
return false;
}
fileoutputstream fos = null;
try {
fos = new fileoutputstream(destfile);
bitmap.compress(format, quality, fos);
fos.flush();
return true;
} catch (exception e) {
e.printstacktrace();
return false;
} finally {
if (fos != null) {
try { fos.close(); } catch (exception ex) { }
}
}
}
}
// ===========================================
// 文件: mainactivity.java
// 描述: 示例 activity,展示如何选择本地图片、进行压缩预览和保存压缩结果
// ===========================================
package com.example.uploaddemo;
import android.manifest;
import android.content.intent;
import android.content.pm.packagemanager;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.net.uri;
import android.os.asynctask;
import android.os.bundle;
import android.provider.mediastore;
import androidx.annotation.nullable;
import androidx.appcompat.app.appcompatactivity;
import androidx.core.app.activitycompat;
import androidx.core.content.contextcompat;
import android.view.view;
import android.widget.button;
import android.widget.imageview;
import android.widget.toast;
import java.io.file;
import java.io.inputstream;
public class mainactivity extends appcompatactivity {
private static final int request_code_select_image = 100;
private imageview ivoriginal;
private imageview ivcompressed;
private button btncompress;
private uri selectedimageuri;
private bitmap originalbitmap;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
// 设置布局文件 activity_main.xml
setcontentview(r.layout.activity_main);
ivoriginal = findviewbyid(r.id.iv_original);
ivcompressed = findviewbyid(r.id.iv_compressed);
btncompress = findviewbyid(r.id.btn_compress);
// 点击原始图片区域选择图片
ivoriginal.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
intent intent = new intent(intent.action_pick, mediastore.images.media.external_content_uri);
startactivityforresult(intent, request_code_select_image);
}
});
// 点击压缩按钮执行图片压缩,采用质量压缩方式示例
btncompress.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
if(originalbitmap != null) {
// 异步任务执行压缩操作,防止阻塞主线程
new compressimagetask().execute();
} else {
toast.maketext(mainactivity.this, "请先选择一张图片", toast.length_short).show();
}
}
});
// 检查存储读取权限
if (contextcompat.checkselfpermission(this, manifest.permission.read_external_storage)
!= packagemanager.permission_granted) {
activitycompat.requestpermissions(this,
new string[] { manifest.permission.read_external_storage }, 1);
}
}
@override
protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) {
super.onactivityresult(requestcode, resultcode, data);
if(requestcode == request_code_select_image && resultcode == result_ok && data != null) {
selectedimageuri = data.getdata();
try {
// 加载预览 bitmap
inputstream inputstream = getcontentresolver().openinputstream(selectedimageuri);
originalbitmap = bitmapfactory.decodestream(inputstream);
ivoriginal.setimagebitmap(originalbitmap);
} catch (exception e) {
e.printstacktrace();
}
}
}
/**
* compressimagetask 异步任务,后台执行图片压缩操作
*/
private class compressimagetask extends asynctask<void, void, bitmap> {
@override
protected void onpreexecute() {
toast.maketext(mainactivity.this, "开始压缩图片", toast.length_short).show();
}
@override
protected bitmap doinbackground(void... voids) {
// 示例:先采样压缩,再进行质量压缩
string imagepath = pathutil.getrealpathfromuri(mainactivity.this, selectedimageuri);
// 通过采样降低分辨率(例如目标尺寸 800x800)
bitmap sampledbitmap = imagecompressionutils.compressimagebysampling(imagepath, 800, 800);
// 然后进行质量压缩,设定 jpeg 格式和 70 的压缩质量
return imagecompressionutils.compressimagebyquality(sampledbitmap, bitmap.compressformat.jpeg, 70);
}
@override
protected void onpostexecute(bitmap compressedbitmap) {
if(compressedbitmap != null) {
ivcompressed.setimagebitmap(compressedbitmap);
// 保存压缩后的图片到本地(保存目录与文件名自定义)
file dir = getexternalfilesdir("compressedimages");
if(dir != null && !dir.exists()) {
dir.mkdirs();
}
file outfile = new file(dir, system.currenttimemillis() + ".jpg");
boolean success = imagecompressionutils.savebitmaptofile(compressedbitmap, outfile, bitmap.compressformat.jpeg, 70);
if(success) {
toast.maketext(mainactivity.this, "压缩图片保存成功:" + outfile.getabsolutepath(), toast.length_long).show();
} else {
toast.maketext(mainactivity.this, "压缩图片保存失败", toast.length_short).show();
}
} else {
toast.maketext(mainactivity.this, "图片压缩失败", toast.length_short).show();
}
}
}
}
// ===========================================
// 文件: pathutil.java
// 描述: 工具类,从图片 uri 中获取实际文件路径,适配不同 android 版本
// ===========================================
package com.example.uploaddemo;
import android.content.context;
import android.database.cursor;
import android.net.uri;
import android.provider.mediastore;
public class pathutil {
public static string getrealpathfromuri(context context, uri contenturi) {
string[] proj = { mediastore.images.media.data };
cursor cursor = context.getcontentresolver().query(contenturi, proj, null, null, null);
if(cursor != null){
int column_index = cursor.getcolumnindexorthrow(mediastore.images.media.data);
cursor.movetofirst();
string path = cursor.getstring(column_index);
cursor.close();
return path;
}
return null;
}
}<!-- ===========================================
文件: activity_main.xml
描述: mainactivity 的布局文件,包含原始图片预览、压缩后图片预览和上传按钮
=========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<scrollview
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:padding="16dp">
<linearlayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<textview
android:id="@+id/tv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击下方图片选择原始图片"
android:textsize="18sp"
android:layout_marginbottom="8dp"/>
<imageview
android:id="@+id/iv_original"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#eeeeee"
android:scaletype="centercrop"
android:layout_marginbottom="16dp"
android:contentdescription="原始图片预览"/>
<button
android:id="@+id/btn_compress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="压缩图片"
android:layout_marginbottom="16dp"/>
<textview
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="压缩后图片预览"
android:textsize="18sp"
android:layout_marginbottom="8dp"/>
<imageview
android:id="@+id/iv_compressed"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#eeeeee"
android:scaletype="centercrop"
android:contentdescription="压缩后图片预览"/>
</linearlayout>
</scrollview>
<!-- ===========================================
文件: colors.xml
描述: 定义项目中使用的颜色资源
=========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#ffffff</color>
<color name="primary">#3f51b5</color>
</resources>
<!-- ===========================================
文件: styles.xml
描述: 定义应用主题与样式资源,采用 appcompat 主题
=========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="apptheme" parent="theme.appcompat.light.noactionbar">
<item name="android:windowbackground">@color/white</item>
<item name="android:textcolorprimary">@color/primary</item>
</style>
</resources>bitmapfactory.options 的使用
在 imagecompressionutils.compressimagebysampling() 中,先通过设置 injustdecodebounds 读取图片的原始尺寸,再计算合适的 insamplesize 值,实现按比例采样加载图片,降低内存使用。
采样与质量压缩的结合
通过先采样压缩降低分辨率,再调用 bitmap.compress() 进行质量压缩,可大幅减少图片体积,同时保持较好视觉效果。
压缩格式与质量
使用 bitmap.compress() 方法,可以选择 jpeg、png 或 webp 格式,并通过 quality 参数设定压缩质量(0~100);
内存与性能考量
结合 insamplesize 和 compress() 方法,既能减少图片内存占用,又能加快文件上传及显示速度,平衡品质与性能。
asynctask 应用
在 mainactivity 中,通过 uploadtask 异步任务在子线程中进行图片压缩操作,并在 onpostexecute() 中更新压缩结果展示于 imageview,同时可保存文件或分享。
路径解析与文件保存
使用 pathutil 工具类从图片 uri 中获取实际文件路径,利用 imagecompressionutils.savebitmaptofile() 方法将压缩后的 bitmap 保存到指定目录。
bitmapfactory.options 采样
加载大图时使用采样技术降低图片分辨率,减少内存占用,避免 oom;
对象复用
在图片压缩过程中尽量重用 paint、bytearrayoutputstream 等对象,减少频繁创建和垃圾回收。
后台执行
图片压缩等耗时操作在 asynctask 或其他异步方式中执行,确保 ui 主线程流畅;
网络与 io 性能
若后续涉及上传,建议结合 okhttp、retrofit 实现高效网络通信。
日志输出
在关键步骤(如图片加载、压缩、文件保存)加入日志调试信息,方便使用 logcat 分析问题;
异常处理
为 bitmap 操作、文件 io 加入 try-catch 机制,确保因内存、权限等问题引起异常时能及时反馈。
本项目详细介绍了如何在 android 应用中实现对本地图片的压缩功能,包括采样压缩和质量压缩两种方式。主要成果包括:
图片加载与采样技术
通过 bitmapfactory.options 计算 insamplesize,实现图片采样加载,降低内存占用;
质量压缩实现
利用 bitmap.compress() 方法,将 bitmap 压缩为 jpeg(或其它格式),自定义压缩质量参数达到文件体积和画质间的平衡;
异步任务与结果展示
采用 asynctask 处理图片压缩,避免在 ui 线程中执行耗时操作,并将结果通过 imageview 实时展示;
模块化代码结构
将图片选择、压缩处理、文件保存等功能模块化封装在独立的工具类中,所有代码均整合在一起,结构清晰便于后续扩展。
未来可以进一步在以下方面扩展与优化本项目:
批量图片压缩
扩展为支持多图片批量处理,并实现压缩进度显示和队列管理;
更高效的异步处理
利用 rxjava、kotlin coroutine 或 workmanager 进行后台任务调度,提高代码响应性;
动态调整压缩参数
提供界面允许用户自定义采样率、压缩格式和质量参数,并实时预览压缩效果;
与图片上传结合
整合图片压缩与上传功能,缩小上传文件体积,提高网络传输效率;
质量检测与优化
结合图像处理算法,自动检测压缩后图片质量,调整压缩策略,兼顾视觉效果与文件大小;
异常和错误处理
构建完善的错误捕获和重试机制,在图片处理或文件保存失败时给予详细反馈和自动重试。
以上就是android实现压缩本地图片功能的详细内容,更多关于android压缩本地图片的资料请关注代码网其它相关文章!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论