凌峰创科服务平台

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

核心流程

无论使用哪种方法,下载图片的基本流程都是相同的:

Android如何从服务器高效下载图片?-图1
(图片来源网络,侵删)
  1. 发起网络请求:向服务器的图片 URL 发送一个 HTTP/HTTPS 请求。
  2. 获取输入流:从服务器的响应中获取图片数据的输入流。
  3. 解码图片:使用 Android 的 BitmapFactory 将输入流解码成 Bitmap 对象。
  4. 处理结果
    • UI 线程:将 Bitmap 显示在 ImageView 上。
    • 后台线程:将 Bitmap 保存到设备存储(如相册或应用私有目录)。
  5. 处理异常:处理网络错误、IO 错误、解码失败等各种异常情况。

使用 HttpURLConnection (传统方式)

这是 Java 标准库自带的网络 API,不依赖第三方库,适合简单的下载任务。

步骤:

  1. 添加网络权限:在 AndroidManifest.xml 中声明。

    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 如果需要写入外部存储(如保存到相册) -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 
                     android:maxSdkVersion="28" />

    注意:从 Android 10 (API 29) 开始,写入外部存储需要 MANAGE_EXTERNAL_STORAGE 权限,但更推荐使用 MediaStore API 来保存图片到相册。

  2. 编写下载代码强烈建议在后台线程(如 AsyncTaskThreadExecutorService)中执行网络请求,以避免阻塞 UI 线程导致应用无响应。

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

    下面是一个使用 ExecutorService 的例子:

    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 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 MainActivity extends AppCompatActivity {
        private static final String IMAGE_URL = "https://example.com/your_image.jpg";
        private ImageView imageView;
        private ExecutorService executorService;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            imageView = findViewById(R.id.imageView);
            // 创建一个单线程的执行器
            executorService = Executors.newSingleThreadExecutor();
            // 启动下载任务
            executorService.execute(new DownloadImageTask());
        }
        private class DownloadImageTask implements Runnable {
            @Override
            public void run() {
                Bitmap bitmap = null;
                HttpURLConnection connection = null;
                InputStream inputStream = null;
                try {
                    URL url = new URL(IMAGE_URL);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.connect();
                    int responseCode = connection.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) {
                        inputStream = connection.getInputStream();
                        // 从输入流解码Bitmap
                        bitmap = BitmapFactory.decodeStream(inputStream);
                    } else {
                        Log.e("DownloadImage", "HTTP Error Code: " + responseCode);
                    }
                } catch (IOException e) {
                    Log.e("DownloadImage", "Error downloading image", e);
                } finally {
                    // 确保流和连接被关闭
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
                // 在UI线程更新UI
                final Bitmap finalBitmap = bitmap;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (finalBitmap != null) {
                            imageView.setImageBitmap(finalBitmap);
                        } else {
                            // 可以设置一个默认图片或显示错误信息
                            imageView.setImageResource(R.drawable.ic_error);
                        }
                    }
                });
            }
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            // 关闭执行器,防止内存泄漏
            if (executorService != null && !executorService.isShutdown()) {
                executorService.shutdown();
            }
        }
    }

使用 OkHttp (强烈推荐)

OkHttp 是目前 Android 开发中最流行的网络库,它更高效、更易用,并内置了对现代网络协议的支持(如 HTTP/2)。

