Ở bài trước, các bạn đã được hướng dẫn để tạo ra một game, ở phần này chúng ta sẽ bàn đến network. Một trong các đặc điểm chính của mobile, đó là khả năng được kết nối ở mọi nơi và mọi lúc. Bên cạnh việc bạn đạt được điểm số cao(high score); kết quả trả về có thể được chia sẽ trên internet. Với JavaME, bạn có thể dễ dàng truy cập vào những đặc điểm giao tiếp sau:

  • HTTP
  • Socket
  • SMS
  • Bluetooth

Bài viết này sẽ giải thích cách sử dụng giao thức HTTP để gửi dữ liệu high score đến server và làm thế nào để sử dụng chức năng gửi SMS để mời các người khác cùng tham gia game.

Connector

Đặc điểm chính khi sử dụng các chức năng network là lớp Connector. Lớp này cho phép tạo ra các đối tượng Connection thông qua các phương thức tĩnh:

  • open(String name)
  • open(String name, int mode)

Tham số name chính là một chuỗi URL để xác định kết nối mà bạn muốn tạo ra. Tương ứng với giao thức ở URL là một kiểu đối tượng Connection sẽ được tạo ra. Sau đây là danh sách các URL và các đối tượng Connection tương ứng:

  • http://www.server.com“: Trả về một kết nối HTTP sử dụng lớp HttpConnection.
  • “socket://server.com:8080”: trả về một kết nối TCP/IP.
  • “btsp://2343434d3434”: trả về một kết nối Bluetooth.
  • “sms://+351910000000”: trả về một kết nối SMS đến một số điện thoại +351910000000.

Trong các giao thức kể trên, thì giao thức HTTP được tất cả các thiết bị hỗ trợ JavaME hỗ trợ; các giao thức khác thì phụ thuộc vào thiết bị. Tuy nhiên hầu hết các loại điện thoại mới đều hỗ trợ tất cả các giao thức trên.

Lưu ý rằng khi bạn cố gắng mở bất kỳ loại kết nối nào, người dùng các ứng dụng được thông báo từ hệ thống quản lý ứng dụng và cung cấp tùy chọn để chấp nhận hoặc từ chối các yêu cầu kết nối. Nếu bị từ chối yêu cầu kết nối, ứng dụng sẽ nhận được một SecurityException và kết nối không được thiết lập.

HTTP

Có thể nói, kết nối HTTP là một loại kết nối phổ biến nhất trong tất cả các loại kết nối. Để tạo kết nối HTTP trong J2ME, bạn làm như sau:

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

public class Network {
  public byte[] httpConnection(String url, byte[] dataToSend) throws IOException
  {
    HttpConnection hc = null;
    // Prepare Connection
    hc = (HttpConnection) Connector.open(url, Connector.READ_WRITE);
    [...]
  }
}

Các kết nối HTTP được đại diện bởi lớp HttpConnection. Lớp này có ba trạng thái:

  • Setup nơi mà các tham số yêu cầu được định nghĩa.
  • Connected nơi mà bạn có thể gửi và nhận dữ liệu. Bạn cần gửi dữ liệu trước khi bạn có thể nhận bất kỳ sự trả lời nào.
  • Closed sau khi bạn đọc dữ liệu và kết nối được kết thúc.

Khi bạn nhận đối tượng HttpConnection từ Connector, nó rơi vào trạng thái Setup. Sau đó, bạn có thể cấu hình các tham số yêu cầu. Một trong những lựa chọn chính đó là phương thức yêu cầu HTTP(POST, GET hoặc HEAD).

public byte[] httpConnection(String url, byte[] dataToSend) throws IOException {
[..]
if (dataToSend == null){
hc.setRequestMethod( HttpConnection.GET );
} else {
hc.setRequestMethod( HttpConnection.POST );
}
[…]
}

Bạn cũng có thể cấu hình các thuộc tính yêu cầu, chẳng hạn như kiểu nội dung mà bạn sẽ gửi trong trường hợp POST:

hc.setRequestProperty("Content-type", "application/octet-stream" );

Sau khi bạn đã cấu hình kết nối, bạn có thể bắt đầu gửi dữ liệu. Khi bạn bắt đầu quá trình này, bạn không thể thay đổi các tham số yêu cầu. Nếu kết nối được thiết lập, bạn bắt đầu nhận được dữ liệu.

public byte[] httpConnection(String url, byte[] dataToSend) throws IOException {
[…]
if (dataToSend != null){
// Write Data
OutputStream os = hc.openOutputStream();
os.write( dataToSend );
os.close();
}
// gets answer from  server
int rc = hc.getResponseCode();
// check http response
if (rc == HttpConnection.HTTP_OK){
// Read Data
InputStream in = hc.openInputStream();
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
int ch;
while ((ch = in.read()) != -1) {
tmp.write(ch);
}
data = tmp.toByteArray();
in.close();
hc.close();
}

return data;
}

Xây dựng phương thức httpConnection() coi như đã xong, tiếp theo nó được cài đặt trong game để gửi high score đến server. Bạn hãy thêm một command vào màn hình high score:

public Displayable initScoreForm() {
  [...]
  cmdSendHighScore = new Command("Send to Server", Command.ITEM, 1);
  highScoreForm.addCommand(cmdSendHighScore);
  [...]
}

Tiếp theo, bạn tạo một phương thức sử dụng nó khi Command được lựa chọn:

public String sendScore(String user, int score) {
String result = “No Answer”;
// server to send data
String url = “http://www.sergioestevao.com/midpAdventures/post.php”;
// prepare http request
String urlTotal = url + “?user=” + user + “&score=” + score;
byte[] data = null;
try {
data = network.httpConnection(urlTotal, null);
} catch (IOException e) {
result = “Communication Problems”;
e.printStackTrace();
} catch (SecurityException s) {
// user denied access to communication
result = “You need to allow communications in order to send the highscore to server.”;
s.printStackTrace();
}
// check data return.
if (data != null) {
result = new String(data);
}
return result;
}

