ĐỒNG HỒ BẤM GIỜ TRONG ANDROID
Đồng hồ bấm giờ là một ứng dụng khá quen thuộc cho dân lập trình. Một chiếc đồng hồ bấm giờ trong Android được lập trình như thế nào ? Để hiểu được ta cần tìm hiểu đối tượng Handler.
Handler là một đối tượng dùng để tạo ra một tiến trình (thread) phụ trong Android. Cũng giống như các ngôn ngữ bậc cao khác, Android cho phép người lập trình sử dụng nhiều tiến trình cùng một lúc. Song song với tiến trình chính (Main thread) dùng để quản lý các activity, intent receiver,… thì ta vẫn có thể tạo một tiến trình thứ cấp thông qua Handler. Khi sử dụng Handler, ta cần chú ý đến đối tượng Runnable, đây là đối tượng được kế thừa từ lớp Thread, nó cho phép chương trình kích hoạt một hành động nào đó khi một tiến tình được bắt đầu và vẫn chưa kết thúc (dễ hình dung, thì có thể coi đây là vòng lặp không xác định).
Một Handler cho phép bạn gửi và xử lý các đối tượng Message và Runnable đã được kết hợp với một MessageQueue. Mỗi Hanlder được liên kết với một thread duy nhất và MessageQueue của thread đó. Khi ta tạo một Handler mới, nó được gắn với message queue của thread tạo ra nó – từ đó trở đi, nó sẽ gửi các message và các runnable tới message queue đó và thực thi chúng khi chúng ra khỏi message queue. Trong bài viết này, chúng ta sẽ cùng nhau tạo một Đồng hồ bấm giờ thông qua Handler.
Mục lục bài viết:
1. Phân tích yêu cầu của một Đồng hồ bấm giờ
2. Khởi tạo giao diện đồng hồ bấm giờ
3. Tiến hành coding Đồng hồ bấm giờ
1. Phân tích yêu cầu của Đồng hồ bấm giờ
Về cơ bản, một chiếc đồng hồ bấm giờ sẽ có các chức năng chính: Bắt đầu đếm giờ, Tạm dừng, Ngừng hẳn. Và trên giao diện đồng hồ sẽ có các thông tin về số phút, giây, mili-giây. Và từ những phân tích thực tế này, ta sẽ cần ba Button tương ứng với 3 chức năng chính và một cái TextView nhằm hiển thị thời gian trên đồng hồ.
2. Khởi tạo giao diện đồng hồ bấm giờ
Như những gì ta đã phân tích, đầu tiên, ta cần thiết kế một TextView có id là txtTimer để lưu thời gian thực thi của đồng hồ. Đồng thời cũng tạo 3 Button với id lần lượt là btnStart, btnPause, btnStop (tương ứng với Start, Pause, Stop). Trong activty_main.xml, ta code như sau:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.baobao.demodonghobamgio.MainActivity"> <TextView android:text="00:00:00" android:textSize="60dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="120dp" android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:id="@+id/txtTimer" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="START" android:id="@+id/btnStart" android:layout_alignTop="@+id/txtTimer" android:layout_marginTop="120dp" android:layout_alignParentLeft="true" android:layout_marginLeft="20dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="PAUSE" android:id="@+id/btnPause" android:layout_alignTop="@+id/txtTimer" android:layout_marginTop="120dp" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="STOP" android:id="@+id/btnStop" android:layout_alignTop="@+id/txtTimer" android:layout_marginTop="120dp" android:layout_alignParentRight="true" android:layout_marginRight="20dp" /> </RelativeLayout>
3. Tiến hành coding Đồng hồ bấm giờ
Sau khi thiết kế giao diện, ta sẽ coding bên MainActivity.java để xử lý các yêu cầu của Đồng hồ bấm giờ. Đầu tiên, ta cần xác định được những biến toàn cục trong ứng dụng này là gì. Ngoại trừ 3 Button và 1 TextView ở phần giao diện, ta cần thêm 3 biến Long để lưu giá trị của thời điểm bắt đầu, thời điểm mà ta tạm dừng và thời điểm của hệ thống ứng dụng. Và một biến boolean nhằm xác định đồng hồ đang chạy hay đang dừng. Đồng thời ta cần một Handler và một biến Runnable. Như vậy ta cần khởi tạo các biến toàn cục như sau:
Button btnStop,btnStart, btnPause; TextView txtTimer; long lStartTime, lPauseTime, lSystemTime = 0L; Handler handler = new Handler(); boolean isRun; Runnable runnable = new Runnable() { @Override public void run() { lSystemTime = SystemClock.uptimeMillis() - lStartTime; long lUpdateTime = lPauseTime + lSystemTime; long secs = (long)(lUpdateTime/1000); long mins= secs/60; secs = secs %60; long milliseconds = (long)(lUpdateTime%1000); txtTimer.setText(""+mins+":" + String.format("%02d",secs) + ":" + String.format("%03d",milliseconds)); handler.postDelayed(this,0); } };
Đối với biến runnable, ta cần override lại hàm run() của nó. Trong đó lSystemTime là khoảng thời gian kể từ thời điểm hiện hành của hệ thống được bậc so với thời điểm đồng hồ được lệnh bắt đầu chạy. lUpdateTime được xác định từ lSystemTime với lPauseTime (nếu đồng hồ được lện Stop thì lPauseTime sẽ bằng 0). Các biến secs, mins, milliseconds sẽ xác định thời gian của đồng hồ. Kế đến, ta sẽ hiển thị chúng thông qua hàm setText(). Để handler tiếp tục như một vòng lặp liên tục, ta phải thêm lệnh handler.postDelayed(this,0) ở cuối hàm run().
Ở hàm onCreate ta tiến hành ánh xạ các Button và TextView bên giao diện như sau:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnStart = (Button)findViewById(R.id.btnStart); btnStop = (Button)findViewById(R.id.btnStop); btnPause = (Button)findViewById(R.id.btnPause); txtTimer = (TextView)findViewById(R.id.txtTimer); }
Việc cuối cùng là xây dựng các chức năng thông qua 3 Button. Ta sẽ bỏ 3 sự kiện click 3 Button này vào các hàm nhỏ và gọi lại chúng trong hàm onStart() như sau:
@Override protected void onStart() { super.onStart(); clickStart(); clickStop(); clickPause(); } void clickStart() { btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(isRun) return; isRun = true; lStartTime = SystemClock.uptimeMillis(); handler.postDelayed(runnable, 0); } }); } void clickStop() { btnStop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(!isRun) return; isRun = false; lPauseTime = 0; handler.removeCallbacks(runnable); } }); } void clickPause() { btnPause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(!isRun) return; isRun = false; lPauseTime += lSystemTime; handler.removeCallbacks(runnable); } }); }
Hàm clickStart(), tương ứng với chức năng bắt đầu chạy đồng hồ khi người dùng nhấn vào nút START. Đơn giản, ở hàm này, tôi sẽ kiểm tra nếu đồng hồ đang chạy thì tôi sẽ vô hiệu hàm này bằng câu lệnh thoát return. Ngược lại, tôi sẽ gán cho isRun có giá trị là true (xác nhận đồng hồ bắt đầu chạy). Đồng thời, lStartTime được gán với thời điểm hiện hành của hệ thống. Cuối cùng, khi tôi click Start như vậy thì tôi sẽ “phất cờ” cho runnable của handler chạy bằng lệnh:
handler.postDelayed(runnable, 0);
Đối với clickStop(), tôi sẽ kiểm tra, nếu đồng hồ đang ngừng thì tôi sẽ vô hiệu chức năng này. Ngược lại, tôi lại gán isRun bằng false. lPauseTime = 0 (do đây là Stop nên không có nhu cầu lưu lại khoảng thời gian đồng hồ chạy trước đó). Cuối cùng là lệnh ngừng runnable:
handler.removeCallbacks(runnable);
Cũng thế, khi clickPause() tôi sẽ làm những việc như clickStop(). Tuy nhiên, tôi sẽ lưu lại khoảng thời gian đồng hồ chạy trước đó để chuẩn bị cho lượt clickStart() tiếp theo, bằng lệnh:
lPauseTime += lSystemTime;
Như vậy, ta đã tạo thành công ứng dụng Đồng hồ bấm giờ. Thông qua đó, ta đã thấy được cách làm việc của Handler cũng như Runnable trong Android.