3. Thread
●
●
●
Thread là một đơn vị thực thi đồng thời.
Thông thường chúng ta sử dụng Thread trong
tình huống có một công việc xử lý với khối
lượng lớn hay thời gian thực hiện kéo dài.
Có hai cách để tạo thread:
●
●
●
Kế thừa lớp Thread và override phương thức run().
Tạo đối tượng Runnable và hiện thực phương thức
run().
Để thực thi một thread gọi phương thức start()
4. Shared Agenda
●
Trong Android, việc xử lý tuần tự các message
và task được thực hiện thông qua Shared
Agenda Thread pattern.
●
●
●
Agenda là thuật ngữ trong ngành trí tuệ nhân tạo và
thường được dùng trong AI Robotic.
Pipline (xử lý ống) là thuật ngữ tương tự trong Hệ
điều hành. Thuật ngữ này thường được sử dụng
trong lĩnh vực xử lý đồng thời.
Ý tưởng chính là dùng Shared Agenda thread để gửi
message & task một cách tuần tự từ background
thread đến UI thread và ngược lại.
5. Shared Agenda
●
Shared Agenda (SA):
●
●
●
Một thread có queue chứa các message & task.
Message (android.os.Message) là một cấu trúc dữ
liệu dùng trao đổi, xử lý.
Task (java.lang.Runnable) là một đơn vị CPU có thể
thực thi được.
●
Nếu trong queue của SA không có task thì SA block.
●
Các thread khác có thể gửi message đến SA.
●
Nếu trong queue của SA có message thì các message
này sẽ được xử lý tuần tự.
6. Shared Agenda
●
Looper (android.os.Looper):
●
●
Looper được dùng để biến một thread thành SA với
một message queue.
Handler (android.os.Handler):
●
Handler: Là kênh để các thread khác gửi message
đến SA.
Thread
Thread
Looper
Message Queue
Handler
Message
7. Handler
●
●
Handler được dùng để schedule message
(Message) và task (Runnable).
Để gửi một task vào queue ta dùng các phương
thức sau:
●
●
postAtTime(Runnable, long)
●
●
post (Runnable)
postDelayed(Runnable,long)
Các task đang nằm trong queue sẽ được thực thi ngay
khi chúng được nhận.
8. Handler
●
Để gửi một message vào queue ta dùng các
phương thức sau:
●
sendEmptyMessage(int)
●
sendMessage(Message)
●
sendMessageAtTime(Message,long)
●
sendDelayedMessage(Message,long)
9. Looper
●
Looper quản lý message queue cho thread.
●
Looper.prepare() dùng để tạo ra message queue.
●
Looper.loop() dùng để xử lý các message khi
Thread đang thực thi.
10. Sample Code
Handler mhandler = null;
public run(){
try{
//Biến thread thành shared agenda
Looper.prepare();
//Bind Handler
mhandler = new Handler(){
//Bind vào thread hiện tại
public void handleMessage(Message msg) {
// xử lý message nhận được
}
}
//Start looper
Looper.loop();
}catch(Throwable ex){
//xử lý ngoại lệ
}
}
//Ở một thread khác có thể truy xuất mhandler
//Thì từ thread này có thể gửi message và task
mhandler.post(new Runnable(){
@Override
public void run(){
//Thực thi nghiệp vụ
}
});
11. UI Thread
●
Trong mỗi ứng dụng Android có một thread đặc
biệt gọi là UI Thread:
●
UI Thread tự động start khi chương trình start.
●
UI Thread nhận các sự kiện của người dùng.
●
UI thread là một shared agenda.
●
Để đăng ký Handler cho Looper của UI Thread sử dụng:
Handler mHandler = new Handler(Looper.getMainLooper())
●
Lưu ý:
●
Không block UI Thread (ANR)
●
Không thay đổi các thành phần của UI bên ngoài thread UI.
●
Sử dụng các phương pháp đồng bộ thread khi dùng chung dữ
liệu
12. Bài tập 04
Hiện thực chương trình Android có các chức năng
sau đây:
●
Đọc dữ liệu từ một tập tin ở SD card.
●
Hiển thị progress bar khi đang đọc dữ liệu.
●
Trình bày nội dung tập tin lên TextView khi đọc
xong dữ liệu.
Yêu cầu: Sử dụng background thread để đọc dữ
liệu và dùng Handler để giao tiếp với UI thread.
13. AsyncTask
●
●
AsyncTask là phương tiện khác để xử lý công
việc sử dụng background thread và giao tiếp với
UI thread mà không dùng Thread hay Handler.
Để sử dụng AsynTask, định nghĩa lớp con:
class MyAsyncTask extends AsyncTask<Params, Progress, Result>{
@Override
protected Result doInBackground(Params... params) {
return null;
}
}
●
Params → Tham số của doInBackground (Array)
●
Progress → Tham số của onProgressUpdate
●
Result → Tham số của onPostExecute và giá trị trả
về của doInBackground
15. Ví dụ
Hiện thực chương trình Android có các chức năng
sau đây sử dụng kỹ thuật AsyncTask:
●
●
●
Background thread đếm từ 0 → 10. Cập nhật kết
quả lên màn hình (UI Thread).
Khi đến 10 thì hiển thị “Kết thúc”.
Trong khi đếm thì hiển thị “busy indicator” ở
thanh tiêu đề.
16. Ví dụ
public class AsyncTaskSample extends Activity {
private TextView txtCounter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Cài đặt chế độ hiển thị busy indicator ở thanh tiêu đề
//Dòng lệnh này phải được gọi trước câu lệnh setContentView.
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);
this.txtCounter = (TextView) findViewById(R.id.TextView01);
this.txtCounter.setText("" + 0);
//Gọi thực thi AsyncTask
new CounterAsyncTask().execute("args as doInBackground");
}
17. Ví dụ
class CounterAsyncTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
//Hiển thị busy indicator
setProgressBarIndeterminateVisibility(true);
}
@Override
protected Boolean doInBackground(String... params) {
Log.v("AIT", "param:" + params[0]);
for (int i = 0; i <= 10; i++) {
try {
//Gửi thông tin lên UI Thread
publishProgress(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
return true;
}
18. Ví dụ
class CounterAsyncTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
//Hiển thị busy indicator
setProgressBarIndeterminateVisibility(true);
}
@Override
protected Boolean doInBackground(String... params) {
Log.v("AIT", "param:" + params[0]);
for (int i = 0; i <= 10; i++) {
try {
//Gửi thông tin lên UI Thread
publishProgress(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
return true;
}
20. Loader
●
●
●
●
●
Loader cung cấp một cơ chế load dữ liệu một cách
bất đồng bộ.
Giám sát nguồn cung cấp dữ liệu, mỗi khi nguồn
cung cấp dữ liệu có sự thay đổi thì nó tự động cập
nhật dữ liệu ở kết quả.
Mỗi Activity hay Fragment đều có Loader.
Mỗi khi được tạo lại do cấu hình hệ thống thay đổi
thì nó tự động khôi phục lại đúng điểm mà loader
trước đó đang giữ và do đó không truy vấn lại dữ
liệu.
Loader có từ API 11+ (sử dụng support library cho
phiên bản cũ).
21. Loader API
Tên Class
Mô tả
LoaderManager
Là lớp trừu tượng dùng để quản lý các loader instance gắn với
Activity hay Fragment.
Loader
Là lớp trừu tượng dùng để load dữ liệu bất đồng bộ, giám sát sự
thay đổi của nguồn cung cấp dữ liệu để cập nhật dữ liệu kết quả.
Các lớp thừa kế là AsyncTaskLoader.
AsyncTaskLoader
Là lớp trừu tượng, cung cấp một AsyncTask để thực hiện một công
việc chiếm thời gian dài hay load dữ liệu lớn. Lớp thừa kế là
CursorLoader
CursorLoader
Là lớp con của AsyncTaskLoader, là cơ chế tốt nhất để load data từ
provider.
LoaderManager.LoaderCallbacks
Để tương tác với LoaderManager, client cần hiện thực các callback
sau đây:
●onCreateLoader → Tạo mới loader từ ID được cung cấp.
●onLoadFinished → Gọi khi loader đã hoàn thành xong nhiệm vụ.
●onLoaderReset → Gọi khi loader bị reset.
22. Sử dụng Loader
●
Các bước để sử dụng Loader trong ứng dụng
Android:
●
Hiện thực một Activity hay Fragment.
●
Get một instance của LoaderManager.
●
●
●
Sử dụng CursorLoader (hoặc hiện thực một custom
loader).
Hiện thực các callbacks trong
LoaderManager.Callbacks.
Sử dụng dữ liệu từ loader để trình bày lên GUI.
23. Loader Example
public class LoadersSampleActivity extends ListActivity implements LoaderCallbacks<Cursor>
{
private SimpleCursorAdapter mAdapter = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Tạo adapter rỗng dùng để trình bày dữ liệu chúng ta load được lên ListView.
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, null,
new String[] { "address", "body" },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
//Khởi tạo Loader với ID = 0
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
// TODO Auto-generated method stub
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
}
}
24. Loader Example
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri = Uri.parse("content://sms");
// Tạo ra trả về đối tượng CursorLoader.
return new CursorLoader(this, baseUri, null, null, null,null);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
//Điền dữ liệu từ Cursor sang mAdapter
mAdapter.swapCursor(arg1);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
//Reset adapter
mAdapter.swapCursor(null);
}