Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách tải xuống hình ảnh từ URL trong Android?

Giới thiệu

Hướng dẫn tải ảnh từ Internet về Bitmap và hiển thị lên ImageView bằng AsyncTask (cách cổ điển) và giới thiệu cách hiện đại hơn.

Các bước thực hiện (Cách cổ điển - AsyncTask)

Bước 1: Tạo dự án mới

Khởi tạo project Android Studio.

Bước 2: Giao diện (res/layout/activity_main.xml)

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true">
    </ImageView>
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/image"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/button" />
</RelativeLayout>

Bước 3: String resource (res/values/strings.xml)

<resources>
    <string name="app_name">Sample</string>
    <string name="menu_settings">Settings</string>
    <string name="button">Tải ảnh xuống</string>
</resources>

Bước 4: Logic tải ảnh (src/MainActivity.java)

import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {
    String url = "https://images.pexels.com/photos/1226302/pexels-photo-1226302.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500";
    ImageView image;
    Button button;
    ProgressDialog progressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        image = findViewById(R.id.image);
        button = findViewById(R.id.button);

        button.setOnClickListener(v -> new DownloadImage().execute(url));
    }

    private class DownloadImage extends AsyncTask<String, Void, Bitmap> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setTitle("Hướng dẫn tải ảnh");
            progressDialog.setMessage("Đang tải...");
            progressDialog.setIndeterminate(false);
            progressDialog.show();
        }

        @Override
        protected Bitmap doInBackground(String... urls) {
            String imageUrl = urls[0];
            Bitmap bitmap = null;
            try {
                InputStream input = new java.net.URL(imageUrl).openStream();
                bitmap = BitmapFactory.decodeStream(input);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            image.setImageBitmap(result);
            if (progressDialog != null && progressDialog.isShowing()) {
                progressDialog.dismiss();
            }
        }
    }
}

Bước 5: AndroidManifest.xml (cần quyền INTERNET)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Chạy ứng dụng

Kết nối thiết bị (có internet), nhấn Run. Nhấn "Tải ảnh xuống" → ProgressDialog hiện → Ảnh hiển thị khi xong.

Cách tải xuống hình ảnh từ URL trong Android?


⚠️ Lưu ý: AsyncTask ĐÃ DEPRECATED (API 30+)

Google khuyến cáo không dùng AsyncTask cho project mới. Dưới đây là các cách hiện đại:

1. Coroutines + Kotlin (Khuyên dùng cho Kotlin)

// build.gradle: implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0+'
lifecycleScope.launch(Dispatchers.IO) {
    try {
        val bitmap = URL(url).openStream().use { BitmapFactory.decodeStream(it) }
        withContext(Dispatchers.Main) {
            imageView.setImageBitmap(bitmap)
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

2. Thread/Executor + Handler (Java hiện đại)

ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());

executor.execute(() -> {
    try {
        Bitmap bitmap = BitmapFactory.decodeStream(new URL(url).openStream());
        handler.post(() -> imageView.setImageBitmap(bitmap));
    } catch (Exception e) {
        e.printStackTrace();
        handler.post(() -> Toast.makeText(this, "Lỗi tải ảnh", Toast.LENGTH_SHORT).show());
    }
});

3. Thư viện load ảnh (Rất khuyên dùng - Picasso/Glide/Coil)

// Picasso (đơn giản nhất)
Picasso.get().load(url).into(imageView);

// Glide (mạnh hơn, hỗ trợ GIF)
Glide.with(this).load(url).into(imageView);

// Coil (Kotlin first)
imageView.load(url) // Cần dependency coil

So sánh

CáchƯu điểmNhược điểm
AsyncTask (cũ)Dễ hiểu, built-inDeprecated, leak memory, không handle lifecycle
Thread + HandlerKiểm soát tốt, không deprecatedVerbose, manual lifecycle
CoroutinesStructured concurrency, lifecycle-aware, conciseYêu cầu Kotlin
Picasso/Glide/CoilCache, placeholder, transform, lifecycle, performanceThêm dependency

Best Practices

  • Luôn dùng thư viện (Picasso/Glide/Coil) cho production app
  • Placeholder/error image để UX tốt
  • Resize ảnh phù hợp kích thước ImageView (tránh OOM)
  • Disk cache + Memory cache tự động của thư viện
  • Xử lý lỗi mạng gracefully
  • Coil cho Kotlin, Glide cho Java/Kotlin mixed, Picasso cho đơn giản