核心挑战
直接从网络加载图片会面临几个主要问题:

- 性能问题:主线程(UI线程)不能执行网络请求,否则会导致应用卡顿甚至“应用无响应”(ANR)。
- 内存问题:高清图片可能非常大,直接加载到内存中很容易导致
OutOfMemoryError(OOM)。 - 用户体验问题:图片加载需要时间,如果没有处理,用户会看到空白或占位符。
- 缓存问题:重复下载相同的图片会浪费用户流量和服务器资源。
一个好的图片加载方案必须解决以上所有问题。
传统方式(不推荐,但有助于理解)
这种方式使用 Android 原生的 HttpURLConnection 或 OkHttp,结合 BitmapFactory 和 ImageView,它能让你了解底层原理,但不适合在生产环境中使用。
步骤:
-
添加网络权限 (
app/src/main/AndroidManifest.xml)<uses-permission android:name="android.permission.INTERNET" />
-
在后台线程加载图片 不能在
onCreate()或onResume()等主线程方法中直接进行网络请求,通常使用AsyncTask、Thread+Handler或ExecutorService。
(图片来源网络,侵删)示例 (使用
AsyncTask):public class ImageLoadTask extends AsyncTask<String, Void, Bitmap> { private ImageView imageView; public ImageLoadTask(ImageView imageView) { this.imageView = imageView; } @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(); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null && imageView != null) { imageView.setImageBitmap(bitmap); } } } -
在 Activity 或 Fragment 中调用
ImageView myImageView = findViewById(R.id.my_image_view); String imageUrl = "https://example.com/path/to/image.jpg"; // 必须在主线程之外执行网络请求,AsyncTask 会自动处理 new ImageLoadTask(myImageView).execute(imageUrl);
缺点:
- 代码冗长,需要自己处理线程、缓存、内存优化等。
- 容易发生内存泄漏(在
AsyncTask完成前 Activity 被销毁)。 - 无法处理图片的缩放、圆角等复杂变换。
使用成熟的图片加载库(推荐)
为了解决上述所有问题,业界涌现了许多优秀的第三方图片加载库,它们极大地简化了开发,并提供了强大的功能。

