Thứ Bảy, 21 tháng 4, 2012

Hướng dẫn sử dụng thread trong Android


Đối với bất kỳ ứng dụng nào cũng đều phải chú ý đến hiệu năng thực hiện của ứng dụng. Với các thao tác load hình ảnh, dữ liệu, đặc biệt khi có giao tiếp với internet, không nên để các quá trình này chạy trong main thread.
Ví dụ:
                public void onClick(View v){
                                downloadData(uri);
                                Log.i("exam", "Download data completed. Process the next command lines");
                                showOnLayout();
                }
Trong sự kiện onClick, ta cho hàm downloadData chạy trong main thread, hàm Log.i() và hàm showOnLayout() phải chờ cho đến khi hàm downloadData() chạy xong => lúc này bị block UI Thread

·         2 nguyên tắc hàng đầu cần phải nhớ:
1.      Không block UIThread
2.      Không update view ngoài UI Thread (còn gọi là main thread). Việc update view phải được thực hiện trong main thread.
Ví dụ:
public void onClick(View v){
                                new Thread(){
                                                run(){
                                                                downloadData(uri);
                                                }.start();
                                }
                }
ð  Lúc này, việc downloadData() được thực hiện trong Thread riêng, không gây block UI Thread.
Câu hỏi:  trong trường hợp muốn lấy dữ liệu load về vào trong UI Thread thì làm thế nào?
Ví dụ:
onClick{
                                new Thread(){
                                                run(){
                                                                image = downloadData(uri);
                                                                ImageView.setImageSource(image);
//=> đụng vấn đề cập nhật view ngoài main thread.
                                                }.start();
                                }
                }

ð  Có 2 cách xử lý như sau:
Cách 1:
activity.runOnUIThread(new Runnable(){
                                run(){
                                                ImageView.setImageResource(...);
                                }
                });

Cách 2:
               
                ImageView.post(new Runnable()){
                                run(){
                                                ImageView.setImageResource(...);
                                }
                }

ð  Lúc này nảy sinh vấn đề mới: thread quá nhiều => khắc phục bằng cách dùng AsyncTask => UI Tread vẫn có thể được thực hiện, data vẫn có thể download, cũng có thể cập nhật view
Ví dụ:
                class DownloadImage extends AsyncTask<Object1, Object2, Object3>{

       }
Trong đó:
-    Object1: dữ liệu cần thiết để chạy, ví dụ: uri dùng để download data
-    Object2: tham số dưới background muốn bắn lên trong UI => thong báo về tình trang download, phần tram download, có thể thực hiện ngay khi việc load data dưới background chưa hoàn thành.
-    Object3: dùng để cập nhật View khi công việc download hoàn thành.

Khi extends từ AsyncTask cần phải override các phương thức sau đây:

-    Public void onPreExcute(){
ð  thực hiện trong UI Thread, gồm các thao tác trước khi thực hiện công việc ở background. Vd: show thanh progressBar
}
-    public Object3 doInBackground(Object1){
public progress(Object2){}
ð  Thực hiện trong Thread khác, lấy dữ liệu đang download về, lấy thong tin tiến trình, hiển thị % download
ð  Muốn thực hiện cập nhật View khi dữ liệu download về chưa xong (download tới đâu, cập nhật View tới đó), dùng onProgressUpdate()
}
-    Protected void onPostExcute(Object3){
ð  Thực hiện trong UI Thread, sau khi hoàn thành tiến trình. Vd: đóng progressBar lại
}
-    Protected onProgressUpdate(Object2){
ð  Cập nhật UI dựa trên data đang download dỡ dang về.
}

Cuối cùng, ta viết lại hàm ban đầu như sau:

       public void onClick(View v){
              new DownloadImage().excute(Object1);
       }


Bài viết chỉ với mục đích lưu trữ. Cám ơn thầy Bùi Quốc Ánh đã giúp đỡ em thực hiện bài viết này.