Ở 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)
24 responses to “Phát triển game đơn giãn trên Mobile(P7 – Phần cuối)”
Hung
February 10th, 2010 at 14:10
Anh a.
Cam on anh!may bai anh post len rat hay. nhung em chay van chua duoc
Cho em xin code cua may phan sau nhe’.
Đại
February 23rd, 2010 at 12:05
em mới tìm hiểu về j2me , đọc bài viết của anh thấy rất cơ bản , dễ hiểu , anh gửi cho em code hoàn chỉnh của game đc ko anh ? 🙂
Phong
March 1st, 2010 at 22:16
Anh ơi cho em hỏi làm thế nào để tạo ra một TextField trong lớp Canvas, các kí tự chỉ viết trên 1 dòng, khi hết dòng dãy kí tự sẽ tự dịch chuyển sang trái. Em viết được phần nhập dữ liệu nhưng không biết làm thế nào để kiểm tra xem kí tự đã hết dòng chưa. Có cách nào để lấy độ rộng của một kí tự không ạ (trả về số pixel)?
mobilesprogramming
March 1st, 2010 at 22:24
Bạn thử sử dụng 1 trong 2 phương thức này xem:
Font.getDefaultFont().stringWidth(String)
hoặc
Font.getDefaultFont().substringWidth(String str, int offset, int len);
Phong
March 2nd, 2010 at 09:49
Cảm ơn anh nhiều. Em làm được rồi
KID
March 13th, 2010 at 00:29
cậu có thể gửi cho mình code ví dụ được không, mình cũng đang làm bài tập về cái này, thanks nhiều nhé
bemap
March 13th, 2010 at 23:25
anh gửi cho em code với từ P4 trở đi ấy anh. Em đang thực tập cơ sở, nên cần lắm anh ạ. EM cảm ơn ^^
chuotfx
March 14th, 2010 at 09:20
bạn có thể send cho mình code đc kô? thật sự là hàm thích j2me rồi
quannhox
March 19th, 2010 at 13:55
thanks anh..cho em xin cai code nhe
Tan_Phong_kiem
March 21st, 2010 at 15:40
Bạn ơi, cho mình hỏi.
Phương thức pauseApp() được gọi trong trường hợp nào.
Mình không biết nên làm thế nào để cho tạm dừng 1 game.
Bạn giúp mình với.
Cảm ơn nhiều.
Võ Quang Hòa
March 21st, 2010 at 20:33
Anh cho em code nhé, và cho em hỏi cái này chút. Em không có điện thoại hỗ trợ Java, em học lập trình J2ME trên IDE Eclipse với bộ Sun Java (TM) Wireless Toolkit thì làm thế nào để thử những bài này ạ. Cần phải tạo file, thư mục ở những đâu.
mobilesprogramming
March 22nd, 2010 at 08:46
Khi bạn dùng IDE Eclipse để chạy J2ME, bạn copy các file ví dụ bỏ vào project, nếu là các file .java thì bạn bỏ vào thư mục src, còn nếu là các image thì bạn bỏ vào thư mục res ấy
mobilesprogramming
March 22nd, 2010 at 08:51
Mình xin trả lời câu hỏi của bạn Tan_Phong_kiem như sau: Phương thức pauseApp() được gọi khi bạn đang sử dụng ứng dụng của mình mà có 1 cuộc gọi đến hay một tin nhắn đến, lúc này nếu ứng dụng của bạn không xử lý lưu lại đối tượng Display thì ứng dụng của bạn sẽ bị exit, ngược lại nếu bạn có lưu lại đối tượng Display thì sau khi bạn nghe gọi hay đọc tin nhắn xong thì nó sẽ quay trở lại ứng dụng của bạn. Còn bạn muốn tạm dừng cho một game thì ý này mình chưa hiểu lắm, bạn có thể giải thích rõ hơn được không?
PhamNgocVi
March 26th, 2010 at 18:34
gửi cho mình code của game này với :D. hay quá!!!
hoangnh
March 27th, 2010 at 08:16
Cau cho minh xin code nhe
Minh dang lam game tren j2me
doc bai cua ban, minh thay rat hay va bo ich
Tien
March 27th, 2010 at 16:05
Những bài viết của anh rất hay và rất có ích đối với những người như em.Cảm ơn anh nhiều,anh cho em xin source của mấy bài anh viết nhé.Cảm ơn anh rất nhiều.
nguyễn thành luân
April 1st, 2010 at 17:04
mình nghĩ là bạn nên gửi file lên cho mọi người down về đi,bạn gửi tới mail thì mất công lắm,bài viết của bạn rất có ý nghĩa đấy,mình đọc mới thấy hiểu.Hồi trước xem mấy giáo trình tiếng anh chẳng hiểu gì mấy.thanks a lot!!
anhhungdie
April 2nd, 2010 at 13:04
gui code cho minh voi
hung
April 15th, 2010 at 22:43
bạn ơi cho mình hỏi muốn làm cho sprite tự chuyển động qua lại 1 vị trí thì làm như nào
Hao
May 14th, 2010 at 14:34
Thanks bạn! Bài của bạn viết rất hay.
likefogs
December 28th, 2010 at 21:35
Chào Anh!
Em có code của bài viết của anh về biên dịch ra băng netbean 6.8 nhưng báo lỗi ,mong anh giup cho.
javacall_file_open:………………….._delete_notify.dat
mobilesprogramming
December 29th, 2010 at 14:18
Chào bạn, bạn có thể nêu rõ lỗi ko?
likefogs
December 29th, 2010 at 21:22
vâng! em down code về rồi viết lại vào netbean 6.8 Code thì không bị lỗi, nhưng khi chạy vào giả lập điện thoại của netbean thì chỉ xuất hiện màn hình với 2 dòng
1. install application
2. manage certificat authorities
không chạy vào màn hình game.
mong anh giúp cho!
MHN Dao
March 21st, 2011 at 23:50
E chao a! bai vi du cua a rat hay. a co the cho e xin code dc ko? cam on a!