So sánh 3 kỹ thuật handler-message handler post asynctask năm 2024

There are two ways to make asynchronous calls: AsyncTask, and Handler plus Thread. I briefly introduced how to use AysncTask to make asynchronous calls in the previous post. In this post, I will explain making asynchronous calls with Handler. I will also compare AsyncTask with Handler.

What is Handler?

A Handler is associated with a thread that creates the handler. It receives and processes messages and

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

0 objects that are sent from a different thread. With Handler, you can run a time-consuming task in a background thread and send messages to the main thread to update the UI. I will use uploading an image to Amazon S3 as an example to explain how to use Handler to make asynchronous calls.

Defining a Thread Handler

An Android application has a default thread — the main thread. When a Handler object is created in

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

4, it’s bonded to the main thread. You override the

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

5 method in order to process messages that are sent to the handler. The

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

6 class consists of a user-defined message code

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

7, two integer value arguments

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

8 and

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

9, and an arbitrary object `AsyncTask`0. Not all fields are set by the sender. Usually, at minimum the message code is set to identify an event.

In the process of uploading an image to AsyncTask`1, you are likely to be interested in three events: when the upload starts, when data is transferring, and when the upload finishes. Therefore, three message codes, S3_UPLOAD_START, S3_UPLOAD_PROGRESS, and S3_UPLOAD_FINISH, are used to identify these events, respectively. Based on the message, you can use a AsyncTask`2 statement to perform a corresponding operation.

Handler handler = new Handler() {

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
    case S3_UPLOAD_START:
        // Set up a progress dialog when upload starts
        dialog = new ProgressDialog(...);
        dialog.setMessage(getString(R.string.uploading));
        dialog.setCancelable(false);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setMax(msg.arg1);
        dialog.show();
        break;
    case S3_UPLOAD_PROGRESS:
        // Update progress
        dialog.setProgress(msg.arg1);
        break;
    case S3_UPLOAD_FINISH:
        // When upload finishes, dismiss progress dialog
        dialog.dismiss();
        break;
    default:
        break;
    }
}
}

Running a Task in a Thread

As a general rule, it’s best to not block the main thread in Android. So time-consuming tasks must be run in a background thread. You can extend the AsyncTask`3 class and implement the time-consuming task inside the AsyncTask4 method, which you kick off by calling AsyncTask5. In order to communicate with the main thread, messages have to be created and sent to the main thread’s handler. `Handler has a convenient method, `AsyncTask`7, to create a message. You can define message code and include additional data, such as number of bytes that are transferred and error string, to the message. You then send this data to the thread handler.

AsyncTask`8 gets the Uri of an image to be uploaded from its constructor. The upload logic is implemented inside the AsyncTask`4 method. You can send an S3_UPLOAD_START message before the upload starts and an S3_UPLOAD_FINISH message when it finishes. A progress listener is attached to the upload request in order to report transfer progress. When the listener is triggered, you compute the total bytes transferred and send an S3_UPLOAD_PROGRESS message with this number to the main thread.

class S3PutObjectThread extends Thread {

private Uri selectedImage;
S3PutObjectThread(Uri selectedImage) {
    this.selectedImage = selectedImage;
}
@Override
public void run() {
    // Get the file path of the image from selectedImage
    ...
    File imageFile = new File(filePath);
    Message msg;
    // Notify the main thread that the upload starts
    msg = handler.obtainMessage(S3_UPLOAD_START);
    handler.sendMessage(msg);
    try {
        // Create a S3 bucket if necessary
        ...
        PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile);
        // Attach a progress listener to the request
        por.setProgressListener(new ProgressListener() {
            int total = 0;
            @Override
            public void progressChanged(ProgressEvent pe) {
                total += (int) pe.getBytesTransfered();
                // Create a message, and set the total bytes transferred to arg1
                Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0);
                // Send progress update to the main thread
                handler.sendMessage(msg);
            }
        });
        // Send the request
        s3Client.putObject(por);
    } catch (Exception e) {
        // Handle exception here.
        ...
    }
    // Upload finishes
    msg = handler.obtainMessage(S3_UPLOAD_FINISH);
    handler.sendMessage(msg);
}
} ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();

Comparison Between Handler and AsyncTask

AsyncTask is a wrapper of Handler and AsyncTask`3. It allows you to conveniently make asynchronous calls without handling threads and handlers. You only need to focus on the four steps in the execution cycle of `AsyncTask: Handler`4, Handler5, Handler6, and Handler`7. However, convenience comes with a price. Here are some limitations.

  • An AsyncTask instance has to be initiated and executed in the main thread.
  • AsyncTask instances cannot communicate with each other easily.
  • It’s not easy to schedule AsyncTask to run at a certain time in the future.
  • The execution order of multiple AsyncTask instances may not be what you think. Since the arrival of Thread`2 (Android 3.0, API level 11), multiple `AsyncTask instances are executed sequentially by default. If you want to change this behavior, you can have them executed on Thread`4 by invoking Thread`5.

Handler and AsyncTask`3 are the underlying implementation of `AsyncTask. They don’t have the limitations of AsyncTask and give you more control over what you want to do.

  • The initialization and execution of a `AsyncTask`3 are not limited to the main thread. They can be done almost everywhere.
  • Communication between threads won’t be a problem with handlers. If you want to send messages to a thread, you can attach a handler to it. There is a handy class `AysncTask`1 that allows you to create a handler for a thread (other than the main thread).
  • Handler has several methods for scheduling messages in the future: `AysncTask`3, `AysncTask`4, `AysncTask`5, and `AysncTask`6.

The choice between AsyncTask and Handler may vary, depending on specific requirements, legacy code, or personal taste. I hope that after reading this post you have a better understanding of Handler and that you properly choose which way to make asynchronous calls in the future.

If you have any questions, please don’t hesitate to post at Mobile Development Forum.

If you like building mobile applications that use cloud services that our customers use on a daily basis, perhaps you would like to join the AWS Mobile SDK and Tools team. We are hiring Software Developers, Web Developers, and Product Managers.