步骤:

  1. 添加依赖:在 app/build.gradle 文件中添加 OkHttp 依赖。

    dependencies {
        implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
    }
  2. 编写下载代码OkHttp 的回调在后台线程执行,所以同样需要切换到 UI 线程来更新界面。

    Android如何从服务器高效下载图片?-图3
    (图片来源网络,侵删)
    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 okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import java.io.IOException;
    import java.io.InputStream;
    public class MainActivity extends AppCompatActivity {
        private static final String IMAGE_URL = "https://example.com/your_image.jpg";
        private ImageView imageView;
        private OkHttpClient okHttpClient;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            imageView = findViewById(R.id.imageView);
            okHttpClient = new OkHttpClient();
            downloadImageWithOkHttp();
        }
        private void downloadImageWithOkHttp() {
            Request request = new Request.Builder()
                    .url(IMAGE_URL)
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(okhttp3.Call call, IOException e) {
                    // 在后台线程执行,处理失败
                    Log.e("OkHttp", "Download failed", e);
                    runOnUiThread(() -> {
                        imageView.setImageResource(R.drawable.ic_error);
                    });
                }
                @Override
                public void onResponse(okhttp3.Call call, Response response) throws IOException {
                    // 在后台线程执行,处理成功
                    if (response.isSuccessful()) {
                        InputStream inputStream = response.body().byteStream();
                        final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        // 切换到UI线程更新ImageView
                        runOnUiThread(() -> {
                            if (bitmap != null) {
                                imageView.setImageBitmap(bitmap);
                            } else {
                                imageView.setImageResource(R.drawable.ic_error);
                            }
                        });
                    } else {
                        Log.e("OkHttp", "Response not successful: " + response.code());
                        runOnUiThread(() -> {
                            imageView.setImageResource(R.drawable.ic_error);
                        });
                    }
                }
            });
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            // 可以在这里关闭OkHttpClient,但通常它作为单例使用
            // okHttpClient.dispatcher().executorService().shutdown();
        }
    }

使用 GlidePicasso (最佳实践 - 专为图片设计)

对于加载和显示图片,直接使用 GlidePicasso 是最简单、最高效的方式,它们不仅处理了下载,还管理了内存缓存、磁盘缓存、图片缩放、线程池等所有复杂细节。

使用 Glide (推荐)

Glide 是 Google 推荐的图片加载库,性能优异,功能强大。

  1. 添加依赖

    dependencies {
        implementation("com.github.bumptech.glide:glide:4.16.0") // 使用最新版本
        annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
    }
  2. 编写代码: 代码极其简洁!只需一行。

    import android.os.Bundle;
    import android.widget.ImageView;
    import androidx.appcompat.app.AppCompatActivity;
    import com.bumptech.glide.Glide;
    public class MainActivity extends AppCompatActivity {
        private static final String IMAGE_URL = "https://example.com/your_image.jpg";
        private ImageView imageView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            imageView = findViewById(R.id.imageView);
            // 一行代码搞定!Glide会自动处理后台下载、缓存和UI更新。
            Glide.with(this)
                 .load(IMAGE_URL)
                 .into(imageView);
        }
    }

使用 Picasso

Picasso 由 Square 公司开发,曾经非常流行,但现在维护较少,新项目更推荐 Glide

  1. 添加依赖

    dependencies {
        implementation("com.squareup.picasso:picasso:2.8") // 使用最新版本
    }
  2. 编写代码: 同样非常简单。

    import android.os.Bundle;
    import android.widget.ImageView;
    import androidx.appcompat.app.AppCompatActivity;
    import com.squareup.picasso.Picasso;
    public class MainActivity extends AppCompatActivity {
        private static final String IMAGE_URL = "https://example.com/your_image.jpg";
        private ImageView imageView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            imageView = findViewById(R.id.imageView);
            // 一行代码搞定
            Picasso.get().load(IMAGE_URL).into(imageView);
        }
    }

总结与对比

特性 HttpURLConnection OkHttp Glide / Picasso
易用性 较低,需要手动处理线程和流 中等,回调简洁 极高,一行代码
功能 基础的 HTTP 请求 强大,支持 HTTP/2,Interceptor 等 专业,自动缓存、缩放、转换、占位图
性能 一般 优秀 优秀,针对图片优化
依赖 无(Android SDK 内置) 需添加第三方库 需添加第三方库
适用场景 简单、无依赖的 Demo 或学习 所有需要网络请求的 App(不仅仅是图片) 任何需要显示网络图片的 App

最终建议

  • 如果你只是想快速学习网络请求的原理:使用 HttpURLConnection
  • 如果你正在开发一个需要网络通信的现代 App:使用 OkHttp,它是网络请求的事实标准。
  • 如果你的 App 需要加载和显示图片直接使用 Glide,这是最佳实践,能让你从繁琐的图片处理细节中解放出来,专注于业务逻辑,它内部其实也使用了 OkHttp 作为默认的 HttpClient
分享:
扫描分享到社交APP
上一篇
下一篇