目录
- 核心概念:为什么不能直接用
HttpURLConnection或HttpClient? - 基础实现(不推荐,但有助于理解)
- 使用
HttpURLConnection和ImageView - 存在的问题:主线程阻塞、内存浪费、无缓存。
- 使用
- 官方推荐 - Glide (最流行、最简单)
- Glide 是什么?
- 如何集成和使用?
- 核心功能:缓存、转换、占位符、错误图。
- 强力对比 - Picasso
- Picasso 是什么?
- 与 Glide 的简单对比。
- 功能强大 - Coil (Kotlin 优先)
- Coil 是什么?
- 优势:Kotlin 友好、轻量级。
- 手动实现(进阶)
- 使用
OkHttp+Glide的底层库Glide或自己实现缓存。 - 使用
OkHttp+Android-Universal-Image-Loader(UIL,已不推荐,但了解历史有好处)。
- 使用
- 最佳实践总结
- 选择哪个库?
- 图片优化:服务器端和客户端。
- 内存管理。
核心概念:为什么不能直接用 HttpURLConnection?
在早期,开发者可能会这样做:

// 错误示范!不要在主线程(UI线程)执行网络请求!
URL url = new URL("http://example.com/image.jpg");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream inputStream = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
imageView.setImageBitmap(bitmap);
这种方式存在严重问题:
- ANR (Application Not Responding):网络请求是耗时操作,在 Android 的主线程(UI线程)上执行会导致应用卡顿,甚至弹出“无响应”对话框。
- 内存浪费:
BitmapFactory.decodeStream()会将整个图片一次性加载到内存中,如果图片很大(如 5MB),很容易导致OutOfMemoryError(OOM) 崩溃。 - 无缓存:每次打开页面都要重新下载图片,浪费用户流量,加载速度慢。
我们必须使用专业的图片加载库来解决这些问题。
方法一:基础实现(仅用于理解)
为了更好地理解库的工作原理,我们先来看一个使用 AsyncTask 进行后台加载的简化版。
// 在 Activity 或 Fragment 中
private ImageView imageView;
// 在 onCreate 或 onViewCreated 中
imageView = findViewById(R.id.my_image_view);
new LoadImageTask().execute("http://example.com/image.jpg");
private class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
String imageUrl = urls[0];
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream inputStream = connection.getInputStream();
return BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null && imageView != null) {
imageView.setImageBitmap(bitmap);
} else {
// 加载失败,可以设置一个错误图
imageView.setImageResource(R.drawable.error_image);
}
}
}
这个改进版解决了主线程阻塞问题,但仍然有内存浪费和无缓存的问题。 这就是为什么我们需要使用专业库。

方法二:官方推荐 - Glide (最流行、最简单)
Glide 是 Google 推荐的图片加载库,由bumptech公司开发,它以其简洁的 API 和强大的功能而闻名。
如何集成
在 app/build.gradle 文件中添加依赖:
dependencies {
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
}
如何使用
基本用法一行代码搞定:
// 在 Activity 或 Fragment 中
ImageView imageView = findViewById(R.id.my_image_view);
String imageUrl = "https://picsum.photos/400/800"; // 一个随机图片服务
// 基本加载
Glide.with(this) // 'this' 可以是 Activity, Fragment, 或 Context
.load(imageUrl) // 加载图片的 URL
.into(imageView); // 设置到哪个 ImageView 上
核心功能
Glide 的强大之处在于其链式调用。

