Thứ Bảy, 2 tháng 2, 2013

Bài 1 - Tạo MIDlet



Để phục vụ cho công tác nghiên cứu Java, Web development & design, Rails, PHP… và chia sẻ những kết quả đạt được, huetoday sẽ lập blog để đăng tải các nội dung lên tại địa chỉ http://huetoday.wordpress.com. Rất mong được sự hưởng ứng, cộng tác của tất cả các bạn.
Để mở đầu, huetoday sẽ giới thiệu một loạt bài nói về J2ME lập trình trên các thiết bị di động với ngôn ngữ Java. Vì các tài liệu hiện có được chia sẻ trên mạng rất ít, rất ngắn và không chi tiết. Tuy nhiên các bài viết của tớ dịch và sưu tầm sẽ không quá đi sâu. Một loạt bài bao gồm nhiều Series, mỗi series có nhiều bài, các bạn nhớ chú ý theo dõi.
Nguồn tài liệu được dịch trực tiếp trên java.net, các trang mạng, một số ebook khác.
Java 2 Micro Edition (J2ME) kết hợp một máy ảo JVM và một tập các hàm API của Java phục vụ cho việc phát triển ứng dụng trên các thiết bị di động. Bài viết này là mở đầu trong chuỗi các hướng dẫn về J2ME. Sau đây, tôi sẽ cung cấp một hướng dẫn từng bước khi tạo một ứng dụng J2ME, còn được biết đến như là MIDlet bằng một ví dụ đơn giản. Ví dụ này sẽ cho biết cách thử nghiệm và triển khai các MIDlet sao cho tốt. Cuối cùng, tôi sẽ nói về chu kỳ sống của một MIDlet.
1.Giới thiệu J2ME
Như đã nói ở trên, Java 2 Micro Edition (J2ME) kết hợp một máy ảo JVM và một tập các hàm API của Java phục vụ cho việc phát triển ứng dụng trên các thiết bị di động. Với bạn, một người lập trình, có phải cài đặt máy ảo JVM và tập các API này lên các thiết bị đó hay không? Xin thưa, nếu bạn là một người lập trình, bạn chỉ cần phát triển các ứng dụng vào những thiết bị này và cài đặt chúng mà thôi. Còn JVM (và các API kèm theo) đã được các nhà sản xuất thiết bị đó cài đặt và đóng gói lại trên các thiết bị trước đó rồi. Sướng quá phải không?
J2ME có thể được chia thành 3 phần, như hình dưới đây: một configuration, một profile, và các gói tùy chọn. Mộtconfiguration chứa JVM (không phải là JVM truyền thống trên Java desktop, mà được chế riêng cho các thiết bị di động) và một số thư viện lớp; một profile dựa vào trên những thư viện lớp cơ sở này bằng cách cung cấp một tập các API hữu ích; và các gói tùy chọn, cũng là một tập các API mà bạn có thể hoặc không thể sử dụng khi tạo ứng dụng. Configuration và profile được cung cấp bởi các nhà sản xuất và được nhúng trên các thiết bị.
Profile và configuration phổ biến nhất là MIDP (Mobile Information Device Profile) và CLDC (Connected Limited Device Configuration). CLDC dành cho các thiết bị có cấu hình hạn chế; chẳng hạn, thiết bị đó chỉ có 128 – 512Kb bộ nhớ cho các ứng dụng Java. Do đó, JVM mà nó cung cấp là rất hạn chế và chỉ hỗ trợ một số ít các lớp Java truyền thống. Ngoài ra còn có một configuration giống với CLDC là CDC dành cho các thiết bị có bộ nhớ ít nhất 2MB và hỗ trợ JVM nhiều hơn.
Ở đây ta chỉ tập trung vào MIDP và CLDC.
2.Cài đặt J2ME Development Kit
- Đầu tiên trên máy tính của bạn phải có JDK bản 1.4.2 hoặc mới hơn.
- Tải và cài đặt J2ME Wireless Toolkit 2.2 (download), bộ toolkit này cung cấp môi trường phát triển với MIDP 2.0 và CLDC 1.1. Cài nó vào một thư mục trên máy tính ví dụ C:\WTK22.
3.Quá trình tạo ra MIDlet không dùng toolkit
Có 7 bước trong quá trình tạo một MIDlet, bao gồm: thiết kế, viết mã, biên dịch, tiền kiểm tra, đóng gói, thử nghiệm, và triển khai.
Để bạn hiểu được quá trình này, hãy cùng xem xét một ví dụ đơn giản: huetoday và các bạn sẽ cùng tạo một MIDlet, nó sẽ in ra ngày tháng và thời gian hiện tại theo các bước trên.
Bước 1: Thiết kế
MIDlet có khác với các ứng dụng khác mà bạn đã từng tạo, đơn giản bởi vì MIDlet chạy trên một môi trường rất khác biệt. Có một vài vấn đề ở đây, nhưng không phải là những vấn đề hiện hữu nhất (chẳng hạn, tương tác của MIDlet với người dùng của bạn), nhưng một số vấn đề khác lại ảnh hưởng đến khả năng sử dụng.
Trong ví dụ của chúng ta, Date-Time MIDlet không cần giao diện người dùng. Nó chỉ cần hiển thị ngày tháng và thời gian khi người dùng thực thi một MIDlet. Với các thiết kế phức tạp hơn có nhiều màn hình, tốt nhất là thiết kế màn hình một cách chuyên nghiệp trước khi bắt đầu vào quá trình viết mã.
Bước 2: Viết mã
Mỗi MIDlet phải kế thừa từ lớp trừu tượng MIDlet trong gói javax.microedition.midlet, cũng như việc tạo Applet bằng cách kế thừa từ lớp java.applet.Applet. Tối thiểu MIDlet của bạn phải ghi đè 3 phương thức của lớp trừu tượng này, startApp(), pauseApp(), và destroyApp(boolean unconditional). Dưới đây là lớpDateTimeApp:
package com.j2me.part1;
import java.util.Date;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class DateTimeApp extends MIDlet {
   Alert timeAlert;
   public DateTimeApp() {
      timeAlert = new Alert("Alert!");
      timeAlert.setString(new Date().toString());
   }

   public void startApp() {
      Display.getDisplay(this).setCurrent(timeAlert);
   }

   public void pauseApp() {
   }

   public void destroyApp(boolean unconditional) {
   }
}
Trong ví dụ này, phương thức khởi tạo của DateTimeApp tạo đối tượng cần thiết cho hiển thị thời gian lên màn hình thiết bị và phương thức startApp() thực sự làm nhiệm vụ hiển thị đối tượng này. Bạn chưa cần phải hiểu đối tượng Alert ngay lúc này.
Copy đoạn mã này vào một file và đặt tên là DateTimeApp.java và lưu nó trong một thư mục (như com/j2me/part1) ở bất kỳ đâu trong máy của bạn. Ở đây tôi lưu tại C:\WTK22\article\com\j2me\part1.
Bước 3: Biên dịch
Biên dịch một MIDlet thì không khác gì so với biên dịch một ứng dụng Java thông thường. Bạn vẫn sử dụng trình biên dịch javac, trừ phi bạn cần thay đổi CLASSPATH khi biên dịch MIDlet. Việc thay đổi CLASSPATH sẽ tác động các lớp Java cơ sở mà trình biên dịch Java dùng để biên dịch MIDlet ngược lại, do đó đảm bảo việc biên dịch đi theo các hàm API cần thiết của J2ME. Vì vậy thay vì biên dịch ngược lại java.lang.Date trong Java “thông thường”, thực sự bạn muốn biên dịch với java.lang.Date của J2ME. Điều này được thực hiện được bằng cách trỏ đến các lớp CLDC và MIDP với tùy chọn -bootclasspath trong khi biên dịch. Lúc này ta có:
C:\WTK22\article>javac -bootclasspath ..\lib\cldcapi11.jar; ..\lib\midp20.jar; com\j2me\part1\DateTimeApp.java (enter)
Trong lệnh này, tôi biên dịch với 2 phiên bản CLDC API 1.1 và MIDP API 2.0, các bạn cần thay đổi tham số nếu có các phiên bản khác để trình biên dịch hiểu.
Bước 4: Tiền kiểm tra
Trước khi triển khai lớp MIDlet của bạn, MIDlet cần được kiểm tra lần đầu tiên. Việc xác minh byte code là một bước được thực hiện bởi JVM trước khi chạy bất kỳ file class nào để đảm bảo file class có cấu trúc và đúng khái niệm đối với đặc tả JVM. Nếu file class thất bại trong kiểm tra này, file class bị đẩy ra và thoát khỏi JVM, sau đó thông báo bằng thông điệp vi phạm an ninh hay vi phạm tính toàn vẹn. Việc xác minh này được thực hiện bởi tất cả JVM, kể cả JVM chứa trong CLDC configuration.
Tiền kiểm tra là một phần trong quá trình xác thực 2-bước, được thiết kế đặc biệt cho các thiết bị được ràng buộc, như là một thiết bị chạy với CLDC-cơ sở JVM. Ý tưởng là cho phép một nhà phát triển xác thực trước các lớp của anh ta, có vượt qua giới hạn nào không khi các lớp được xác thực trên thiết bị. Quá trình tiền kiểm tra này thêm các thông tin đặc biệt vào các lớp để nhận dạng chúng đã được xác thực và làm cho xử lý trên thiết bị hiệu quả hơn.
Quá trình này được thực hiện bằng lệnh sau:
C:\WTK22\article>..\bin\preverify.exe -classpath ..\lib\cldcapi11.jar;..\lib\midpapi20.jar com.j2me.part1.DateTimeApp
Theo mặc định, trình xác thực sẽ tạo phiên bản đã được xác thực của file DateTimeApp.class trong thư mục output tại thư mục hiện hành. Nó sẽ giữ gìn cấu trúc gói, vì thế lớp được xác minh sẽ ở trong thư mục C:\WTK22\article\output\com\j2me\part1\. Dĩ nhiên bạn có thể trỏ output đến thư mục khác bằng cách sử dụng tùy chọn -d.
Bước 5: Đóng gói
Bước đầu tiên là tạo một file Manifest. File Manifest này mô tả nội dung của file Java Archive (JAR) mà chúng ta sẽ tạo trong bước tiếp theo. Nội dung của file này như sau:
MIDlet-Name: DateTimeApp
MIDlet-Version: 1.0.0
MIDlet-Vendor: Vikram Goyal
Lưu file này lại thành Manifest.mf trong thư mục C:\WTK22\article\output\. (Chú ý là dòng cuối cùng bắt buộc phải có: MIDlet-Vendor: Vikram Goyal)
Tiếp theo, tạo file JAR để đóng gói lại file DateTimeApp.class và file Manifest. Để tạo file JAR, đi đến thư mục C:\WTK22\article\output\ và sử dụng lệnh sau:
C:\WTK22\article\output>jar cvfm DateTimeApp.jar Manifest.mf .\com
Lệnh này sẽ tạo file DateTimeApp.jar trong thư mục hiện hành.
Bước cuối cùng là tạo một file có phần mở rộng là *.jad. Một file Java Application Descriptor (JAD) trỏ đến vị trí của MIDlet để sau này một thiết bị J2ME có thể cài đặt nó. File này chứa một số nội dung:
MIDlet-1: DateTimeApp, , com.j2me.part1.DateTimeApp
MIDlet-Name: DateTimeApp
MIDlet-Version: 1.0.0
MIDlet-Vendor: Vikram Goyal
MIDlet-Jar-URL: DateTimeApp.jar
MIDlet-Jar-Size:
MicroEdition-Profile: MIDP-2.0
MicroEdition-Configuration: CLDC-1.1
Lưu file này lại thành DateTimeApp.jad và lưu cùng thư mục với file JAR. Chú ý là giá trị thuộc tính MIDlet-Jar-Size: bị bỏ trống. Giá trị này đưa đến cho bạn vào bước cuối cùng của đóng gói, khi mà bạn định rõ kích thước của file DateTimeApp.jar, và đặt giá trị này vào bên trong file JAD, tính theo đơn vị byte. Nó rất quan trọng, nếu thông tin sai thì việc cài đặt sẽ thất bại. Trên máy của tôi, giá trị này là 1469 byte, và do đó, thông tin tôi bổ sung vào file JAD là:
MIDlet-Jar-Size: 1469
Bước 6: Thử nghiệm
Trước khi triển khai MIDlet của bạn, chúng phải được thử nghiệm bằng một công cụ mô phỏng có chức năng tương đương một thiết bị trên thực tế. Từ thư mục output, chạy lệnh sau:
C:\WTK22\article\output>..\..\bin\emulator.exe -Xdescriptor DateTimeApp.jad
Bạn sẽ thấy trình mô phỏng bật ra, với MIDlet DateTimeApp được chọn sẵn. Ngược lại, có thể là có lỗi thông tin kích thước trong file JAR.
Ấn nút Launch ở góc dưới bên phải màn hình mô phỏng. Trình mô phỏng cài đặt MIDlet và sẵn sàng để chạy. Bạn ấn OK trên bàn phím ảo và MIDlet sẽ hiển thị thông tin ngày tháng và thời gian hiện tại trong một khoảng thời gian ngắn rồi biến mất. Chú ý là MIDlet vẫn còn đang chạy dù thông tin hiển thị bị biến mất, đó là do mã lập trình của bạn (đối tượng Alert), bạn chưa thực sự hủy MIDlet.
Bước 7: Triển khai
Có nhiều cách để triển khai. Đầu tiên là qua một kết nối mạng giữa máy tính và thiết bị cầm tay của bạn. Có thể là qua cáp USB hay kết nối Bluetooth.
Cách thứ hai là thông qua Internet. Lúc đó, bạn cần phải truy cập vào một web server với một tên miền hoặc một địa chỉ IP thực. Bạn cũng cần có đặc quyền admin để thay đổi các file cấu hình trên webserver và thêm 2 kiểu MIME là JAD và JAR. Nếu bạn sử dụng Tomcat, bạn không cần làm điều này. Còn với các server Apache, bạn cần thay đổi file mime.types và thêm 2 kiểu mở rộng sau:
text/vnd.sun.j2me.app-descriptor jad
application/java-archive jar
Bằng cách thêm những MIME này, bạn thông báo cho trình duyệt, hay bất cứ client nào khác đang truy cập những file này, cách xử lý chúng khi được tải về thiết bị.
Tiếp theo, tạo một file HTML chứa liên kết đến các file đó. Cũng hoàn toàn không cần phải tạo file HTML, bởi vì một thiết bị có thể truy cập một file HTML thì cũng có thể truy cập một file JAD. File HTML cũng không cần phải đẹp. Đừng quên là người dùng của bạn truy cập trang này qua một thiết bị di động, vậy hãy thiết kế một cách đơn giản. File này có thể là như sau:
Click here to download DateTimeApp MIDlet!
Trang này cung cấp một liên kết đến file JAD, và một file JAD lại cung cấp một liên kết đến file JAR thông qua thuộc tính MIDlet-Jar-URL: DateTimeApp.jar. Lời khuyên thêm ở đây là tạo liên kết tuyệt đối đến file JAD. Nếu tôi cài đặt MIDlet qua một site, tôi thay đổi liên kết thành URL tuyệt đối:
MIDlet-Jar-URL: http://www.craftbits.com/j2me/DateTimeApp.jar
Dĩ nhiên, bạn sẽ thay đổi thông tin này theo thực tế riêng của bạn.
Cuối cùng tải file JAD đã được thay đổi, file HTML, và file JAR lên webserver. Bây giờ, bất kỳ ai với một chiếc điện thoại đều có thể duyệt trang của bạn, tải và cài đặt MIDlet.
4.Quá trình tạo MIDlet dùng Toolkit
Nếu bạn đã cài đặt J2ME Development Toolkit vào thư mục C:\WTK22. Hãy khám phá nội dung của thư mục này:
Các thư mục quan trọng nhất là apps và bin. Thông tin về các thư mục như sau:
appdb: chứa mô phỏng thiết bị di động
apps: các MIDlet được tạo bởi Toolkit.
bin: chứa các file thực thi của J2ME, bao gồm các công cụ như trình xác thực, trình mô phỏng
docs: tài liệu và API của J2ME, cụ thể là cho MIDP 2.0 và MIDP 1.1
lib: chứa các file JAR cho MIDP (cả 2.0 và 1.1), CLDC (1.1 và 1.0), và vài thư viện khác
sessions: phiên mạng và profile
wtklib: thư viện của chính Toolkit
Ta tập trung vào việc sử dụng Toolkit, chạy ktoolbar.exe trong thư mục bin. Giao diện chính của Toolkit sẽ xuất hiện:
Chọn New Project… và nhập thông tin như hình dưới đây:
Cửa sổ tiếp theo cho phép bạn thay đổi các thiết lập tương ứng với nền tảng cuối của MIDlet. Trong trường hợp này, bạn muốn chọn cho MIDlet các thông tin như MIDP 2.0 và CLDC 1.1. Chọn Target Platform là JTWI. Bỏ tùy chọn Optional Mobile Media API, như hình sau:
Click OK để bắt đầu tạo dự án mới. Các thông tin về dự án mới sẽ được hiển thị ra màn hình như hình sau:
Sao chép file DateTimeApp.java vào thư mục C:\WTK22\apps\DateTimeApp\src\com\j2me\part1. Trở lại cửa sổ chính, bấm nút Run. Toolkit sẽ biên dịch, tiền kiểm tra, và đóng gói, và chạy DateTimeApp trong trình mô phỏng.
Bên cạnh đó, để tạo file JAR, bạn chọn menu Project -> Package -> Create Package.
5.Vòng đời của MIDlet
Các thiết bị di động, dù là thực tế hay mô phỏng, đều tương tác với MIDlet bằng phần mềm của riêng chúng gọi làApplication Management Software (AMS). AMS này chịu trách nhiệm khởi tạo, bắt đầu, dừng, chạy lại, và hủy một MIDlet. Để thuận tiện cho quản lý, một MIDlet có thể là ở một trong 3 trạng thái khi được điều khiển qua các phương thức MIDlet, mà mỗi MIDlet phải kế thừa và ghi đè lại. Ba trạng thái này là active – hoạt động, paused – dừng lại, và destroyed – hủy.
Như bạn thấy ở hình trên, một MIDlet được đặt ở trạng thái paused khi AMS tạo một thể hiện MIDlet mới, bằng cách gọi bộ khởi tạo no-args của nó. MIDlet cũng có thể đi vào trạng thái paused khi AMS gọi phương thứcpauseApp() trên một MIDlet đang hoạt động. MIDlet cũng có thể đi vào trạng thái paused khi tự nó gọi phương thức notifyPaused(). Chính xác thì đã có gì xảy ra với MIDlet khi nó ở trong trạng thái paused?
Khi ở trong trạng thái paused, MIDlet chờ cơ hội nhảy vào trạng thái active. Về mặt lý thuyết, ở trạng tháipaused, MIDlet sẽ không giữ hay sử dụng bất kỳ tài nguyên thiết bị nào và sẽ ở trạng thái bị động. Một khi MIDlet được tạo ra, paused là trạng thái mặc nhiên trước khi chuyển sang trạng thái active. Còn nữa, việc nhảy vào trạng thái paused là cần thiết khi thiết bị yêu cầu MIDlet dùng bớt ít tài nguyên hơn, bởi vì những tài nguyên này có thể được yêu cầu cho việc xử lý các chức năng thiết bị khác, như là xử lý một cuộc gọi đến. Đây là lúc thiết bị gọi phương thức pauseApp() thông qua AMS. Nếu MIDlet thông báo cho AMS rằng nó muốn dừng thì MIDlet sẽ gọi phương thức notifyPaused().
Một cách cuối cùng để một MIDlet vào trạng thái paused là khi phương thức startApp() của MIDlet được gọi khi AMS gọi nó để bắt đầu MIDlet (hoặc là lần đầu tiên hoặc là từ một trạng thái paused), thì ném ra ngoại lệMIDletStateChangeException. Về bản chất, trong trường hợp có một lỗi, MIDlet chọn con đường an toàn là đi vào trạng thái paused.
Ở trạng thái active, MIDlet có thể thực hiện các phương thức của nó, chiếm giữ tài nguyên thiết bị và những gì nó nghĩ là phải làm. Một MIDlet ở trong trạng thái active khi AMS gọi phương thức startApp() từ một trạng tháipaused. Một MIDlet ở trạng thái paused có thể yêu cầu đi vào trạng thái active bằng cách gọi phương thứcresumeRequest() thông qua AMS. AMS có thể lờ đi yêu cầu này hoặc đưa nó vào hàng đợi nếu có MIDlet khác đang yêu cầu điều tương tự.
MIDlet ở trạng thái destroyed khi phương thức destroyApp(boolean unconditional) được gọi và trả về thành công, hoặc là từ một trạng thái active hay một trạng thái paused. Phương thức này được gọi bởi AMS khi nó nhận thấy không cần để MIDlet tiếp tục chạy và là chỗ MIDlet có thể muốn thực hiện dọn dẹp hay một số hành động cuối cùng. MIDlet có thể tự vào trạng thái này bằng cách gọi phương thức notifyDestroyed() thông qua AMS và báo cho AMS biết nó đã dọn dẹp tài nguyên của nó và là thích hợp cho việc hủy MIDlet. Dĩ nhiên, khi ở trong trường hợp này, phương thức destroyApp(boolean unconditional) không được gọi bởi AMS, mọi hành động cuối cùng phải được thực hiện trước khi phương thức này được gọi.
Chuyện gì xẩy ra nếu AMS gọi phương thức destroyApp(boolean unconditional) ở giữa chừng một bước quan trọng trong khi có thể MIDlet đang làm gì đó, và có thể miễn cưỡng bị hủy? Đây là nơi cờ Boolean unconditional tham gia giải quyết vấn đề này. Nếu cờ này đặt là true, MIDlet sẽ bị hủy, bất chấp MIDlet đang làm gì đi nữa. Tuy nhiên, nếu cờ này đặt là false, AMS sẽ nói cho MIDlet rằng nó muốn MIDlet bị hủy, nhưng nếu MIDlet đang làm cái gì đó quan trọng, nó có thể phát sinh ngoại lệ MIDletStateChangeException, và AMS sẽ không hủy được nó. Dẫu vậy, không có bảo đảm nào là MIDlet sẽ không bị hủy, và nó vẫn cho mỗi thiết bị tự quyết định cách chúng xử lý yêu cầu. Nếu thiết bị không tôn trọng yêu cầu của MIDlet, thiết bị có thể thử và gọi destroyApp(boolean unconditional) ở một giai đoạn khác.
Chú ý rằng khi một MIDlet ở trạng thái destroyed có nghĩa là thể hiện MIDlet đó đã bị hủy đi, nhưng không được gỡ bỏ đi từ thiết bị. MIDlet vẫn được cài đặt trên thiết bị, và một thể hiện mới của nó có thể được tạo ra sau này.
Để kết thúc bài 1, series 1, tôi sẽ cho bạn thấy một biểu đồ luồng hoạt động của một tuần tự các sự kiện với MIDlet DateTimeApp, các hành động AMS, các trạng thái MIDlet:
Ở phần tiếp theo của J2ME – Series 1, bạn sẽ bắt đầu tạo các MIDlet bằng cách dùng các UI API của MIDP 2.0. Thư viện này cho phép bạn tạo ra giao diện người dùng mạnh mẽ, một yêu cầu chính cho bất kỳ MIDlet nào.

2 nhận xét: