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

Cách đặt Timeout cho AsyncTask trên Android

Giới thiệu

Bài viết minh họa cách chạy AsyncTask với khả năng đặt thời gian chờ (timeout). Mặc dù AsyncTask đã deprecated từ API 30, bài viết vẫn hữu ích cho bảo trì code cũ. Khuyến nghị dùng Coroutines/Flow (Kotlin) hoặc RxJava/ExecutorService cho dự án mới.

Các bước thực hiện

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

Android Studio → File → New Project → Empty Activity.

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/enterTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:layout_centerHorizontal="true"
        android:background="@android:drawable/editbox_background"
        android:inputType="number"
        android:hint="Nhập giây" />

    <Button
        android:id="@+id/btnRun"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/enterTime"
        android:layout_centerHorizontal="true"
        android:text="Chạy AsyncTask" />
</RelativeLayout>

Bước 3: Logic (src/MainActivity.java)

import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    Button button;
    EditText editTextTime;

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

        button = findViewById(R.id.btnRun);
        editTextTime = findViewById(R.id.enterTime);

        button.setOnClickListener(v -> {
            AsyncTaskRunner runner = new AsyncTaskRunner();
            String sleepTime = editTextTime.getText().toString();
            runner.execute(sleepTime);
        });
    }

    private class AsyncTaskRunner extends AsyncTask<String, String, String> {
        ProgressDialog progressDialog;

        @Override
        protected void onPreExecute() {
            progressDialog = ProgressDialog.show(MainActivity.this,
                    "ProgressDialog",
                    "Chờ " + editTextTime.getText().toString() + " giây");
        }

        @Override
        protected String doInBackground(String... params) {
            publishProgress("AsyncTask bắt đầu...");
            String resp;
            try {
                int time = Integer.parseInt(params[0]) * 1000;
                Thread.sleep(time); // Giả lập công việc nặng
                resp = "Đã ngủ " + params[0] + " giây";
            } catch (Exception e) {
                e.printStackTrace();
                resp = e.getMessage();
            }
            return resp;
        }

        @Override
        protected void onProgressUpdate(String... text) {
            Toast.makeText(MainActivity.this, text[0], Toast.LENGTH_SHORT).show();
        }

        @Override
        protected void onPostExecute(String result) {
            progressDialog.dismiss();
            Toast.makeText(MainActivity.this, "AsyncTask kết thúc: " + result, Toast.LENGTH_SHORT).show();
        }
    }
}

Bước 4: Thêm Timeout (Mở rộng)

AsyncTask không có timeout built-in. Cách thực hiện timeout:

// Cách 1: Sử dụng Future.get(timeout) - Chạy AsyncTask trên ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
    // Chạy code doInBackground ở đây
    Thread.sleep(5000);
    return "Done";
});

try {
    String result = future.get(3, TimeUnit.SECONDS); // Timeout 3 giây
} catch (TimeoutException e) {
    future.cancel(true); // Hủy task
    // Xử lý timeout
}

// Cách 2: Kiểm tra isCancelled() trong doInBackground (cooperative cancellation)
@Override
protected String doInBackground(String... params) {
    for (int i = 0; i < 100; i++) {
        if (isCancelled()) break; // Kiểm tra cờ hủy
        // Làm việc từng chút
    }
    return "Done";
}

// Hủy từ UI thread:
asyncTask.cancel(true); // true = mayInterruptIfRunning

Bước 5: Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">
    <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

Nhập số giây (vd: 5) → nhấn Chạy AsyncTask → ProgressDialog hiện ra → sau 5 giây tự đóng.

Cách đặt Timeout cho AsyncTask trên Android Cách đặt Timeout cho AsyncTask trên Android

Tại sao AsyncTask bị deprecated?

  • Rò rỉ bộ nhớ (memory leak) do giữ reference Activity
  • Hành vi không nhất quán giữa các phiên bản Android
  • Khó xử lý lỗi, timeout, hủy bỏ
  • Không hỗ trợ composition, retry, backpressure

Thay thế hiện đại

Ví dụ Coroutines với Timeout

// Kotlin
lifecycleScope.launch {
    try {
        withTimeout(5000) { // 5 giây timeout
            val result = repository.fetchData() // suspend function
            showResult(result)
        }
    } catch (e: TimeoutCancellationException) {
        showError("Hết thời gian chờ")
    }
}
Công nghệNgôn ngữĐặc điểm
Kotlin CoroutinesKotlinStructured concurrency, suspend functions, timeout() built-in
RxJavaJava/KotlinReactive streams, timeout operator, backpressure
ExecutorService + CompletableFutureJavaFuture.get(timeout), cancel(), composition
WorkManagerJava/KotlinDeferred background work, guaranteed execution