凌峰创科服务平台

Android如何高效下载服务器图片?

核心步骤

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

Android如何高效下载服务器图片?-图1
(图片来源网络,侵删)
  1. 获取网络权限:在 AndroidManifest.xml 中声明需要访问网络的权限。
  2. 发起网络请求:使用 HttpURLConnection 或第三方库(如 OkHttp)向图片的 URL 发送请求。
  3. 获取输入流:从网络连接中获取图片数据的输入流。
  4. 处理输入流:将输入流中的数据读取出来,并保存为字节数组或直接写入文件。
  5. 显示或保存图片:将字节数组解码为 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 类来处理下载逻辑。

Android如何高效下载服务器图片?-图2
(图片来源网络,侵删)
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 中添加权限。

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

使用 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 进行下载,然后使用 GlideBitmapFactory 来显示结果。
  • 对于新的 Kotlin 项目:优先考虑使用 Kotlin 协程 + OkHttp 的方式来处理自定义下载逻辑,代码更简洁、更安全。
分享:
扫描分享到社交APP
上一篇
下一篇