420 lines
13 KiB
Plaintext
420 lines
13 KiB
Plaintext
# Glide 图片加载错误处理方案
|
||
|
||
## 错误信息
|
||
```
|
||
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279)
|
||
```
|
||
|
||
## 问题分析
|
||
|
||
### 可能原因
|
||
1. **图片格式不支持或损坏**
|
||
- 图片文件损坏
|
||
- 不支持的图片格式
|
||
- 图片编码异常
|
||
|
||
2. **内存不足**
|
||
- 加载大图片时内存溢出
|
||
- 同时加载多张图片导致内存压力
|
||
|
||
3. **网络问题**
|
||
- 网络图片下载不完整
|
||
- 响应数据格式错误
|
||
|
||
4. **Glide配置问题**
|
||
- 缺少ProGuard规则
|
||
- Glide版本兼容性问题
|
||
|
||
5. **Context问题**
|
||
- Activity/Fragment已销毁但仍在加载
|
||
- Context引用导致内存泄漏
|
||
|
||
## 解决方案
|
||
|
||
### 方案一:添加错误处理和占位图(推荐)
|
||
|
||
#### 1. 优化 GlideEngine.java
|
||
|
||
在 `app/src/main/java/tools/GlideEngine.java` 中添加错误处理:
|
||
|
||
```java
|
||
@Override
|
||
public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
|
||
Glide.with(context)
|
||
.load(url)
|
||
.placeholder(R.drawable.picture_image_placeholder) // 添加占位图
|
||
.error(R.drawable.picture_image_placeholder) // 添加错误占位图
|
||
.fallback(R.drawable.picture_image_placeholder) // 添加空URL占位图
|
||
.skipMemoryCache(false) // 使用内存缓存
|
||
.diskCacheStrategy(DiskCacheStrategy.ALL) // 使用磁盘缓存
|
||
.listener(new RequestListener<Drawable>() {
|
||
@Override
|
||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||
Target<Drawable> target, boolean isFirstResource) {
|
||
// 记录错误日志
|
||
if (e != null) {
|
||
Log.e("Glide", "图片加载失败: " + url, e);
|
||
}
|
||
return false; // 返回false让Glide显示error占位图
|
||
}
|
||
|
||
@Override
|
||
public boolean onResourceReady(Drawable resource, Object model,
|
||
Target<Drawable> target, DataSource dataSource,
|
||
boolean isFirstResource) {
|
||
return false;
|
||
}
|
||
})
|
||
.into(imageView);
|
||
}
|
||
```
|
||
|
||
#### 2. 优化 NineImageLoader.java
|
||
|
||
在 `app/src/main/java/utils/NineImageLoader.java` 中:
|
||
|
||
```java
|
||
@Override
|
||
public void onDisplayImage(Context context, ImageView imageView, String url) {
|
||
RequestOptions requestOptions = new RequestOptions();
|
||
requestOptions.placeholder(R.mipmap.icon_default_rectangle);
|
||
requestOptions.error(R.mipmap.icon_default_rectangle);
|
||
requestOptions.skipMemoryCache(false);
|
||
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||
|
||
// 添加错误处理
|
||
Glide.with(context)
|
||
.load(url)
|
||
.apply(requestOptions)
|
||
.listener(new RequestListener<Drawable>() {
|
||
@Override
|
||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||
Target<Drawable> target, boolean isFirstResource) {
|
||
if (e != null) {
|
||
Log.e("NineImageLoader", "九宫格图片加载失败: " + url, e);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
@Override
|
||
public boolean onResourceReady(Drawable resource, Object model,
|
||
Target<Drawable> target, DataSource dataSource,
|
||
boolean isFirstResource) {
|
||
return false;
|
||
}
|
||
})
|
||
.into(imageView);
|
||
}
|
||
```
|
||
|
||
#### 3. 优化 DiscoveryFragment.java
|
||
|
||
在 `app/src/main/java/com/sl/house_property/discovery/DiscoveryFragment.java` 中:
|
||
|
||
```java
|
||
RequestOptions requestOptions = new RequestOptions();
|
||
requestOptions.placeholder(R.mipmap.icon_default_rectangle);
|
||
requestOptions.error(R.mipmap.icon_default_rectangle);
|
||
requestOptions.skipMemoryCache(false);
|
||
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||
|
||
Glide.with(getContext())
|
||
.load(discoveryListEntity.getAvatar())
|
||
.apply(requestOptions)
|
||
.listener(new RequestListener<Drawable>() {
|
||
@Override
|
||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||
Target<Drawable> target, boolean isFirstResource) {
|
||
if (e != null) {
|
||
Log.e("DiscoveryFragment", "头像加载失败: " + discoveryListEntity.getAvatar(), e);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
@Override
|
||
public boolean onResourceReady(Drawable resource, Object model,
|
||
Target<Drawable> target, DataSource dataSource,
|
||
boolean isFirstResource) {
|
||
return false;
|
||
}
|
||
})
|
||
.into(homeGridAdapterItemBinding.imageHead);
|
||
```
|
||
|
||
### 方案二:添加ProGuard规则
|
||
|
||
在 `app/proguard-rules.pro` 中添加:
|
||
|
||
```proguard
|
||
# Glide 图片加载库
|
||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
||
<init>(...);
|
||
}
|
||
-keep public class * implements com.bumptech.glide.module.RegisterGlideModule
|
||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
||
*** rewind();
|
||
}
|
||
|
||
# Glide 注解
|
||
-keep class com.bumptech.glide.load.engine.DecodeJob {
|
||
*;
|
||
}
|
||
-keep class com.bumptech.glide.load.engine.bitmap_recycle.** {
|
||
*;
|
||
}
|
||
-keep class com.bumptech.glide.load.resource.bitmap.** {
|
||
*;
|
||
}
|
||
|
||
# Glide 错误处理
|
||
-keep class com.bumptech.glide.load.engine.GlideException {
|
||
*;
|
||
}
|
||
-keep class com.bumptech.glide.load.engine.GlideException$** {
|
||
*;
|
||
}
|
||
```
|
||
|
||
### 方案三:配置Glide内存和缓存
|
||
|
||
在 `MyApplication.java` 中添加Glide配置:
|
||
|
||
```java
|
||
@Override
|
||
public void onCreate() {
|
||
super.onCreate();
|
||
|
||
// ... 其他初始化代码 ...
|
||
|
||
// 配置Glide
|
||
configureGlide();
|
||
}
|
||
|
||
private void configureGlide() {
|
||
// 设置内存缓存大小(可选)
|
||
// Glide默认会根据设备内存自动调整
|
||
}
|
||
```
|
||
|
||
### 方案四:添加图片URL验证
|
||
|
||
在使用Glide加载图片前,先验证URL:
|
||
|
||
```java
|
||
public static boolean isValidImageUrl(String url) {
|
||
if (TextUtils.isEmpty(url)) {
|
||
return false;
|
||
}
|
||
|
||
// 检查URL格式
|
||
if (!url.startsWith("http://") && !url.startsWith("https://")
|
||
&& !url.startsWith("file://") && !url.startsWith("content://")) {
|
||
return false;
|
||
}
|
||
|
||
// 检查图片格式
|
||
String lowerUrl = url.toLowerCase();
|
||
return lowerUrl.endsWith(".jpg") || lowerUrl.endsWith(".jpeg")
|
||
|| lowerUrl.endsWith(".png") || lowerUrl.endsWith(".gif")
|
||
|| lowerUrl.endsWith(".webp") || lowerUrl.contains("image");
|
||
}
|
||
```
|
||
|
||
## 完整优化示例
|
||
|
||
### 创建统一的Glide工具类
|
||
|
||
创建文件:`app/src/main/java/utils/GlideUtils.java`
|
||
|
||
```java
|
||
package utils;
|
||
|
||
import android.content.Context;
|
||
import android.graphics.drawable.Drawable;
|
||
import android.util.Log;
|
||
import android.widget.ImageView;
|
||
|
||
import androidx.annotation.NonNull;
|
||
import androidx.annotation.Nullable;
|
||
|
||
import com.bumptech.glide.Glide;
|
||
import com.bumptech.glide.load.DataSource;
|
||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||
import com.bumptech.glide.load.engine.GlideException;
|
||
import com.bumptech.glide.request.RequestListener;
|
||
import com.bumptech.glide.request.RequestOptions;
|
||
import com.bumptech.glide.request.target.Target;
|
||
|
||
import com.sl.house_property.R;
|
||
|
||
public class GlideUtils {
|
||
|
||
private static final String TAG = "GlideUtils";
|
||
|
||
/**
|
||
* 加载图片(带错误处理)
|
||
*/
|
||
public static void loadImage(Context context, String url, ImageView imageView) {
|
||
loadImage(context, url, imageView, R.drawable.picture_image_placeholder);
|
||
}
|
||
|
||
/**
|
||
* 加载图片(带错误处理和占位图)
|
||
*/
|
||
public static void loadImage(Context context, String url, ImageView imageView, int placeholderResId) {
|
||
if (context == null || imageView == null) {
|
||
Log.w(TAG, "Context或ImageView为空,无法加载图片");
|
||
return;
|
||
}
|
||
|
||
if (!isValidImageUrl(url)) {
|
||
Log.w(TAG, "无效的图片URL: " + url);
|
||
imageView.setImageResource(placeholderResId);
|
||
return;
|
||
}
|
||
|
||
RequestOptions options = new RequestOptions()
|
||
.placeholder(placeholderResId)
|
||
.error(placeholderResId)
|
||
.fallback(placeholderResId)
|
||
.skipMemoryCache(false)
|
||
.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||
|
||
Glide.with(context)
|
||
.load(url)
|
||
.apply(options)
|
||
.listener(new RequestListener<Drawable>() {
|
||
@Override
|
||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||
Target<Drawable> target, boolean isFirstResource) {
|
||
if (e != null) {
|
||
Log.e(TAG, "图片加载失败: " + url, e);
|
||
// 可以在这里添加错误上报
|
||
}
|
||
return false;
|
||
}
|
||
|
||
@Override
|
||
public boolean onResourceReady(Drawable resource, Object model,
|
||
Target<Drawable> target, DataSource dataSource,
|
||
boolean isFirstResource) {
|
||
return false;
|
||
}
|
||
})
|
||
.into(imageView);
|
||
}
|
||
|
||
/**
|
||
* 验证图片URL是否有效
|
||
*/
|
||
private static boolean isValidImageUrl(String url) {
|
||
if (url == null || url.trim().isEmpty()) {
|
||
return false;
|
||
}
|
||
|
||
String lowerUrl = url.toLowerCase().trim();
|
||
return lowerUrl.startsWith("http://")
|
||
|| lowerUrl.startsWith("https://")
|
||
|| lowerUrl.startsWith("file://")
|
||
|| lowerUrl.startsWith("content://")
|
||
|| lowerUrl.startsWith("drawable://");
|
||
}
|
||
|
||
/**
|
||
* 加载圆形图片
|
||
*/
|
||
public static void loadCircleImage(Context context, String url, ImageView imageView) {
|
||
RequestOptions options = new RequestOptions()
|
||
.circleCrop()
|
||
.placeholder(R.drawable.picture_image_placeholder)
|
||
.error(R.drawable.picture_image_placeholder);
|
||
|
||
Glide.with(context)
|
||
.load(url)
|
||
.apply(options)
|
||
.into(imageView);
|
||
}
|
||
|
||
/**
|
||
* 清除Glide缓存
|
||
*/
|
||
public static void clearCache(Context context) {
|
||
Glide.get(context).clearMemory();
|
||
new Thread(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
Glide.get(context).clearDiskCache();
|
||
}
|
||
}).start();
|
||
}
|
||
}
|
||
```
|
||
|
||
## 使用建议
|
||
|
||
### 1. 统一使用工具类
|
||
将所有Glide调用改为使用 `GlideUtils`:
|
||
|
||
```java
|
||
// 替换前
|
||
Glide.with(context).load(url).into(imageView);
|
||
|
||
// 替换后
|
||
GlideUtils.loadImage(context, url, imageView);
|
||
```
|
||
|
||
### 2. 添加必要的导入
|
||
在使用RequestListener时需要添加:
|
||
```java
|
||
import com.bumptech.glide.load.DataSource;
|
||
import com.bumptech.glide.load.engine.GlideException;
|
||
import com.bumptech.glide.request.RequestListener;
|
||
import com.bumptech.glide.request.target.Target;
|
||
```
|
||
|
||
### 3. 检查Context生命周期
|
||
确保在Activity/Fragment销毁时取消Glide请求:
|
||
```java
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
Glide.with(this).clear(imageView);
|
||
}
|
||
```
|
||
|
||
## 常见错误处理
|
||
|
||
### 错误1:DecodeJob异常
|
||
- **原因**:图片解码失败
|
||
- **解决**:添加错误占位图,验证URL有效性
|
||
|
||
### 错误2:内存溢出
|
||
- **原因**:加载过大图片
|
||
- **解决**:使用override()限制图片尺寸
|
||
|
||
### 错误3:网络图片加载失败
|
||
- **原因**:网络问题或URL无效
|
||
- **解决**:添加网络检查,使用错误占位图
|
||
|
||
## 测试验证
|
||
|
||
1. **测试正常图片加载**
|
||
- 验证占位图显示
|
||
- 验证图片加载成功
|
||
|
||
2. **测试错误情况**
|
||
- 无效URL
|
||
- 网络断开
|
||
- 损坏图片
|
||
|
||
3. **测试内存**
|
||
- 加载大量图片
|
||
- 检查内存使用
|
||
|
||
---
|
||
|
||
**Glide版本**: 4.9.0
|
||
**建议升级**: 考虑升级到Glide 4.16.0+(如果兼容)
|
||
|