Search…

Multithreading - AsyncTask trong Android

12/11/20206 min read
Multithreading và cách thức sử dụng multithreading trong lập trình Android.

Giới thiệu

Trong ứng dụng Android có nhiều tác vụ (task) chạy song song với nhau để đảm bảo ứng dụng tận dụng được hết khả năng của CPU và mang lại hiệu suất cho ứng dụng. Vậy để lập trình được nhiều tác vụ chạy song song trong ứng dụng Android như thế nào thì bài viết này sẽ hướng dẫn chi tiết.

Multithreading là gì?

Multithreading là khái niệm dùng để nhắc đến những tác vụ chạy song song với nhau, giúp quá trình tương tác của người dùng với ứng dụng không bị gián đoạn.

Khi 1 ứng dụng được start lên sẽ có 1 thread được gọi là main thread hay UI thread được chạy. Từ thread này có thể tạo ra những thread khác để làm những tác vụ chiếm nhiều thời gian. Nếu cố tình xử lý những tác vụ này trên UI thread mà không tạo ra thread khác có thể dẫn đến làm treo UI hoặc crash ứng dụng.

Những trường hợp nên sử dụng multithreading:

  • Thao tác với hệ thống mạng (network).
  • Đọc, ghi file.
multithreading asynctask trong android
Mô phỏng multithreading

Multithreading trong Android

Trong Android, khi tạo 1 thread để xử lý 1 tác vụ trong phương thức run() và gọi phương thức start() để chạy tác vụ này thì trong phương thức run(), không được cập nhật các giá trị trực tiếp lên UI mà phải gửi message về UI thread để nó cập nhật các giá trị lên UI, application sẽ ném ra 1 ngoại lệ nếu vẫn thực hiện điều này.

Mô hình làm việc multithreading trong Android.
Mô hình multithreading trong Android

Để hỗ trợ người dùng lập trình multithreading dễ dàng và nhanh gọn, Google giới thiệu 1 class dùng để đáp ứng công việc này đó là AsyncTask.

Sử dụng AsyncTask trong Android

AsyncTask có các phương thức chính như sau:

onPreExecute

onPreExecute là phương thức được chạy đầu tiên khi task được bắt đầu, phương thức này làm việc trên UI thread.

doInBackground

doInBackground là phương thức chạy background thực hiện các tác vụ, phương thức này chạy trên background thread vì thế không được cập nhật giá trị lên UI trong phương thức này.

onPostExecute

onPostExecute là phương thức được gọi ngay sau khi phương thức doInBackground kết thúc, đối số của phương thức này chính là kết quả được trả về từ phương thức doInBackground, phương thức này làm việc trên UI thread.

Mô hình multithreading trong Android
Mô hình multithreading trong Android

Ngoài 3 phương thức chính trên còn có 2 phương thức nữa đó là:

publishProgress

Được sử dụng trong phương thức doInBackground() để cập nhật quá trình làm việc lúc runtime và gọi phương thức onProgressUpdate trên UI thread để update giá trị trong khi doInBackground vẫn làm việc.

onProgressUpdate

Nếu phương thức publishProgress() được gọi ở trong phương thức doInBackground() thì cập nhật giá trị lên UI. Phương thức này làm việc trên UI thread.

3 đối số mà khi tạo class extends từ AsyncTask là:

AsyncTask<Params, Progress, Result>

Param

Là kiểu của đối số được truyền vào phương thức doInBackground()

Progress

Là kiểu biến dùng để cập nhật giá trị trong quá tình doInBackground() chạy, giá trị này được truyền vào phương thức onProgressUpdate()

Result

Là kiểu trả về của phương thức doInBackground() và là kiểu của đối số của phương thức onPostExcute().

class DownloadTask extends AsyncTask<String, Integer, String>{
   @Override
   protected void onPreExecute() {
      super.onPreExecute();
   }

   @Override
   protected String doInBackground(String... params) {
      return null;
   }

