Bài 37: Xử lý đa tiến trình bằng AsyncTask


Các bài tập 34, 35,36 Tui đã trình bày về xử lý đa tiến trình dùng Handler class. Trong bài tập này Tui muốn trình bày một kỹ thuật mới đó là cách xử lý bằng AsyncTask. Nó hơi hơi phức tạp 1 xí, nhưng cũng không có khó khăn gì … vấn đề là bạn hãy làm đi làm lại thật nhiều lần để hiểu thấu đáo về kỹ thuật này. Khi đã hiểu rồi thì bạn không cần dùng Handler class nữa mà nên dùng luôn AsyncTask. Đặc biệt khi xử lý đa tiến trình mà lại muốn kiểm tra kết quả trả về của tiến trình thì ta phải dùng AsyncTask.

Tui sẽ trình bày sơ qua về lý thuyết của AsyncTask trước khi vào những ví dụ cụ thể:

Bạn xem hình chụp 1 sau (bạn nhìn vào các đường mũi tên trỏ các parameter):

mul9

và hình số 2 sau như (nhìn vào các đường chỉ parameter):

mul10

– Tui sẽ giải thích kỹ về vấn đề này:

Trong AsyncTask<Params, Progress, Result>  có 3 đối số là các Generic Type:

Params: Là giá trị ((biến) được truyền vào khi gọi thực thi tiến trình và nó sẽ  được truyền vào doInBackground

Progress: Là  giá trị (biến) dùng để update giao diện diện lúc tiến trình thực thi, biến này sẽ được truyền vào hàm onProgressUpdate.

Result: Là biến dùng để lưu trữ kết quả trả về sau khi tiến trình thực hiện xong.

Những đối số nào không sử dụng trong quá trình thực thi tiến trình thì ta thay bằng Void.

Thông thường trong 1 AsyncTask sẽ chứa 4 hàm, đó là :

onPreExecute() : Tự động được gọi đầu tiên khi tiến trình được kích hoạt.

doInBackground(): Được thực thi trong quá trình tiến trình chạy nền, thông qua hàm này để ta gọi hàm onProgressUpdate để cập nhật giao diện (gọi lệnh publishProgress). Ta không thể cập nhật giao diện trong hàm doInBackground().

onProgressUpdate (): Dùng để cập nhật giao diện lúc runtime

onPostExecute(): Sau khi tiến trình kết thúc thì hàm này sẽ tự động sảy ra. Ta có thể lấy được kết quả trả về sau khi thực hiện tiến trình kết thúc ở đây.

Trong 4 hàm trên thì hàm doInBackground() bắt buộc phải tồn tại, còn các hàm khác có thể khuyết, nhưng theo Tui các bạn nên sử dụng đầy đủ 4 hàm đã nêu.

Đối với AsyncTask thì ta cần tạo một lớp kế thừa từ AsyncTask, sau đó từ MainActivity ta gọi hàm execute() của tiến trình này là OK.

Sau đây Tui sẽ làm một ví dụ cập nhật ProgressBar bằng AsyncTask (tui đã làm bằng Handler class trong bài 34):

async1

– Bài ví dụ trên chỉ đơn giản là cập nhật Progressbar (không quản lý kết quả trả về), nên đối số 1 và đối số 3 ta để Void, đối số 2 ta để Integer.

– Bạn tạo một Project như hình chụp dưới đây (với MyAsyncTask kết thừa từ AsyncTask):

async2

– Layout XML tương tự như bài 34:












– Bạn xem Tui giải thích source code MyAsyncTask dưới này:


package tranduythanh.com;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MyAsyncTask
extends AsyncTask {
 //khai báo Activity để lưu trữ địa chỉ của MainActivity
 Activity contextCha;
 //constructor này được truyền vào là MainActivity
 public MyAsyncTask(Activity ctx)
 {
 contextCha=ctx;
 }
 //hàm này sẽ được thực hiện đầu tiên
 @Override
 protected void onPreExecute() {
 // TODO Auto-generated method stub
 super.onPreExecute();
 Toast.makeText(contextCha, "onPreExecute!",
 Toast.LENGTH_LONG).show();
 }
 //sau đó tới hàm doInBackground
 //tuyệt đối không được cập nhật giao diện trong hàm này
 @Override
 protected Void doInBackground(Void... arg0) {
 for(int i=0;i<=100;i++)
 {
 //nghỉ 100 milisecond thì tiến hành update UI
 SystemClock.sleep(100);
 //khi gọi hàm này thì onProgressUpdate sẽ thực thi
 publishProgress(i);
 }
 return null;
 }
 /**
 * ta cập nhập giao diện trong hàm này
 */
 @Override
 protected void onProgressUpdate(Integer... values) {
 // TODO Auto-generated method stub
 super.onProgressUpdate(values);
 //thông qua contextCha để lấy được control trong MainActivity
 ProgressBar paCha=(ProgressBar) contextCha
 .findViewById(R.id.progressBar1);
 //vì publishProgress chỉ truyền 1 đối số
 //nên mảng values chỉ có 1 phần tử
 int giatri=values[0];
 //tăng giá trị của Progressbar lên
 paCha.setProgress(giatri);
 //đồng thời hiện thị giá trị là % lên TextView
 TextView txtmsg=(TextView)
 contextCha.findViewById(R.id.textView1);
 txtmsg.setText(giatri+"%");
 }
 /**
 * sau khi tiến trình thực hiện xong thì hàm này sảy ra
 */
 @Override
 protected void onPostExecute(Void result) {
 // TODO Auto-generated method stub
 super.onPostExecute(result);
 Toast.makeText(contextCha, "Update xong roi do!",
 Toast.LENGTH_LONG).show();
 }
 /**
 * tui làm 2 hàm ví dụ này về params trong Android
 */
 public void goividu()
 {
 viduParamschoham();
 viduParamschoham(5);
 viduParamschoham(5,6);
 viduParamschoham(5,6,5,6,7,8,9,0,0);
 }
 /**
 * dấu ... dùng khai báo param
 * tức là ta truyền bao nhiêu đối số cũng được
 * ds trở thành mảng 1 chiều
 * @param ds
 */
 public void viduParamschoham(int ... ds)
 {
 //test chơi...
 int pt0=ds[0];//có lỗi nếu như không truyền đối số nào
 for(int n:ds)
 {
 System.out.println(n);
 }
 //hoặc
 for(int i=0;i<ds.length;i++)
 {
 System.out.println(ds[i]);
 }
 }
}

– Ở đoạn code trên có 2 hàm cuối cùng là Tui viết thêm vào để giải thích cho việc sử dụng params trong Java, bạn cố gắng đọc để hiểu.

– Tiến trình MyASyncTask sẽ được thực thi khi bên MainActivity ta gọi phương thức: .execute()

source code MainActivity:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

Button btnStart;
 //khai báo MyAsyncTask
 MyAsyncTask mytt;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 btnStart=(Button) findViewById(R.id.button1);
 btnStart.setOnClickListener(new OnClickListener() {

 @Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });

 }
 private void doStart()
 {
 //truyền this (chính là MainActivity hiện tại) qua Child Thread
 mytt =new MyAsyncTask(this);
 //Kích hoạt Tiến trình
 //khi gọi hàm này thì onPreExecute của mytt sẽ thực thi trước
 mytt.execute();
 }
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.activity_main, menu);
 return true;
 }

}