-
占位符 和错误图
Glide.with(this) .load(imageUrl) .placeholder(R.drawable.placeholder) // 加载中显示的图片 .error(R.drawable.error_image) // 加载失败时显示的图片 .into(imageView); -
图片变换
// 圆形图片 Glide.with(this) .load(imageUrl) .circleCrop() // 应用圆形裁剪变换 .into(imageView); // 圆角图片 Glide.with(this) .load(imageUrl) .transform(new RoundedCorners(30)) // 30px 圆角 .into(imageView); -
缓存机制 Glide 默认实现了内存缓存和磁盘缓存,它会自动处理,你无需关心,这大大提升了用户体验。
-
生命周期集成
Glide.with(this)会自动与 Activity 或 Fragment 的生命周期绑定,当 Activity 或 Fragment 被销毁时,Glide 会自动取消正在进行的图片加载任务,避免了内存泄漏和无效加载。
方法三:强力对比 - Picasso
Picasso 是 Square 公司开发的另一个经典图片加载库,曾经非常流行。
如何集成
dependencies {
implementation 'com.squareup.picasso:picasso:2.8'
}
如何使用
API 同样非常简洁。
String imageUrl = "https://picsum.photos/400/800";
Picasso.get()
.load(imageUrl)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView);
Glide vs. Picasso
| 特性 | Glide | Picasso |
|---|---|---|
| 缓存策略 | 内存缓存和磁盘缓存都默认开启。 | 只默认开启内存缓存,磁盘缓存需要手动配置。 |
| 大小调整 | 内置 override() 方法,但推荐使用 RequestBuilder。 |
内置 resize() 和 centerCrop() 方法,非常方便。 |
| GIF 支持 | 原生支持,性能好。 | 需要依赖第三方库(如 gif-drawable)。 |
| 生命周期 | 自动绑定,更安全。 | 需要手动调用 Picasso.setIndicatorsEnabled(true) 来查看请求状态。 |
| 体积 | 功能更多,库体积稍大。 | 非常轻量,API 简洁。 |
| 推荐度 | 当前首选,功能全面,稳定。 | 优秀,但在新项目中 Glide 更常见。 |
对于新项目,Glide 通常是更好的选择,因为它开箱即用的功能(如磁盘缓存)更符合现代 Android 开发的需求。
方法四:功能强大 - Coil (Kotlin 优先)
Coil 是一个相对较新的库,由 Coil 公司(前身为 Instacart)开发,它完全使用 Kotlin 编写,充分利用了 Kotlin 的新特性。
如何集成
dependencies {
implementation("io.coil-kt:coil:2.5.0")
}
如何使用
语法非常 Kotlin 风格。
// 在 Activity 或 Fragment (Kotlin) 中
val imageView: ImageView = findViewById(R.id.my_image_view)
val imageUrl = "https://picsum.photos/400/800"
// 使用扩展函数,代码极其简洁
imageView.load(imageUrl) {
placeholder(R.drawable.placeholder)
error(R.drawable.error_image)
transformations(CircleCropTransformation())
crossfade(true) // 淡入效果
}
优势
- Kotlin 友好:大量使用扩展函数、协程和 Flow,代码更简洁、更安全。
- 轻量级:编译后的体积比 Glide 和 Picasso 都小。
- 性能高:基于 Coroutines,比基于线程的库更高效。
- 现代 API:支持 Jetpack Compose。
如果你的项目是 Kotlin 开发,或者你追求更现代、更轻量的解决方案,Coil 是一个极佳的选择。
方法五:手动实现(进阶)
在某些特殊场景下,你可能需要完全控制图片加载过程,或者使用一个已经停止维护但功能强大的库。
方案 A:OkHttp + Glide 底层
Glide 的底层网络请求默认使用 HttpUrlConnection,你可以通过替换 ModelLoader 来让它使用 OkHttp,以利用其连接池、拦截器等高级功能。
方案 B:OkHttp + Android-Universal-Image-Loader (UIL)
UIL 是一个老牌的强大库,但已多年未更新,它的核心思想是:
- 网络层:使用
OkHttp进行下载。 - 缓存层:自己实现内存缓存 (
LruCache) 和磁盘缓存 (DiskLruCache)。 - 解码层:按需解码,避免 OOM。
- 配置:提供非常灵活的配置选项。
这种方式学习成本高,且容易出错,不推荐在新项目中使用。 了解它有助于理解图片加载库的内部原理。
最佳实践总结
选择哪个库?
- Java 项目:Glide 是最稳妥、功能最全面的选择。
- Kotlin 项目:Coil 是首选,体验最佳;Glide 也完全没问题。
- 需要快速实现,不关心细节:Picasso 依然是一个可靠的选项。
图片优化:服务器端和客户端
一个好的图片加载体验,不仅需要客户端的库,还需要服务器的配合。
-
服务器端优化
- 提供多种尺寸:为同一张图片提供不同分辨率的版本(如
image_320x480.jpg,image_640x960.jpg),让客户端根据屏幕大小选择最合适的,减少流量和内存占用。 - 使用 WebP 格式:WebP 是 Google 推出的现代图片格式,在同等质量下,体积比 JPEG 和 PNG 小得多。
- 启用 CDN 和缓存分发网络(CDN)和正确的 HTTP 缓存头(如
Cache-Control,ETag),让图片离用户更近,并避免重复下载。
- 提供多种尺寸:为同一张图片提供不同分辨率的版本(如
-
客户端优化
- 始终使用占位符:提升用户体验,让界面感觉更流畅。
- 合理使用变换:如
centerCrop、circleCrop,让图片更好地适应 UI 设计。 - 监听加载状态:对于列表项,可以监听
onResourceReady和onLoadFailed来执行一些额外逻辑,比如更新列表状态。 - 注意内存泄漏:确保在
Activity/Fragment的onDestroy或onViewDestroyed中取消图片加载,虽然 Glide/Coil/Picasso 大部分时候能自动处理,但在特殊场景(如 Dialog)下需要手动取消。
// Glide 手动取消示例 Glide.with(context).clear(imageView); // 或者 Glide.with(context).clear(target);
希望这份详细的指南能帮助你掌握在 Android 中从服务器加载图片的技能!