   @Override
   protected void onProgressUpdate(Integer...values) {
      super.onProgressUpdate(values);
   }

   @Override
   protected void onPostExecute(String s) {
      super.onPostExecute(s);
   }
}

Ví dụ demo

Ví dụ 1 ứng dụng sẽ cập nhật giá trị từ 0 đến 100.000 lên ProgressBar sau khi người dùng nhấn button Start.

File xml định nghĩa layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.nguyennghia.asynctaskworking.MainActivity">

    <ProgressBar
        android:id="@+id/pb_counter"
        style="?android:attr/progressBarStyleHorizontal"
        android:indeterminate="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_message"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:layout_gravity="center"
        android:text="Start"
        android:id="@+id/btn_counter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

class UpdateProgressBarTask

class UpdateProgresBarTask extends AsyncTask<Integer, Integer, Void>{
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        tvMessage.setText("Update");
    }

    @Override
    protected Void doInBackground(Integer... params) {
        int n = params[0];
        for(int i = 0; i < n; i++)
            publishProgress(i);
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        pbCounter.setProgress(values[0]);
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        tvMessage.setText("End");
        btnCounter.setEnabled(true);
    }
}

Để sử dụng UpdateProgressBarTask tạo 1 thể hiện của nó và gọi phương thức excute() và truyền đối số phù hợp để chạy task.

Ở đây khi người dùng nhấn vào Button thì Task bắt đầu chạy.

btnCounter.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
         new UpdateProgresBarTask().execute(MAX);
         btnCounter.setEnabled(false);
      }
});

Source MainActivity.java

package com.example.nguyennghia.asynctaskworking;
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.ProgressBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private ProgressBar pbCounter;
    private TextView tvMessage;
    private Button btnCounter;
    private static final int MAX = 100000;

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

        pbCounter = (ProgressBar)findViewById(R.id.pb_counter);
        pbCounter.setMax(MAX);
        tvMessage = (TextView)findViewById(R.id.tv_message);
        btnCounter = (Button)findViewById(R.id.btn_counter);
        btnCounter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new UpdateProgresBarTask().execute(MAX);
                btnCounter.setEnabled(false);
            }
        });
    }

    class UpdateProgresBarTask extends AsyncTask<Integer, Integer, Void>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            tvMessage.setText("Update");
        }

        @Override
        protected Void doInBackground(Integer... params) {
            int n = params[0];
            for(int i = 0; i < n; i++)
                publishProgress(i);
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            pbCounter.setProgress(values[0]);

        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            tvMessage.setText("End");
            btnCounter.setEnabled(true);
        }
    }
}

Tải source code bằng 1 trong cách sau:

1 ví dụ nữa về AsyncTask có nhiệm vụ download hình ảnh và hiển thị lên ImageView

class DownImageTask extends AsyncTask<String, Void, Bitmap>{
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.e(TAG, "onPreExecute:");

    @Override
    protected Bitmap doInBackground(String... strings) {
        Log.e(TAG, "doInBackground:" );
        Bitmap bm = null;
        try {
            URL aURL = new URL(strings[0]);
            URLConnection conn = aURL.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            bm = BitmapFactory.decodeStream(bis);
            bis.close();
            is.close();
        } catch (IOException e) {
            Log.e("Hub","Error getting the image from server : " + e.getMessage().toString());
        }
        return bm;

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        Log.e(TAG, "onPostExecute:");
        super.onPostExecute(bitmap
    imvAvatar.setImageBitmap(bitmap);
    }
}

Hay AsyncTask download nội dung của trang web.

class DownloadContentWeb extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... strings) {
        URL url = null;
        StringBuilder stringBuilder = null;
        try {
            url = new URL(strings[0]);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 2. Open InputStream to connection
            conn.connect();
            InputStream in = conn.getInputStream();
            // 3. Download and decode the string response using builder
            stringBuilder = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }
}

Mọi thao tác thông qua network phải cấp quyền internet trong manifest.xml

<uses-permission android:name="android.permission.INTERNET" />
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024