a
This commit is contained in:
419
Glide错误处理方案.txt
Normal file
419
Glide错误处理方案.txt
Normal file
@@ -0,0 +1,419 @@
|
||||
# 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+(如果兼容)
|
||||
|
||||
Reference in New Issue
Block a user