核心步骤
无论使用哪种方法,下载图片的基本逻辑都遵循以下步骤:

- 获取网络权限:在
AndroidManifest.xml中声明需要访问网络的权限。 - 发起网络请求:使用
HttpURLConnection或第三方库(如 OkHttp)向图片的 URL 发送请求。 - 获取输入流:从网络连接中获取图片数据的输入流。
- 处理输入流:将输入流中的数据读取出来,并保存为字节数组或直接写入文件。
- 显示或保存图片:将字节数组解码为
Bitmap对象用于显示,或者将文件保存到设备存储中。
使用 HttpURLConnection (原生 API,适合简单场景)
这是最基础的方式,不需要引入任何第三方库,适合简单的下载任务。
添加网络权限
在 app/src/main/AndroidManifest.xml 文件中添加:
<uses-permission android:name="android.permission.INTERNET" />
注意:从 Android 9 (API 28) 开始,默认情况下应用只能使用 HTTPS,如果你的服务器使用的是 HTTP,需要在 application 标签下添加 android:usesCleartextTraffic="true"。
<application
...
android:usesCleartextTraffic="true"
...>
</application>
创建下载工具类
创建一个 ImageDownloader 类来处理下载逻辑。

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class ImageDownloader {
private static final String TAG = "ImageDownloader";
// 下载并显示到 ImageView
public void download(String imageUrl, ImageView imageView) {
new DownloadImageTask(imageView).execute(imageUrl);
}
// 下载并保存到文件
public void downloadAndSave(String imageUrl, String savePath) {
new DownloadAndSaveImageTask().execute(imageUrl, savePath);
}
// 异步任务,用于在后台线程下载图片
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private final ImageView imageView;
DownloadImageTask(ImageView imageView) {
this.imageView = imageView;
}
@Override
protected Bitmap doInBackground(String... urls) {
String imageUrl = urls[0];
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream inputStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
Log.e(TAG, "Error downloading image", e);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}
// 异步任务,用于下载并保存到文件
private static class DownloadAndSaveImageTask extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
String imageUrl = params[0];
String savePath = params[1];
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream inputStream = connection.getInputStream();
// 这里需要实现将 inputStream 写入到 savePath 文件的逻辑
// 例如使用 FileOutputStream
// FileHelper.saveStreamToFile(inputStream, savePath);
return true;
} catch (IOException e) {
Log.e(TAG, "Error downloading and saving image", e);
return false;
}
}
}
}
使用 AsyncTask 的注意事项:
AsyncTask 在 Android 11 上已被标记为过时,对于新的项目,强烈建议使用方案二中的现代方式。
使用 OkHttp + Glide (现代、高效、推荐)
这是目前 Android 开发中最主流和推荐的方式,OkHttp 用于高效的网络请求,Glide 用于图片的加载、缓存和显示,它们配合得天衣无缝。
添加依赖
在 app/build.gradle 文件的 dependencies 代码块中添加:
// OkHttp for networking
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Glide for image loading and caching
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
添加网络权限
与方案一相同,在 AndroidManifest.xml 中添加权限。

