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.

⚠️ 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ểm | Nhược điểm |
|---|---|---|
| AsyncTask (cũ) | Dễ hiểu, built-in | Deprecated, leak memory, không handle lifecycle |
| Thread + Handler | Kiểm soát tốt, không deprecated | Verbose, manual lifecycle |
| Coroutines | Structured concurrency, lifecycle-aware, concise | Yêu cầu Kotlin |
| Picasso/Glide/Coil | Cache, placeholder, transform, lifecycle, performance | Thê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