Bài 1:      Tổng Quan Về Blackberry

I.       Vòng Đời Của Blackberry Application

1.     Bắt đầu ứng dụng

Một ứng dụng thường bắt đầu theo một trong 3 cách sau:

  • Người dùng click vào icon của ứng dụng trên home screen Blacberry.
  • Ứng dụng là một ứng dụng start một cách tự động và chạy khi thiết bị turn on hay sau khi nó reboot.
  • Ứng dụng chạy bởi một ứng dụng khác.

Trong mọi trường hợp, phương thức main là một entry point đầu tiên của ứng dụng. Các thiết bị Blackberry sẽ tạo một tiến trình để gọi phương thức main. Khi phương thức main exit, tiến trình cũng kết thúc và ứng dụng exit. Điều này có nghĩa rằng, nếu bạn muốn ứng dụng làm bất cứ điều gì, bạn sẽ làm điều đó trong phương thức main.

Phương thức main nhận một mảng các đối tượng String làm tham số. Thông thường, mảng này empty, nhưng các tham số có thể được gán nếu bạn định nghĩa chúng trong properties project hay nếu chúng được gán bởi một tiến trình khác mà tiến trình này start ứng dụng.

2.     Tạo ứng dụng

  • Tất cả các ứng dụng BlackBerry muốn trình diễn một UI cho người dùng phải kế thừa lớp UiApplication. Bạn chỉ có thể tạo ra một thể hiện của UiApplication cho tất cả tiến trình ứng dụng; BlackBerry runtime sẽ ném một ngoại lệ nếu bạn cố gắng khởi tạo một tiến trình thứ hai.
  • Thậm chí, các ứng dụng không có UI phải kế thừa từ lớp net.rim.device.api.system.Application, tuy nhiên các kiểu ứng dụng như vậy không được thảo luận trong bài này.
  • Bạn có thể truy cập thể hiện của ứng dụng bằng cách sử dụng phương thức static UiApplication.getUiApplication(). Phương thức này sẽ trả về một thể hiện của lớp ứng dụng, vì vậy ở bất kỳ đâu trong ứng dụng HelloWorld (có một lớp HelloWorldApp kế thừa UiApplication), công việc sau được phép:

HelloWorldApp helloWorld = (HelloWorldApp)UiApplication.getUiApplication();

3.     Gọi event thread

  • Các thread sự kiện được bắt đầu bởi hệ điều hành BlackBerry, nhưng nó không bắt đầu xử lý sự kiện và vẽ các giao diện cho đến khi bạn yêu cầu nó một cách tường minh. Bạn yêu cầu nó thực thi bằng cách gọi phương thức UiApplication.enterEventDispatcher(). Một khi phương thức này được gọi, thread đó đi vào phương thức main() và làm nhiệm vụ lắng nghe các input từ UI và vẽ giao diện lên màn hình. Bạn vẫn sẽ nhận được một cơ hội để làm việc trên thread này, nhưng thông thường, các hoạt động của nó được lên kế hoạch(schedule) với hệ điều hành BlackBerry.
  • Phương thức enterEventDispatcher() sẽ không trả lại cho toàn bộ vòng đời ứng dụng của bạn, do đó, nếu có bất cứ điều gì main thread phải làm trước khi gọi phương thức này (ví dụ, thực hiện một số công việc khởi tạo), thì bạn phải thực hiện các công việc khởi tạo trước(có thể khởi tạo trước lời gọi enterEventDispatcher() hay khởi tạo trong Constructor của ứng dụng).

4.     Xử lý sự kiện

Ứng dụng này đáp ứng các sự kiện nhập vào bàn phím, trackball, hoặc chuyển động màn hình cảm ứng và nhấp chuột và các sự kiện khác như thông điệp hệ thống.

5.     Thoát khỏi ứng dụng

Nói chung, một ứng dụng BlackBerry exit khi màn hình cuối cùng được remove từ display stack (bằng cách đóng nó). Bạn có thể gọi phương thức System.exit() để thoát khỏi ứng dụng, nhưng cách này được khuyến nghị nên tránh và nên clean up ứng dụng đang tồn tại bằng cách đóng tất cả các màn hình. Khi thoát ứng dụng, tất cả các trạng thái ứng dụng sẽ được clean up, và lần tiếp theo nếu người dùng nhấp vào icon của ứng dụng thì phương thức main sẽ được gọi trở lại với một tiến trình mới.

II.       Threading Và Event Thread

