Một ứng dụng hoạt động trơn tru, mạnh mẽ đã thực sự thành công hay chưa?

Tôi khẳng định là chưa, một ứng dụng mạnh mẽ với một giao diện người dùng nghèo sẽ không được phổ biến, cho dù nó chạy trên máy tính để bàn hoặc các thiết bị nhỏ. Đối với người dùng desktop, với nhiều năm kinh nghiệm, có thể chịu được các giao diện có hơi vụng về. Tuy nhiên đối với các thiết bị nhỏ, nó rơi vào các chủng loại thiết bị điện tử tiêu dùng, nơi mà người dùng đang kỳ vọng cao hơn và khắt khe hơn rất nhiều.

Nhiều MIDlets tạo ra các kết nối vào mạng. Bởi vì các mạng không dây ngày nay rất chậm, một câu hỏi đặt ra với giao diện người dùng là: loại feedback  nào người sử dụng xem? Và bạn sẽ thấy, trả lời câu hỏi này sẽ dẫn đến một cuộc thảo luận về đa luồng(multithread).

Bài viết này mô tả một số phương pháp tiếp cận để cung cấp các thông tin phản hồi trong thời gian hoạt động của người sử dụng mạng, và để quản lý các thread thích hợp hơn. Ví dụ đầu tiên là tạo ra một MIDlet đơn giản mà không cung cấp thông tin phản hồi, sau đó dần dần cải tiến nó.

Ví dụ 1:

import java.io.*;

import javax.microedition.io.*;

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

public class PrimitiveMIDlet extends MIDlet

implements CommandListener {

private Display mDisplay;

private Form mMainForm;

private Command mExitCommand, mConnectCommand;

public void startApp() {

mDisplay = Display.getDisplay(this);

if (mMainForm == null) {

mMainForm = new Form(“PrimitiveMIDlet”);

mMainForm.append(new StringItem(“”,

“Select Connect to make a network connection”));

mExitCommand = new Command(“Exit”, Command.EXIT, 0);

mConnectCommand = new Command(“Connect”, Command.SCREEN, 0);

mMainForm.addCommand(mExitCommand);

mMainForm.addCommand(mConnectCommand);

mMainForm.setCommandListener(this);

}

mDisplay.setCurrent(mMainForm);

}

public void commandAction(Command c, Displayable s) {

if (c == mExitCommand)

notifyDestroyed();

else if (c == mConnectCommand)

connect();

}

private void connect() {

String url = getAppProperty(“NetworkThreading.URL”);

try {

// Query the server and retrieve the response.

HttpConnection hc = (HttpConnection)Connector.open(url);

InputStream in = hc.openInputStream();

// Pull back the server’s response. If a content length is not

// specified, we’ll just read 255 bytes.

int contentLength = (int)hc.getLength();

if (contentLength == 1) contentLength = 255;

byte[] raw = new byte[contentLength];

int length = in.read(raw);

// Clean up.

in.close();

hc.close();

// Show the response to the user.

String s = new String(raw, 0, length);

Alert a = new Alert(“Response”, s, null, null);

a.setTimeout(Alert.FOREVER);

mDisplay.setCurrent(a, mMainForm);

}

catch (IOException ioe) {

Alert a = new Alert(“Exception”, ioe.toString(), null, null);

a.setTimeout(Alert.FOREVER);

mDisplay.setCurrent(a, mMainForm);

}

}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

}

Ở ví dụ trên, màn hình của PrimitiveMIDlet rất đơn giản. Nó trình bày một màn hình chính. Từ đó, bạn có thể thoát khỏi ứng dụng hoặc thực hiện một kết nối mạng. Nếu bạn chọn kết nối mạng, các kết quả từ máy chủ xuất hiện như được hiển thị.

Vậy ở ví dụ này nói lên điều gì?

PrimitiveMIDlet không cung cấp bất kỳ thông tin phản hồi trực quan nào trong khi nó đang kết nối mạng. Đây không phải là một vấn đề rắc rối trong một môi trường phát triển, nơi mà một giả lập của thiết bị được kết nối với một số máy chủ đang chạy trên máy cục bộ của lập trình viên. Kết nối sẽ mất rất ít thời gian để kết nối thành công. Tuy nhiên, trong thế giới thực, sự chậm trễ sẽ được chú ý nhiều hơn. Người dùng sẽ nhìn chằm chằm vào một ứng dụng trong khoảng năm hay mười giây trong khi MIDlet các kết nối tới máy chủ.

Người lập trình đã sử dụng thread của hệ thống để thực hiện xử lý kết nối mạng. Khi người dùng nhấn một command, thì hệ thống sẽ gọi phương thức commandAction(). Thread gọi phương thức này phụ thuộc vào hệ thống chứ không phụ thuộc vào developer. Điều này dĩ nhiên không có gì để bàn đến nếu phương thức thực hiện nhanh chóng, tuy nhiên trong trường hợp này, việc kết nối mạng sẽ làm cho main(hay system) thread bị ngắt quãng để đợi việc kết nối mạng trong một thời gian dài.

Khi chạy MIDlet, hệ thống sẽ gọi từ một trong những thread riêng của nó, mà ở đây là main thread. Khi các phương thức xử lý sự kiện của MIDlet như: startApp(), pauseApp(), destroyApp() được gọi, thì chúng sẽ được chạy bên trong một thread của hệ thống, gọi là main thread. Các phương thức của bạn phải trả về một cách nhanh chóng để main thread có thể tiếp tục làm các công việc khác của nó. Các công việc mà không thể được hoàn thành nhanh chóng phải được chuyển ra khỏi thread của hệ thống.

Hình dưới đây sẽ minh họa main thread gọi các  phương thức trong MIDlet:

Trong hình vẽ trên, ở phía bên trái( tương ứng màu vàng ), đại diện cho code thực thi bên dưới MIDP. Và phía bên phải(tương ứng màu xanh), đại diện cho code mà bạn viết, đường thẳng màu đỏ đại diện cho các sự kiện phát sinh bởi main thread theo thời gian.

Ở sơ đồ tiếp theo, bạn sẽ được nhìn thấy một lỗ hổng ở PrimitiveMIDlet, đó là main thread sẽ bị treo một khoảng thời gian trong khi đang thực hiện phương thức connect() của MIDlet. Trong khi main thread đợi cho việc kết nối mạng, nó không thể thực hiện các thao tác khác. Lúc đó, giao diện người dùng bị đóng băng, hay màn hình bị treo khi người dùng thực hiện tiếp các thao tác khác trong ứng dụng; kết quả là người dùng sẽ rất thất vọng.

Bạn thấy bài viết này thế nào?

Các bài liên quan:
Sử dụng Thread hiệu quả trong J2ME – Phan 2
Sử dụng Thread hiệu quả trong J2ME – Phan 3
Sử dụng Thread hiệu quả trong J2ME – Phan 4