睿诚科技协会

Android如何从网络高效下载图片?

核心步骤

无论使用哪种方法,下载网络图片的基本逻辑都包含以下几个步骤:

Android如何从网络高效下载图片?-图1
(图片来源网络,侵删)
  1. 获取网络权限:在 AndroidManifest.xml 中声明网络访问权限。
  2. 发起网络请求:使用 HTTP 客户端(如 HttpURLConnection 或第三方库)从指定 URL 获取图片的输入流。
  3. 读取数据:从输入流中读取图片的二进制数据。
  4. 处理数据:将读取到的数据转换为 Bitmap 对象。
  5. 显示图片:在 ImageView 中显示 Bitmap
  6. 处理线程非常重要! 网络操作不能在主线程(UI线程)中进行,否则会导致应用卡顿甚至崩溃(NetworkOnMainThreadException)。

使用 HttpURLConnection (原生方法)

这是 Android SDK 自带的方法,无需添加额外依赖,适合简单的下载任务。

添加网络权限

app/src/main/AndroidManifest.xml 文件中添加:

<uses-permission android:name="android.permission.INTERNET" />

创建下载工具类

为了代码的复用和清晰,我们创建一个 ImageDownloadUtil 类。

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class ImageDownloadUtil {
    private static final String TAG = "ImageDownloadUtil";
    /**
     * 使用 AsyncTask 下载图片 (已过时,但能很好地理解原理)
     * @param imageView  要显示图片的 ImageView
     * @param urlString  图片的 URL
     */
    public static void downloadImage(ImageView imageView, String urlString) {
        new DownloadImageTask(imageView).execute(urlString);
    }
    private static class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
        private final ImageView imageView;
        public DownloadImageTask(ImageView imageView) {
            this.imageView = imageView;
        }
        @Override
        protected Bitmap doInBackground(String... urls) {
            String urlDisplay = urls[0];
            Bitmap bitmap = null;
            try {
                InputStream inputStream = new URL(urlDisplay).openStream();
                // 使用 BitmapFactory 解析输入流
                bitmap = BitmapFactory.decodeStream(inputStream);
            } catch (IOException e) {
                Log.e(TAG, "Error downloading image", e);
            }
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

在 Activity 或 Fragment 中使用

import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    private String imageUrl = "https://example.com/path/to/your/image.jpg"; // 替换成你的图片URL
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = findViewById(R.id.imageView);
        // 调用工具类下载图片
        ImageDownloadUtil.downloadImage(mImageView, imageUrl);
    }
}

activity_main.xml 布局文件:

Android如何从网络高效下载图片?-图2
(图片来源网络,侵删)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:scaleType="centerCrop" />
</LinearLayout>

使用现代协程 (Kotlin - 推荐方式)

如果你使用 Kotlin,协程是处理异步任务的现代、简洁且强大的方式,它避免了回调地狱,代码可读性极高。

添加网络权限 (同上)

添加协程依赖

app/build.gradle.kts (或 app/build.gradle) 文件中添加:

// 在 dependencies 闭包中添加
implementation 'androidx.core:core-ktx:1.12.0' // 通常已包含
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' // 提供 lifecycleScope

使用协程下载图片

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Log
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.HttpURLConnection
import java.net.URL
class MainActivityKotlin : AppCompatActivity() {
    private lateinit var imageView: ImageView
    private val imageUrl = "https://example.com/path/to/your/image.jpg"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.imageView)
        // 在 Activity 的 lifecycleScope 中启动一个协程
        // 当 Activity 销毁时,协程会自动取消
        lifecycleScope.launch {
            val bitmap = downloadImage(imageUrl)
            if (bitmap != null) {
                imageView.setImageBitmap(bitmap)
            }
        }
    }
    private suspend fun downloadImage(urlString: String): Bitmap? = withContext(Dispatchers.IO) {
        // withContext(Dispatchers.IO) 将这段代码切换到 IO 线程执行
        var bitmap: Bitmap? = null
        var connection: HttpURLConnection? = null
        try {
            val url = URL(urlString)
            connection = url.openConnection() as HttpURLConnection
            connection.doInput = true
            connection.connect()
            val inputStream = connection.inputStream
            bitmap = BitmapFactory.decodeStream(inputStream)
        } catch (e: Exception) {
            Log.e("MainActivityKotlin", "Error downloading image", e)
        } finally {
            connection?.disconnect()
        }
        bitmap
    }
}

使用第三方库 (强烈推荐)

在实际开发中,我们强烈建议使用成熟的第三方库,它们不仅简化了代码,还解决了缓存、图片加载(如圆角、圆形)、内存管理等一系列复杂问题。

推荐库:Glide (Google 官方推荐)

Glide 是一个快速、高效的图片加载和缓存库。

Android如何从网络高效下载图片?-图3
(图片来源网络,侵删)

添加依赖

app/build.gradle.kts (或 app/build.gradle) 文件中添加:

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.16.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' // 如果使用 Java
    // 对于 Kotlin,使用 kapt
    // kapt 'com.github.bumptech.glide:compiler:4.16.0'
}

在 XML 中添加网络权限 (同上)

在 Activity 或 Fragment 中使用

Glide 的使用极其简单,一行代码即可。

// Java
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
public class MainActivityGlide extends AppCompatActivity {
    private ImageView mImageView;
    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);
        mImageView = findViewById(R.id.imageView);
        // 使用 Glide 加载图片
        Glide.with(this)
             .load(imageUrl)
             .into(mImageView);
    }
}
// Kotlin
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
class MainActivityGlideKotlin : AppCompatActivity() {
    private lateinit var imageView: ImageView
    private val imageUrl = "https://example.com/path/to/your/image.jpg"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.imageView)
        // 使用 Glide 加载图片
        Glide.with(this)
             .load(imageUrl)
             .into(imageView)
    }
}

Glide 的优点:

  • 自动缓存:内存缓存和磁盘缓存都自动处理,无需手动管理。
  • 生命周期集成:自动感知 Activity/Fragment 的生命周期,在销毁时自动取消请求,避免内存泄漏。
  • 强大的 API:支持圆角、圆形、裁剪、淡入淡出等多种变换。
  • 高性能:高效的解码和内存管理策略。

重要注意事项和最佳实践

  1. 不要在主线程做网络操作:这是 Android 开发的铁律。AsyncTaskThreadHandlerThreadKotlin CoroutinesRxJava 都可以用来在后台线程执行网络请求。
  2. 处理图片内存:高清图片可能非常大,直接加载到内存
分享:
扫描分享到社交APP
上一篇
下一篇