@@ -218,7 +218,7 @@ public class BackgroundSourceUtils {
clearCropTempFiles ( ) ;
LogUtils . d ( TAG , " 【文件初始化】完成。 " ) ;
}
// 【核心实现】定义 getFileProviderUri 方法:将 File 转为 ContentUri( 适配 FileProvider)
public Uri getFileProviderUri ( File file ) {
Log . d ( " BackgroundSourceUtils " , " getFileProviderUri: 生成FileProvider Uri, 文件路径: " + file . getAbsolutePath ( ) ) ;
@@ -245,13 +245,16 @@ public class BackgroundSourceUtils {
return contentUri ;
}
public boolean createCropFileProviderBackgroundBean ( Uri uri ) {
/*
* 创建预览数据剪裁环境
*/
public boolean createAndUpdatePreviewEnvironmentForCropping ( BackgroundBean oldPreviewBackgroundBean ) {
InputStream is = null ;
FileOutputStream fos = null ;
loadSettings ( ) ;
try {
clearCropTempFiles ( ) ;
Uri uri = UriUtil . getUriForFile ( mContext , oldPreviewBackgroundBean . getBackgroundFilePath ( ) ) ;
//String szType = mContext.getContentResolver().getType(uri);
// 2. 截取MIME类型后缀( 如从image/jpeg中提取jpeg) 【核心新增逻辑】
String fileSuffix = FileUtils . getFileSuffix ( mContext , uri ) ;
@@ -259,51 +262,63 @@ public class BackgroundSourceUtils {
mCropSourceFile = new File ( fCropCacheDir , newCropFileName + " . " + fileSuffix ) ;
mCropResultFile = new File ( fCropCacheDir , " SelectCompress_ " + newCropFileName + " . " + fileSuffix ) ;
mCropSourceFile . createNewFile ( ) ;
mCropResultFile . createNewFile ( ) ;
// 1. 打开Uri输入流( 兼容content:///file:// 等多种Uri格式)
is = mContext . getContentResolver ( ) . openInputStream ( uri ) ;
if ( is = = null ) {
LogUtils . e ( TAG , " 【选图解析】ContentResolver打开Uri失败, Uri: " + uri . toString ( ) ) ;
return false ;
if ( FileUtils . isFileExists ( oldPreviewBackgroundBean . getBackgroundScaledCompressFilePath ( ) ) ) {
FileUtils . copyFile ( new File ( oldPreviewBackgroundBean . getBackgroundScaledCompressFilePath ( ) ) , mCropResultFile ) ;
} else {
mCropResultFile . createNewFile ( ) ;
}
// 2. 初始化选图临时文件输出流( Java7 手动创建流, 不依赖try-with-resources)
fos = new FileOutputStream ( mCropSourceFile ) ;
byte [ ] buffer = new byte [ 1024 * 8 ] ; // 8KB缓冲区, 平衡读写性能与内存占用
int readLen ; // 每次读取的字节长度
if ( FileUtils . isFileExists ( oldPreviewBackgroundBean . getBackgroundFilePath ( ) ) ) {
FileUtils . copyFile ( new File ( oldPreviewBackgroundBean . getBackgroundFilePath ( ) ) , mCropSourceFile ) ;
} else {
mCropSourceFile . createNewFile ( ) ;
// 1. 打开Uri输入流( 兼容content:///file:// 等多种Uri格式)
is = mContext . getContentResolver ( ) . openInputStream ( uri ) ;
if ( is = = null ) {
LogUtils . e ( TAG , " 【选图解析】ContentResolver打开Uri失败, Uri: " + uri . toString ( ) ) ;
return false ;
}
// 3 . 流复制( Java7 标准while循环, 避免Java8+语法 )
while ( ( readLen = is . read ( buffer ) ) ! = - 1 ) {
fos . write ( buffer , 0 , readLen ) ; // 精准写入读取到的字节,避免空字节填充
}
// 2 . 初始化选图临时文件输出流( Java7 手动创建流, 不依赖try-with-resources )
fos = new FileOutputSt ream ( mCropSourceFile ) ;
byte [ ] buffer = new byte [ 1024 * 8 ] ; // 8KB缓冲区, 平衡读写性能与内存占用
int readLen ; // 每次读取的字节长度
// 4 . 强制同步写入磁盘( 解决Android 10+ 异步写入导致的文件无效问题 )
fos . flush ( ) ;
if ( fos ! = null ) {
try {
fos . getFD ( ) . sync ( ) ; // 确保数据写入物理磁盘,而非缓存
} catch ( IOException e ) {
LogUtils . w ( TAG , " 【选图解析】文件同步到磁盘失败, 用flush()兜底: " + e . getMessage ( ) ) ;
fos . flush ( ) ;
// 3 . 流复制( Java7 标准while循环, 避免Java8+语法 )
while ( ( readLen = is . read ( buffer ) ) ! = - 1 ) {
fos . write ( buffer , 0 , readLen ) ; // 精准写入读取到的字节,避免空字节填充
}
// 4. 强制同步写入磁盘( 解决Android 10+ 异步写入导致的文件无效问题)
fos . flush ( ) ;
if ( fos ! = null ) {
try {
fos . getFD ( ) . sync ( ) ; // 确保数据写入物理磁盘,而非缓存
} catch ( IOException e ) {
LogUtils . w ( TAG , " 【选图解析】文件同步到磁盘失败, 用flush()兜底: " + e . getMessage ( ) ) ;
fos . flush ( ) ;
}
}
}
// 加载图片数据模型数据
loadSettings ( ) ;
// 修改预览数据模型
previewBackgroundBean . setBackgroundFileName ( mCropSourceFile . getName ( ) ) ;
previewBackgroundBean . setBackgroundFilePath ( mCropSourceFile . getAbsolutePath ( ) ) ;
previewBackgroundBean . setBackgroundScaledCompressFileName ( mCropResultFile . getName ( ) ) ;
previewBackgroundBean . setBackgroundScaledCompressFilePath ( mCropResultFile . getAbsolutePath ( ) ) ;
// 保存数据模型数据更改
saveSettings ( ) ;
// 6. 解析成功日志(打印文件信息,便于问题排查)
LogUtils . d ( TAG , " 【选图解析】Uri解析成功! " ) ;
LogUtils . d ( TAG , " → 原Uri: " + uri . toString ( ) ) ;
LogUtils . d ( TAG , " → 目标临时文件 : " + mCropSourceFile . getAbsolutePath ( ) ) ;
LogUtils . d ( TAG , " → 目标临时 文件大小: " + mCropSourceFile . length ( ) + " bytes " ) ;
LogUtils . d ( TAG , " → 目标剪裁临时 文件: " + mCropResultFile . getAbsolutePath ( ) ) ;
LogUtils . d ( TAG , " → 目标剪裁临时 文件大小: " + mCropResultFile . length ( ) + " bytes " ) ;
LogUtils . d ( TAG , " → 图片剪裁数据源 : " + mCropSourceFile . getAbsolutePath ( ) ) ;
LogUtils . d ( TAG , " → 图片剪裁数据源 文件大小: " + mCropSourceFile . length ( ) + " bytes " ) ;
LogUtils . d ( TAG , " → 剪裁结果数据 文件: " + mCropResultFile . getAbsolutePath ( ) ) ;
LogUtils . d ( TAG , " → 剪裁结果数据 文件大小: " + mCropResultFile . length ( ) + " bytes " ) ;
return true ;
} catch ( Exception e ) {
@@ -424,7 +439,7 @@ public class BackgroundSourceUtils {
* 保存配置( 核心: 将两份独立Bean实例, 分别写入各自的JSON文件)
*/
public void saveSettings ( ) {
if ( currentBackgroundBean ! = null & & previewBackgroundBean ! = null ) {
if ( currentBackgroundBean ! = null & & previewBackgroundBean ! = null ) {
BackgroundBean . saveBeanToFile ( currentBackgroundBeanFile . getAbsolutePath ( ) , currentBackgroundBean ) ; // 正式Bean→正式JSON
BackgroundBean . saveBeanToFile ( previewBackgroundBeanFile . getAbsolutePath ( ) , previewBackgroundBean ) ; // 预览Bean→预览JSON
LogUtils . d ( TAG , " 【配置管理】两份配置保存成功: 正式JSON= " + currentBackgroundBeanFile . getAbsolutePath ( ) + " , 预览JSON=" + previewBackgroundBeanFile . getAbsolutePath ( ) ) ;
@@ -531,12 +546,12 @@ public class BackgroundSourceUtils {
// 更新当前背景文件路径
currentBackgroundBean . setBackgroundFilePath ( currentFile . getAbsolutePath ( ) ) ; // 原图路径( BackgroundSource)
currentBackgroundBean . setBackgroundScaledCompressFilePath ( currentCropFile . getAbsolutePath ( ) ) ; // 压缩图路径( BackgroundCrops)
saveSettings ( ) ; // 分别保存: 正式Bean→currentJSON, 预览Bean→previewJSON( 两份独立)
LogUtils . d ( TAG , " 【配置管理】预览背景深拷贝到正式Bean: 两份实例独立, 压缩图统一存储到BackgroundCrops " ) ;
ToastUtils . show ( " 背景图片应用成功 " ) ;
}
/**
* 将正式背景同步到预览背景( 正式Bean → 预览Bean: 深拷贝, 新建预览Bean实例+逐字段拷贝)
* 核心: 深拷贝后, 修改预览Bean不会影响正式Bean, 两份实例完全独立, 压缩图路径统一指向BackgroundCrops