BlackBerry UI API là đơn luồng(single-thread). Điều này có nghĩa rằng tất cả các thông tin cập nhật giao diện người dùng và sự kiện được xử lý bởi cùng một thread, hay chính xác hơn, phải được thực hiện trong khi giữ khóa sự kiện, mà hầu hết thời gian được tổ chức bởi thread UI. Nó có một vài tác động đối với các ứng dụng BlackBerry: các thread khác không thể truy cập trực tiếp vào UI mà không đạt được khóa sự kiện một cách rõ ràng(một ngoại lệ sẽ được ném ra nếu bạn cố gắng), và nếu bạn thực hiện một hoạt động trên event thread mà nó chiếm nhiều thời gian, toàn bộ giao diện người dùng sẽ tạm dừng trong khi hoạt động đó đang diễn ra.

1.     Nhận biết khi ứng dụng của bạn hoạt động trên event thread

Bạn luôn luôn có thể nhận biết nếu code của bạn đang được thực hiện bởi event thread bằng cách gọi phương thức UiApplication.isEventDispatchThread(). Tuy nhiên, thông thường chúng ta không gọi phương thức này. Một nguyên tắc nhỏ để nhận biết ứng dụng đang làm việc trên event thread:

  • nếu code được gọi trực tiếp từ phương thức run() của thread mà ta tự tạo ra, có nghĩa là ứng dụng không hoạt động trên event thread.
  • nếu code được gọi bởi hệ thống để đáp ứng các input từ người dùng(ví dụ như click vào menuitem hay button), thì có nghĩa là ứng dụng đang hoạt động trên event thread.

2.     Cập nhật UI từ các thread khác

Tất nhiên, có nhiều khi bạn muốn một sự kiện trong thread khác để được phản ánh(reflect) trong UI, ví dụ tạo một thread để xử lý quá trình download file. Để làm điều đó, có một vài phương pháp.

  • Phương pháp đầu tiên sử dụng UiApplication.invokeLater() hoặc UiApplication.invokeAndWait() để báo cho thread UI để chạy một số code trên event thread ở cơ hội có sẵn tiếp theo.
  • Phương pháp thứ hai là thu được khóa sự kiện UI bằng cách đồng bộ hóa trên các đối tượng trả về bởi UiApplication.getEventLock(). Chúng ta sẽ ứng dụng phương pháp đầu tiên bằng cách sửa đổi ứng dụng Hello World để bắt đầu một thread mới, và thread này sẽ thêm các message vào LabelField của main screen sau mỗi 5 giây.

Lưu ý Cả hai phương thức invokeLater() và invokeAndWait() cùng hoạt động như nhau – đó là đưa vào hàng đợi một thể hiện của java.lang.Runnable để được thực thi trên event thread. Sự khác biệt là invokeLater() trả về ngay lập tức, trong khi invokeAndWait() không trả lại cho đến khi phương thức run() của Runnable đã kết thức việc thực thi,  và do đó, block thread gọi nó.

Đầu tiên, thay thế biến cục bộ labelField thành một biến thành viên, sau đó thêm một phương thức appendLabelText().

public class HelloWorldMainScreen extends MainScreen {

private LabelField labelField;

public HelloWorldMainScreen() {

labelField = new LabelField(“Hello World”);

add(labelField);

}

public void appendLabelText(String text) {

labelField.setText(labelField.getText() + “\n” + text);

}

}

Bởi vì phương thức appendLabelText() gọi phương thức LabelField.setText(), lời gọi này chỉ có thể được thực hiện event thread. Nếu bạn cố gắng gọi phương thức này trực từ thread khác, một ngoại lệ sẽ được ném.

Bây giờ, chúng ta sẽ tạo một lớp MainScreenUpdaterThread kế thừa từ Thread để thực hiện công việc cập nhật. Nó sẽ lặp từ 1 đến 10. Mỗi lần lặp, nó sẽ đợi 5 giây và sau đó thêm một đoạn text và LabelField.

package blog.mobileprogramming;

import net.rim.device.api.ui.UiApplication;

public class MainScreenUpdaterThread extends Thread {

HelloWorldMainScreen mainScreen;

public MainScreenUpdaterThread(HelloWorldMainScreen mainScreen) {

this.mainScreen = mainScreen;

}

public void run() {

for (int i = 0; i < 10; i++) {

try {

Thread.sleep(5000);

} catch (InterruptedException ex) {

}

// Queue a new task on the event thread

UiApplication.getUiApplication().invokeLater(new Runnable() {

public void run() {

mainScreen.appendLabelText(“Update”);

}

});

}

}

}