Và gọi nó khi Command được sử dụng:

if (cmd == comInviteSend) {
        result = sendScore(scores[0].name, scores[0].value);
        display(new Alert("Result", result, null, AlertType.INFO));
   }

Nếu bạn sử dụng đoạn code này, bạn sẽ nhận được một cảnh báo:

Warning: To avoid potential deadlock, operations that may block, such as networking, should be performed in a different thread than the commandAction() handler.

Hãy nhớ rằng bạn đang thực thi đoạn mã này như là một câu trả lời cho một sự kiện mà người sử dụng đang được thực thi trên thread UI. Nếu đoạn mã này block hoặc mất một thời gian dài để thực hiện, ứng dụng sẽ gặp khó khăn.

Để tránh vấn đề này, bạn nên sử dụng một thread để làm các công việc liên quan đến network. Hãy bắt đầu khai báo một thread và các biến trạng thái để điều khiển các hoạt động của thread.

private static final int ACTION_SEND_HIGHSCORE = 0;
public Thread workThread;
public boolean active = false;

Bất cứ lúc nào có một sự kiện làm tốn nhiều thời gian(như việc kết nối mạng có thể thành công hay thất bại, điều này phụ thuộc các yêu tố bên ngoài như đường truyền mạng…), thì bạn nên sử dụng một thread chuyên biệt để kích hoạt các hoạt động này.

public void doAction(int action) {
// stores action to do
this.action = action;
// check if thread is already created
if (workThread == null) {
workThread = new Thread(this);
workThread.start();
active = true;
}
// wakes up thread to work
synchronized (this) {
notify();
}
}

Thread này sẽ thực thi phương thức run():

public void run() {
while (active) {
// check what action to do
switch (action) {
case (ACTION_SEND_HIGHSCORE):
// send the first score to the server
result = sendScore(scores[0].name, scores[0].value);
commandAction(cmdReceiveHighScore, highScoreForm);
break;
}
// waits for action to do.
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Để start thread này, cần khai báo một giao diện Runnable trong MIDlet:

public class MyMidlet extends MIDlet implements CommandListener, Runnable {

Bây giờ, bạn có thể gọi phương thức doAction() khi người dùng chọn command ‘Send high score’. Lưu ý rằng, phương thức run() gọi một command cmdReceiveHighScore. Điều này được sử dụng để hiển thị các kết quả của các giao tiếp với người dùng.

 if (cmd == cmdSendHighScore) {
        doAction(ACTION_SEND_HIGHSCORE);
      }
      if (cmd == cmdReceiveHighScore) {
        display(new Alert("Result", result, null, AlertType.INFO));
      }

SMS

Một trường hợp sử dụng cho giao tiếp trong Java ME là để mời bạn bè tham gia chơi game. Bạn cũng có thể có một cuộc cạnh tranh trong bảng điểm số cao.

Thứ nhất, cài đặt phương thức gửi tin nhắn SMS trong lớp network.

public boolean sendSms(String number, String message){
boolean result = true;
try {
//sets address to send message
String addr = “sms://”+number;
// opens connection
MessageConnection conn = (MessageConnection) Connector.open(addr);
// prepares text message
TextMessage msg =
(TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE);
//set text
msg.setPayloadText(message);
// send message
conn.send(msg);
} catch (Exception e) {
result = false;
}
return result;
}

Từ đoạn code ví dụ trên, bạn chỉ cần sử dụng phương thức open() của lớp Connector với định dạng URL là “sms://number”. Sau đó tạo một TextMessage và gửi nó thông qua Connection. Kích thước của message được giới hạn trong 160 ký tự.

Tiếp theo, bạn tạo một form để nhận tên và số điện thoại từ người dùng như sau:

public Displayable initInviteForm() {
if (inviteForm == null) {
inviteForm = new Form(“Invite”);
inviteForm.setCommandListener(this);
inviteForm.addCommand(initBackCommand());

inviteName = new TextField(“Name:”, “”, 20, TextField.ANY);
inviteNumber = new TextField(“Number:”, “”, 20, TextField.PHONENUMBER);
inviteForm.append(inviteName);
inviteForm.append(inviteNumber);
comInviteSend = new Command(“Invite”, Command.ITEM, 1);
inviteForm.addCommand(comInviteSend);
}

return inviteForm;
}

Tiếp theo, trong phương thức commandAction(), bạn cài đặt code để hiển thị form và gửi message:

public void commandAction(Command cmd, Displayable display) {
   [...]
   else if (display == inviteForm) {
      if (cmd == comBack) {
        display(initMainForm());
      }
      if (cmd == comInviteSend) {
        doAction(ACTION_SEND_INVITE);
      }
    }
  [...]
}

Cuối cùng, trong phương thức run(), bạn gọi phương thức sendSms() để gửi message:

public void run() {
[…]
case (ACTION_SEND_INVITE):
// invite another player to play
String inviteMessage = ” invites you to play Transnoid!”;
network.sendSms(inviteNumber.getString(), inviteName.getString()
+ inviteMessage);
break;
}
[..]
}

Download code ở đây

Chúc các bạn thành công.

Các bài liên quan:
Phát triển game đơn giãn trên Mobile(P1)
Phát triển game đơn giãn trên Mobile(P2)
Phát triển game đơn giãn trên Mobile(P3)
Phát triển game đơn giãn trên Mobile(P4)
Phát triển game đơn giãn trên Mobile(P5)
Phát triển game đơn giãn trên Mobile(P6)