核心流程
无论使用哪种方法,下载网络图片的基本步骤都是相同的:

- 获取网络权限:在
AndroidManifest.xml中声明需要访问网络的权限。 - 发起网络请求:使用 HTTP 客户端(如
HttpURLConnection或第三方库)从指定的 URL 获取图片数据流。 - 处理响应:检查响应码是否成功(如 HTTP 200),然后从输入流中读取图片数据。
- 解码和显示:将获取到的字节数据解码成
Bitmap对象,并设置到ImageView控件上显示。 - 处理线程:注意:网络操作和 UI 操作都不能在主线程(UI 线程)中进行,否则会导致应用卡顿甚至崩溃(
NetworkOnMainThreadException),必须使用后台线程(如AsyncTask、Thread+Handler,或现代的Coroutine/RxJava)。
使用 HttpURLConnection (原生 API,不推荐用于新项目)
这是最基础的方式,不依赖任何第三方库,但代码量较大,处理起来比较繁琐,适合用于理解底层原理。
添加权限
<!-- AndroidManifest.xml -->
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果你的应用需要支持 HTTP (非 HTTPS),还需要添加 -->
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<application ...>
...
</application>
</manifest>
创建工具类
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ImageDownloader {
// 使用一个线程池来管理下载任务
private final ExecutorService executorService = Executors.newCachedThreadPool();
// 使用 Handler 将结果传回主线程
private final Handler handler = new Handler(Looper.getMainLooper());
public void download(String imageUrl, final OnDownloadListener listener) {
executorService.execute(() -> {
Bitmap bitmap = null;
HttpURLConnection connection = null;
InputStream inputStream = null;
try {
URL url = new URL(imageUrl);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 在主线程中更新 UI
final Bitmap finalBitmap = bitmap;
handler.post(() -> listener.onDownloaded(finalBitmap));
});
}
public interface OnDownloadListener {
void onDownloaded(Bitmap bitmap);
}
}
在 Activity/Fragment 中使用

// 在你的 Activity 或 Fragment 中
ImageView imageView = findViewById(R.id.imageView);
String imageUrl = "https://example.com/path/to/image.jpg";
ImageDownloader downloader = new ImageDownloader();
downloader.download(imageUrl, new ImageDownloader.OnDownloadListener() {
@Override
public void onDownloaded(Bitmap bitmap) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
// 下载失败,可以显示一个默认图片
imageView.setImageResource(R.drawable.error_image);
}
}
});
缺点:
- 代码冗长,需要手动管理线程、连接和流。
- 没有缓存机制,重复下载同一张图片会浪费流量和时间。
- 没有图片压缩功能,大图容易导致
OutOfMemoryError。
使用第三方库 (强烈推荐)
在实际开发中,我们强烈推荐使用成熟的第三方库,它们已经为我们解决了线程、缓存、内存优化等一系列复杂问题。
选项 A: Glide (Google 推荐,最流行)
Glide 是一个由 Google 维护的图片加载库,以其简洁的 API 和出色的性能著称。
添加依赖

// app/build.gradle (或 build.gradle.kts)
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.imageView);
String imageUrl = "https://example.com/path/to/image.jpg";
// 仅需一行代码即可完成加载、缓存和显示
Glide.with(this) // 传入 Context, Activity, 或 Fragment
.load(imageUrl) // 传入图片 URL
.into(imageView); // 传入目标 ImageView
Glide 的优点:
- 极度简单:API 非常简洁。
- 强大的缓存:默认开启内存缓存和磁盘缓存,极大提升加载速度和减少流量消耗。
- 自动处理生命周期:与 Activity/Fragment 生命周期绑定,在页面销毁时会自动取消请求,避免内存泄漏。
- 支持多种数据源:除了 URL,还可以加载本地文件、资源、URI 等。
- 图片变换:支持圆角、裁剪、模糊等多种变换。
选项 B: Picasso (Square 出品,老牌经典)
Picasso 也是由著名开源公司 Square 出品的库,非常稳定和可靠。
添加依赖
// app/build.gradle
dependencies {
implementation 'com.squareup.picasso:picasso:2.8'
}
使用 (同样非常简单)
ImageView imageView = findViewById(R.id.imageView);
String imageUrl = "https://example.com/path/to/image.jpg";
Picasso.get()
.load(imageUrl)
.into(imageView);
Picasso 的优点:
- API 简洁易用。
- 自动进行内存和磁盘缓存。
- 自动处理线程和生命周期。
- 内置图片大小调整功能,有助于避免
OOM。
选项 C: Coil (现代、快速、基于 Kotlin)
Coil 是一个新兴的、完全用 Kotlin 编写的图片加载库,性能非常出色,并且充分利用了 Kotlin 的现代特性。
添加依赖
// app/build.gradle
dependencies {
implementation "io.coil-kt:coil:2.5.0"
}
使用 (非常符合 Kotlin 风格)
// 在 Activity 或 Fragment 中 (使用 Kotlin) val imageView: ImageView = findViewById(R.id.imageView) val imageUrl = "https://example.com/path/to/image.jpg" // 使用扩展函数 imageView.load(imageUrl)
Coil 的优点:
- 性能卓越:基于 Kotlin Coroutines,代码更简洁高效。
- 体积小:相比于 Glide 和 Picasso,库体积更小。
- 现代 API:完全拥抱 Kotlin 和 Jetpack Compose。
- 内存友好:默认使用 OkHttp 作为网络引擎,并采用更高效的内存缓存策略。
最佳实践和进阶
处理加载中和错误状态
一个好的用户体验需要处理加载中和加载失败的状态。
以 Glide 为例:
Glide.with(this)
.load(imageUrl)
.placeholder(R.drawable.placeholder_image) // 加载中显示的图片
.error(R.drawable.error_image) // 加载失败时显示的图片
.into(imageView);
内存优化 (OutOfMemoryError)
大图片很容易导致应用内存溢出,现代库都内置了解决方案。
以 Glide 为例:
// 在 Application 类中初始化 Glide 配置
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 设置内存缓存大小为屏幕尺寸的位图
Glide.get(this).setMemoryCategory(MemoryCategory.LOW);
}
}
// 在加载时,可以指定图片的尺寸
Glide.with(this)
.load(imageUrl)
.override(800, 600) // 指定图片显示的最大宽高,Glide 会自动进行采样
.into(imageView);
取消请求
当列表快速滚动时,应该取消掉 ImageView 上正在进行的旧请求,以避免图片错乱。
以 Glide 为例:
// 在 ViewHolder 的 onViewRecycled 或类似方法中调用 Glide.with(context).clear(imageView);
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HttpURLConnection | 原生,无依赖 | 代码复杂,无缓存,需手动处理线程和OOM | 学习原理,或项目有极特殊限制,不能引入第三方库 |
| Glide | API简洁,功能强大,缓存优秀,Google推荐 | 相对于Coil,库体积稍大 | 绝大多数Android项目的首选 |
| Picasso | 稳定可靠,API成熟 | 停止更新,性能和功能不如Glide/Coil新 | 维护老项目,或个人偏好 |
| Coil | 性能高,库体积小,Kotlin原生,现代化 | 相对Glide/Picasso,社区生态和资料稍少 | 新项目,特别是Kotlin和Jetpack Compose项目 |
最终建议:
- 对于新项目,优先选择 Glide 或 Coil,如果你使用 Kotlin,Coil 是一个非常现代和高效的选择。
- 对于学习,可以尝试用
HttpURLConnection自己实现一次,以了解底层原理。 - 对于维护老项目,如果已经用了 Picasso,可以继续使用,除非有性能瓶颈。