使用 OkHttp 下载图片到本地文件
如果你需要手动控制下载过程(例如显示进度条),可以使用 OkHttp。
import android.os.Handler;
import android.os.Looper;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
public class OkHttpImageDownloader {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
// 下载图片到指定文件
public void downloadToFile(String imageUrl, File destinationFile, final DownloadCallback callback) {
executorService.execute(() -> {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(imageUrl).build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
notifyFailure(callback, "Download failed with code: " + response.code());
return;
}
long fileSize = response.body().contentLength();
long downloadedBytes = 0;
try (InputStream inputStream = response.body().byteStream();
BufferedSink sink = Okio.buffer(Okio.sink(destinationFile))) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
sink.write(buffer, 0, bytesRead);
downloadedBytes += bytesRead;
// 计算进度并通知UI
int progress = (int) ((downloadedBytes * 100) / fileSize);
notifyProgress(callback, progress);
}
}
notifySuccess(callback, destinationFile);
} catch (IOException e) {
notifyFailure(callback, "Error: " + e.getMessage());
}
});
}
private void notifySuccess(DownloadCallback callback, File file) {
mainHandler.post(() -> callback.onSuccess(file));
}
private void notifyFailure(DownloadCallback callback, String error) {
mainHandler.post(() -> callback.onFailure(error));
}
private void notifyProgress(DownloadCallback callback, int progress) {
mainHandler.post(() -> callback.onProgress(progress));
}
public interface DownloadCallback {
void onSuccess(File file);
void onFailure(String error);
void onProgress(int progress);
}
}
使用 Glide 自动下载和显示图片 (最简单的方式)
Glide 会自动处理下载、缓存、解码和显示,你只需要一行代码。
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private String imageUrl = "https://example.com/path/to/your/image.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.my_image_view);
// 使用 Glide 加载图片
// Glide 会自动处理网络请求、内存缓存和磁盘缓存
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 的优点:
- 极度简单:一行代码搞定。
- 强大的缓存:自动管理内存缓存和磁盘缓存,避免重复下载,提升用户体验。
- 生命周期集成:自动与 Activity/Fragment 的生命周期同步,防止内存泄漏。
- 支持多种数据源:除了 URL,还可以加载本地资源、File、Uri 等。
- 图片变换:可以轻松实现圆角、裁剪、模糊等效果。
使用 Kotlin 协程 (现代、简洁、协程首选)
如果你使用的是 Kotlin,协程是处理异步任务的现代标准。
添加依赖
确保你的项目已经配置了 Kotlin 和协程,在 app/build.gradle 中:
// Kotlin Coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
使用协程 + OkHttp 下载
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.io.IOException
class CoroutineImageDownloader {
private val client = OkHttpClient()
suspend fun downloadToFile(imageUrl: String, destinationFile: File): Result<File> {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(imageUrl)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
// 使用 okio 写入文件,比 Java IO 更高效
response.body?.byteStream()?.use { inputStream ->
destinationFile.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
destinationFile // 返回下载的文件
}
}
}
}
使用示例:
// 在 Activity 或 ViewModel 中调用
lifecycleScope.launch {
val downloader = CoroutineImageDownloader()
val result = downloader.downloadToFile(imageUrl, File(cacheDir, "my_image.jpg"))
result.onSuccess { file ->
// 在主线程更新UI
imageView.setImageBitmap(BitmapFactory.decodeFile(file.absolutePath))
}.onFailure { e ->
// 处理错误
Toast.makeText(this@MainActivity, "Download failed: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
总结与最佳实践
| 特性 | HttpURLConnection + AsyncTask |
OkHttp + Glide | Kotlin Coroutines + OkHttp |
|---|---|---|---|
| 易用性 | 中等 (需手动处理缓存和线程) | 极高 (一行代码) | 高 (Kotlin 语法简洁) |
| 性能 | 一般 | 高 (自动缓存,高效网络) | 高 (协程轻量级) |
| 功能 | 基础 | 丰富 (缓存、变换、生命周期) | 基础 (需自行扩展) |
| 线程管理 | AsyncTask |
自动处理 | Dispatchers.IO / Main |
| 现代性 | 过时 | 推荐 | 推荐 (Kotlin 项目) |
| 适用场景 | 学习网络基础、极简项目 | 几乎所有 Android 图片加载场景 | 需要精细控制下载逻辑的 Kotlin 项目 |
最终建议:
- 对于绝大多数应用:直接使用 Glide,它为你解决了 90% 的图片加载问题,稳定、高效且易于使用。
- 对于需要手动控制下载过程的场景(如显示下载进度、自定义存储路径):使用 OkHttp 进行下载,然后使用 Glide 或
BitmapFactory来显示结果。 - 对于新的 Kotlin 项目:优先考虑使用 Kotlin 协程 + OkHttp 的方式来处理自定义下载逻辑,代码更简洁、更安全。