Để cập nhật thực sự UI, chúng ta sử dụng lớp nội bộ vô danh(anonymous inner class), đó là lớp mà chúng ta định nghĩa tại thời điểm khởi tạo nó. Lớp vô danh này gọi một phương thức mà cần được gọi trên event thread, đó là phương thức appendLabelText().

Bây giờ, trong constructor của HelloWorldMainScreen, chúng ta thực hiện như sau:

public HelloWorldMainScreen() {

labelField = new LabelField(“Hello World”);

add(labelField);

MainScreenUpdaterThread thread = new MainScreenUpdaterThread(this);

thread.start();

}

Cuối cùng là ta chạy ứng dụng, out put của nó như hình sau đây:

3.     Sử dụng event lock

Chúng ta sẽ sử dụng một phương pháp thứ 2 để cập nhật UI từ một thread khác; đó là bên trong lớp MainScreenUpdateThread, chúng ta goi phương thức getEventLock() để nhận event lock.

public void run() {

for (int i = 0; i < 10; i++) {

try {

Thread.sleep(5000);

} catch (InterruptedException ex) {

}

// Ensure we have the event lock

synchronized(UiApplication.getEventLock()) {

mainScreen.appendLabelText(“Update”);

}

}

}

Phương pháp này cho ra cùng một kết quả với phương pháp đầu tiên.

III.       Thiết Lập Ứng Dụng Chạy Background

Theo mặc định, một ứng dụng sẽ thoát khi màn hình cuối cùng của nó được đóng; tuy nhiên, bạn có thể override hành vi này để thiết lập một ứng dụng chạy background, có nghĩa là UI sẽ không được hiển thị, nhưng ứng dụng vẫn tiếp tục chạy trong khi người dùng thực hiện các công việc khác. Ứng dụng chạy ở dạng Background chỉ thực sự hữu ích trong các tình huống sau:

  • Bạn muốn ứng dụng của bạn kiểm tra định kỳ các thay đổi trên thiết bị hoặc cho các sự kiện. Ví dụ, ứng dụng BlackBerry Message luôn luôn chạy để kiểm tra các mail đến.
  • Bạn muốn tải về theo định kỳ thông tin mới từ mạng. Một số ứng dụng thời tiết và chứng khoán sử dụng phương pháp này.
  • Bạn cần duy trì kết nối đến server, chẳng hạn các ứng dụng chat hay sử dụng phương pháp này.

1.     Phát hiện ứng dụng đang ở Background hay Foreground

Một ứng dụng có thể được chuyển sang Background nếu một người dùng khi nhấn phím red phone hay chuyển sang các task một cách tường minh. Bạn có thể phát hiện trạng thái background này bằng cách override phương thức UiApplication.deactivate(). Tương tự như vậy, bạn có thể phát hiện các ứng dụng của bạn trở về Foreground bằng cách gọi phương thức  UiApplication.activate().

Bây giờ, sữa đổi HelloWorldApp để hiển thị một message khi Hello World đi vào Background hay Foreground

public class HelloWorldApp extends UiApplication {

private HelloWorldMainScreen mainScreen;

public HelloWorldApp() {

mainScreen = new HelloWorldMainScreen();

pushScreen(mainScreen);

}

public void deactivate() {

mainScreen.appendLabelText(“Went to background”);

}

public void activate() {

mainScreen.appendLabelText(“Came to foreground”);

}

/**

* @param args

*/

public static void main(String[] args) {

HelloWorldApp app = new HelloWorldApp();

app.enterEventDispatcher();

}

}

Khi chạy ứng dụng này, ngay lập tức sẽ thấy một message ”Came to foreground”.
Message được hiển thị bởi vì những kích hoạt ban đầu của ứng dụng. Nhấn nút đỏ, và sau đó chọn icon một vài lần để chuyển ứng dụng từ foreground sang background.

2.     Chuyển ứng dụng sang Background

Bạn có thể chuyển ứng dụng sang Background bằng cách gọi phương thức UiApplication.requestBackground(). Để làm cho Hello World chuyển sang Background thay vì exit khỏi ứng dụng khi người dùng đóng màn hình chính, hãy ghi đè phương thức Screen.close() trong HelloWorldMainScreen:

public class HelloWorldMainScreen extends MainScreen {

//…

public void close() {

UiApplication.getUiApplication().requestBackground();

}

}

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

Các bài viết liên quan:
Tích hợp Blackberry vào Eclipse
Lập Trình Với Blackberry – Phần 2