主流库对比:
| 库 | 特点 | 状态 |
|---|---|---|
| Glide | Google 推荐,性能优异,API 简洁,支持 GIF/Video,生命周期自动管理。 | 强烈推荐,目前最主流的选择之一。 |
| Coil | Kotlin 优先,基于 Coroutines 和 OkHttp,性能极高,API 现代且简洁。 | 强烈推荐,是 Android 开发的新趋势。 |
| Picasso | 老牌库,Square 公司出品,稳定可靠,但已多年不更新,缺少 Kotlin 协程支持。 | 不再推荐新项目使用。 |
| Fresco | 功能极其强大,尤其在处理列表图片时(如 DraweeView),内存管理能力顶尖。 |
适用于对性能和功能有极致要求的大型应用。 |
使用 Glide (Java/Kotlin)
Glide 是一个非常稳定和全面的库,适合所有类型的项目。
添加依赖 (app/build.gradle)
dependencies {
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' // 如果是 Java 项目或需要注解
}
在 AndroidManifest.xml 中添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
在 Activity/Fragment 中使用
基本用法:
// 在 Kotlin 中
val imageView: ImageView = findViewById(R.id.my_image_view)
val imageUrl = "https://picsum.photos/400/800" // 一个提供随机图片的网站
Glide.with(this) // 'this' 可以是 Activity, Fragment, 或 Context
.load(imageUrl) // 加载图片的 URL
.into(imageView) // 设置到哪个 ImageView
// 在 Java 中
ImageView imageView = findViewById(R.id.my_image_view);
String imageUrl = "https://picsum.photos/400/800";
Glide.with(this)
.load(imageUrl)
.into(imageView);
进阶用法(解决核心挑战):
-
占位符:在图片加载完成前显示的图片。
Glide.with(this) .load(imageUrl) .placeholder(R.drawable.placeholder_image) // 加载中的占位图 .error(R.drawable.error_image) // 加载失败时显示的图片 .into(imageView) -
内存缓存:Glide 默认开启了内存缓存和磁盘缓存,你无需手动管理。
-
图片变换:如缩放、裁剪、圆角等。
// 需要添加依赖:implementation 'jp.wasabeef:glide-transformations:4.3.0' Glide.with(this) .load(imageUrl) .transform(CenterCrop(), RoundedCorners(30)) // 先裁剪再添加30px圆角 .into(imageView) -
生命周期管理:
Glide.with(context)会自动绑定到 Activity/Fragment 的生命周期,当界面销毁时,会自动取消图片加载,有效防止内存泄漏。
使用 Coil (Kotlin - 现代首选)
Coil 是一个为 Kotlin 生态打造的现代图片加载库,基于 Coroutines 和 OkHttp,性能和 API 设计都非常出色。
添加依赖 (app/build.gradle)
dependencies {
// 基础库
implementation "io.coil-kt:coil:2.5.0"
// 可选:如果你已经使用了 OkHttp,可以复用其实例
implementation "io.coil-kt:coil-base:2.5.0"
implementation "io.coil-kt:coil-network-okhttp:2.5.0"
}
在 AndroidManifest.xml 中添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
在 Activity/Fragment 中使用 (非常简洁)
基本用法:
val imageView: ImageView = findViewById(R.id.my_image_view) val imageUrl = "https://picsum.photos/400/800" // 使用扩展函数,一行代码搞定 imageView.load(imageUrl)
进阶用法:
imageView.load(imageUrl) {
// 占位符
placeholder(R.drawable.placeholder_image)
error(R.drawable.error_image)
// 变换
transformations(CircleCrop(), RoundedCornersTransformation(radius = 16f))
// 跨度策略,避免因图片尺寸导致布局抖动
crossfade(true) // 淡入效果
size(400, 800) // 指定目标尺寸,有助于优化内存
}
Coil 的优势:
- 性能卓越:使用 OkHttp 作为网络客户端,内存占用更低。
- API 极简:大量使用 Kotlin 扩展函数,代码非常干净。
- 完全基于 Kotlin Coroutines:与 Jetpack Compose 和现代 Android 开发架构无缝集成。
- 体积更小:相比 Glide,APK 体积更小。
-
选择合适的库:
- 新项目:强烈推荐使用 Coil,特别是如果你的项目是 Kotlin 开发,或者你希望拥抱现代化的技术栈。
- 维护旧项目或 Java 项目:Glide 是一个非常安全、稳定且功能全面的选择。
-
总是使用占位符:为
ImageView设置placeholder和error图片,能极大地提升用户体验。 -
注意图片尺寸:不要将服务器返回的超大原图直接加载到内存中,你可以在 URL 中请求特定尺寸的图片(
image.jpg?width=400&height=600),或者在加载时使用.override(width, height)来指定目标尺寸。 -
生命周期管理:使用
Glide.with(context)或imageView.load()时,库会自动处理生命周期。千万不要在onDestroy()中手动取消,这可能会导致问题。 -
处理 HTTPS:现代服务器基本都是 HTTPS,如果你的应用只支持 HTTPS,确保服务器证书有效,如果需要支持自签名证书,需要配置
OkHttpClient。
现代架构下的图片加载
如果你的应用使用了 Jetpack Compose,图片加载的方式又有所不同,你需要使用 AsyncImage 组件。
使用 Coil 加载图片到 Jetpack Compose:
-
添加 Compose 的 Coil 依赖:
implementation "io.coil-kt:coil-compose:2.5.0"
-
在 Composable 函数中使用:
import coil.compose.AsyncImage
@Composable fun MyScreen() { val imageUrl = "https://picsum.photos/400/800"
AsyncImage(
model = imageUrl, // 可以是 URL、File、Drawable 等
contentDescription = "A random image from Picsum",
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder_image),
error = painterResource(R.drawable.error_image)
)
Coil for Compose 的 API 同样非常简洁和强大。
希望这份详细的指南能帮助你顺利地在 Android 应用中加载服务器图片! 