睿诚科技协会

Android如何从网络下载图片?

核心流程

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

Android如何从网络下载图片?-图1
(图片来源网络,侵删)
  1. 获取网络权限:在 AndroidManifest.xml 中声明需要访问网络的权限。
  2. 发起网络请求:使用 HTTP 客户端(如 HttpURLConnection 或第三方库)从指定的 URL 获取图片数据流。
  3. 处理响应:检查响应码是否成功(如 HTTP 200),然后从输入流中读取图片数据。
  4. 解码和显示:将获取到的字节数据解码成 Bitmap 对象,并设置到 ImageView 控件上显示。
  5. 处理线程注意:网络操作和 UI 操作都不能在主线程(UI 线程)中进行,否则会导致应用卡顿甚至崩溃(NetworkOnMainThreadException),必须使用后台线程(如 AsyncTaskThread + 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 中使用

Android如何从网络下载图片?-图2
(图片来源网络,侵删)
// 在你的 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 和出色的性能著称。

添加依赖

Android如何从网络下载图片?-图3
(图片来源网络,侵删)
// 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项目

最终建议

  • 对于新项目,优先选择 GlideCoil,如果你使用 Kotlin,Coil 是一个非常现代和高效的选择。
  • 对于学习,可以尝试用 HttpURLConnection 自己实现一次,以了解底层原理。
  • 对于维护老项目,如果已经用了 Picasso,可以继续使用,除非有性能瓶颈。
分享:
扫描分享到社交APP
上一篇
下一篇