– Vô cùng đơn giản….. mọi thứ đẩy về cho Child Thread.

– Bạn có thể tải coding mẫu ở đây: http://www.mediafire.com/download/9or85fvafatp14o/LearnMultiThreading_ProgressBar_AsyncTask.rar

– Trong bài kế tiếp Tui sẽ làm 1 ví dụ AsyncTask nhưng có quản lý kết quả trả về, bài này tương đối phức tạp, các bạn chú ý theo dõi

– Chúc các bạn thành công!

16 responses

  1. nguyễn huy hoàng | Reply

    cho tôi hỏi là : khi chạy 1 app, và đối tượng 1 sẽ hoạt động không dừng ( ví dụ : di chuyển qua lại mãi mãi) . và hành động khác được chạy xen vào( ví dụ : ta touch vào đối tượng thì đối tượng đó xoay 1 vòng) và hành động 1 dừng lại chờ hành động 2 thực thi xong thì chạy tiếp. thì ta phải dùng asynctask như thế nào ak ?

  2. e cảm ơn thầy những bài viết của thầy rất hay 😀 e chúc thầy có sức khỏe ,hi vọng thầy sẽ ra tip những bài mới 😀

  3. Thầy ơi cho e hỏi là 2 cái hàm này “public void goividu()” và “public void viduParamschoham(int … ds)” dùng để làm gì?

    1. – Ở đoạn code trên có 2 hàm cuối cùng là Tui viết thêm vào để giải thích cho việc sử dụng params trong Java, bạn cố gắng đọc để hiểu.

  4. Lê Xuân Đoàn | Reply

    asynctask có phải tương tự như thread ko a?
    muốn xóa asynctask đã tạo ra thì làm ntn ạ?

  5. Quá hay luôn thầy ơi,tks thầy chia sẻ tỷ mỷ như vậy :))

  6. @Duẫn : thầy nói quá rõ ràng rồi bạn,mình xin nói lại theo cách hiểu của mình nha ,nếu như bạn muốn tạo một hàm nào đó dùng chung có các tham số cùng kiểu dl,thay vì bạn mắc công chèn 1,2 hay 3 tham số vàomột hàm,rồi một lúc nào đó cần làm bài toán khác với số lượng các tham số khác nữa như vậy mắc thời gian tạo lại,vì vậy bạn tạo một hàm với kdl bạn muốn rồi dùng “…” như ngụ ý bạn muốn đưa vào bao nhiêu tham số cũng được tùy theo bài toán của bạn,ví dụ ở vd của thầy,bạn muốn sử dụng hàm viduParamschoham(int … ds) cho bài toán với 1 tham số truyền vào,khi khác lại dùng với 2, hay 3 hoặc lên đến 4,5 tham số gì đó thì bạn dùng lại hàm này vẫn ok,mình chỉ nói theo cách của mình hiểu thôi,hy vọng rằng đúng :))

  7. Bài viết của thầy rất hay,rất dễ hiểu ạ

  8. Em có câu hỏi là: Làm sao để Pause được Thread bằng AsyncTask?

  9. Multi Thread nhưng lại ko Asynchronous như Handler. Ko hiểu tại sao ?

  10. Em có câu hỏi là : Em dùng Hanlder class và xử lý runtime. Tức là em dùng Hanlder class để call 1 URL trên webserice sau đó. Nếu có giá trị thì em sẽ xử lý. Em cho lặp lại trong 1s. Nếu không có dữ liệu thì thôi. Nhưng sau khi dùng 1 thời gian thì ứng dụng của em bị treo. Em không hiểu gì sao. Nó chỉ pause lại Mainacitivy. Mong thấy giúp em. Hoặc em muốn hỏi là có cách nào khác để sync android không? Em mới học android

    1. Hi em,
      Em request liên tục 1s thì server nào mà chịu nổi … Server tự cấm em luôn. Với lại em cần liệt kê chi tiết chút xíu về service và cách em tương tác

  11. mọi người cho mình hỏi mình tạo 1 cái app music, mình dùng asyntask để cập nhật thanh seekbar, bằng cách setProgress (getCurrentDuration ), không hiểu tại sao bản nhạc khi play rất là giật, mình cảm ơn !

  12. E hơi nhầm lẫn chút là: Khi nào nên sử dụng AsyncTask và khi nào thì dùng Service. Vì cả hai đều chạy ngầm

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.