P. 1
Mobile Programming

Mobile Programming

|Views: 65|Likes:
Published by Dinh Chung

More info:

Published by: Dinh Chung on May 01, 2012
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

08/08/2013

pdf

text

original

Sections

  • I.1. Lịch sử của điện thoại di động
  • I.2. Các chuẩn công nghệ
  • II.1. Lịch sử
  • II.2. Lý do chọn J2ME
  • II.3. Kiến trúc của J2ME
  • II.4. Giới thiệu MIDP
  • II.5.Môi trường phát triển J2ME
  • III.1.Đốitượng Display, Displayable và Screens
  • III.2. Thành phần Form và Items
  • III.3. Thành phần List, Textbox, Alert, và Ticker
  • IV. Các thành phần giao diệnởmức thấp củaứng dụng MIDP
  • IV.1. Các hàm APIởmức thấp
  • IV.2. Lớp Canvas
  • IV.3. Lớp Graphics
  • IV.4. Các hàm API dùngđểlập trình Game
  • V.1.Đốitượng Command
  • V.2.Đốitượng Item
  • V.3. Ví dụ
  • VI.1. Persistent Storage Through the Record Store
  • VI.2. Các vấnđềliên quan đến RMS
  • VI.3. Các hàm API trong RMS
  • VI.4. Duyệt Record với RecordEnumeration
  • VI.5. Sắp xếp các record với interface RecordComparator
  • VI.6. Searching with RecordFilter
  • VI.7. Notification of Changes with RecordListener
  • VI.8. Exception Handling
  • VII.1. Những protocolđược hỗtrợtrong GCF
  • VII.2. Hỗtrợgiao thức HTTP trong MIDP
  • VII.3. Accessing a Java servlet
  • VIII.1. Tổng số class và interface trong CLDC
  • Bảng 1: Tổng số class và interface trong CLDC
  • Bảng 3: Tổng số class và interface trong CDC
  • VIII.2. Thông tin của các dòngđiện thoại thông dụng
  • VIII.3. Tham khảo:

ĐẠI HỌC ĐÀ NẴNG TRƯỜNG ĐẠI HỌC BÁCH KHOA ĐÀ NẴNG KHOA CÔNG NGHỆ THÔNG TIN

TH.S TRỊNH CÔNG DUY
(TÀI LIỆU LƯU HÀNH NỘI BỘ)

Mobile Programming (Private Version)

Trịnh Công Duy

MỤC LỤC I. Tổng quan về công nghệ di động 2
I.1. I.2. Lịch sử của điện thoại di động............................................................................2 Các chuẩn công nghệ .........................................................................................11

II.

Giới thiệu về J2ME
II.1. II.2. II.3. II.4. II.5.

14

Lịch sử.................................................................................................................14 Lý do chọn J2ME...............................................................................................14 Kiến trúc của J2ME...........................................................................................14 Giới thiệu MIDP ................................................................................................17 Môi trường phát triển J2ME ............................................................................23

III.

Các thành phần giao diện ở mức cao của ứng dụng MIDP 30
III.1. Đối tượng Display, Displayable và Screens .....................................................30 III.2. Thành phần Form và Items ..............................................................................31 III.3. Thành phần List, Textbox, Alert, và Ticker ...................................................43

IV.

Các thành phần giao diện ở mức thấp của ứng dụng MIDP
IV.1. IV.2. IV.3. IV.4.

51

Các hàm API ở mức thấp..................................................................................51 Lớp Canvas.........................................................................................................51 Lớp Graphics .....................................................................................................61 Các hàm API dùng để lập trình Game ............................................................78

V.

Xử lý sự kiện

79

V.1. Đối tượng Command .........................................................................................79 V.2. Đối tượng Item ...................................................................................................80 V.3. Ví dụ....................................................................................................................80

VI.

Record Management System 82
VI.1. VI.2. VI.3. VI.4. VI.5. VI.6. VI.7. VI.8. Persistent Storage Through the Record Store ................................................82 Các vấn đề liên quan đến RMS ........................................................................84 Các hàm API trong RMS ..................................................................................84 Duyệt Record với RecordEnumeration ...........................................................96 Sắp xếp các record với interface RecordComparator....................................97 Searching with RecordFilter...........................................................................116 Notification of Changes with RecordListener...............................................130 Exception Handling .........................................................................................135

VII.

Kết nối mạng với Generic Connection Framework

135

VII.1.Những protocol được hỗ trợ trong GCF .......................................................136 VII.2.Hỗ trợ giao thức HTTP trong MIDP.............................................................142 VII.3.Accessing a Java servlet ..................................................................................152

VIII.

Phụ lục
VIII.1. VIII.2. VIII.3.

157
Tổng số class và interface trong CLDC .................................................157 Thông tin của các dòng điện thoại thông dụng.....................................158 Tham khảo: ..............................................................................................178 1

Mobile Programming (Private Version)

Trịnh Công Duy

I.
I.1.
I.1.1.

Tổng quan về công nghệ di động
Lịch sử của điện thoại di động
Phát minh ra điện thoại di động:

Tiến sĩ Martin Cooper, cựu tổng giám đốc đơn vị hệ thống của Motorola, được coi là “cha đẻ” của thiết bị liên lạc di động cầm tay và cũng là người đầu tiên thực hiện một cuộc gọi thông qua công cụ này. Ý tưởng liên lạc di động được bộ phận nghiên cứu AT&T thuộc Trung tâm Bell Labs (Mỹ) đưa ra năm 1947. Nhưng đến cuối những năm 60 và đầu thập kỷ 70 của thế kỷ trước, Motorola và Bell Labs mới thực sự trở thành 2 đối thủ lao vào cuộc đua trong việc tích hợp công nghệ này vào các thiệt bị cá nhân di động. Kỹ sư điện Cooper từng có 4 năm phục vụ trong hải quân trước khi chuyển về làm việc cho một công ty viễn thông nhỏ. Năm 1954, ông được Motorola tuyển dụng và tham gia phát triển các sản phẩm di động, đáng chú ý nhất là công cụ liên lạc radio di động đầu tiên dành cho cảnh sát Chicago năm 1967. Năm 1973, ông thiết lập một trạm thu phát tại New York đồng thời tung ra mẫu đầu tiên của cái gọi là điện thoại di động (cellphone): máy Motorola Dyna-Tac. Sau những cuộc thử nghiệm ban đầu tại Washington, Cooper và Motorola quyết định đưa công nghệ mới tới New York để quảng bá với công chúng. Motorola Dyna-Tac - chiếc điện thoại di động đầu tiên: Kích thước (cm): 22,86 x 12,7 x 4,44 Trọng lượng: 1,13kg Màn hình: không có Số bo mạch điện: 30 Thời lượng thoại: 35 phút Thời lượng pin: 10 tiếng Tính năng: Nói, nghe, quay số. Ngày 3/4/1973, đứng trên một phố gần khách sạn Manhattan Hilton, Cooper quyết định thử thực hiện một cuộc gọi riêng trước khi đi lên gác tham dự một cuộc họp báo giới thiệu thiết bị. Cú điện thoại ấy được ông gọi tới chính đối thủ cạnh tranh của mình: Joel Engel, Giám đốc Trung tâm thí nghiệm Bell Labs. Thật tuyệt vời, “hòn gạch” biết nói nặng hơn 1kg của Cooper đã hoạt động rất tốt, kết nối ông với trạm thu phát đặt trên nóc tòa tháp Burlington Consolidated (nay là tòa nhà Alliance Capital Building) ở New York, đồng thời liên lạc được với cả đường dây cố định. Những người qua đường tỏ ra rất ngạc nhiên khi thấy một người đàn ông bấm bấm một công cụ gì đó, áp sát vào tai và rồi say sưa nói chuyện. Nói về tương lai của ngành di động, tiến sĩ Cooper, năm nay đã ngoài 70 tuổi, cho rằng thế giới viễn thông thực ra vẫn còn rất non trẻ và mới đang bắt đầu bùng nổ. Động lực cho quá trình bùng nổ ấy trước hết là quan niệm kinh doanh mới, trong đó chú trọng vào người tiêu 2

Bên cạnh đó. Cooper nhận định. Motorola DynaTAC 8000X (1982) Năm 1973. nhưng trong 10 năm tới. Trông bề ngoài thiết bị này không đẹp mắt nhưng lại cho phép bạn vừa đi vừa nói chuyện ở bất kỳ đâu. Nhưng bạn có tin không. 3 . Motorola DynaTAC 8000X cho phép đàm thoại một giờ. thiết bị này mới bắt đầu xuất hiện trên thị trường năm. nhất là đối với các thị trường đang phát triển. Cooper cho rằng không nên vội cuốn theo sự cám dỗ của xu thế nhảy sang 3G. 30 số. bộ nhớ có thể lưu . Tuy nhiên. Và khi phân bổ.2. "cụ tổ" của nó nặng đến 10 đấy! 1. cho tới khi nào thấy rõ được những ích lợi cụ thể cho mỗi ngành kinh tế và mỗi quốc gia. cần có chính sách để ưu tiên cho những đối tượng biết khai thác tốt nhất giải tần sóng hiện có.1. cần thận trọng trong việc phân bổ giải tần đối với mỗi ứng dụng cụ thể.Mobile Programming (Private Version) Trịnh Công Duy dùng. đó sẽ là những chuyển biến có tính cơ bản”. Theo ông. nhà phát minh viễn thông cho rằng.995 USD. giá 3. Đối với các nước đang phát triển. cần tiến hành một cách hợp lý tùy thực tế của mỗi thị trường. ban đầu. Quan trọng hơn nữa là một nền hoạt động mở. phải đến năm 1983. trong đó có những công nghệ mới với khả năng tăng cường khai thác giải tần. “Lời khuyên của tôi về công nghệ liên lạc di động trong tương lai. là hãy từ từ. chậm nhưng hiệu quả”. nặng 1kg. Motorola giới thiệu dòng "alô" di động đầu tiên của thế giới dài 27 cm. dù là cho thế hệ 3G hay 4G.. “Những thay đổi sẽ không diễn ra quá nhanh. Lịch sử các thiết bị ĐTDĐ giờ đây đang hướng tới mục tiêu "siêu mỏng và siêu nhẹ". I.

Được giới thiệu năm 1982. hộp bom hơn điện thoại di động. Nhưng năm 1993 đây là một ý tưởng mới lạ.. PDA Simon Personal Communicator (1993) Điện thoại có tính năng PDA bây giờ không còn mới. 4 . sổ địa chỉ. Nokia Mobira Senator được thiết kế sử dụng trong xe ô tô. Nokia Mobira Senator (1982) Trịnh Công Duy Mới nhìn Nokia Mobira Senator giống. nhưng thiết bị hình hộp to đồ sộ này là điện thoại đầu tiên của Nokia.Mobile Programming (Private Version) 2. Simon Personal Communicator là điện thoại đầu tiên có tính năng của liên minh IBM và BellSouth. Nó nặng khoảng. Giá 900 USD. Nó là điện thoại kiêm máy tính. 10 kg.. 3.. máy fax và e-mail....

5. Nokia 8260 ra mắt năm 2000 ấn tượng hơn khi được tô điểm chiếc áo màu. Vào những năm 1990. Motorola StarTAC 3. thân 5. 5 . Nhưng Nokia 6160 và Nokia 8260 gây được sự chú ý của nhiều người.4 ounce. dài 4 inch. Nhưng thiết bị nhỏ bé có thể gập này đã cho thấy rằng phong cách là cực kỳ quan trọng. các tính năng điện thoại di động được chú trọng nhiều hơn thời trang. các điện thoại hình thỏi kẹo của Nokia xuất hiện hàng loạt.1 ounce là nhỏ nhất trong thời điểm đó và đến nay nó vẫn nhỏ và nhẹ hơn nhiều "dế" khác.2 inch. nặng 6 ounce. nặng chỉ 3. Nokia 6160 có màn hình đen trắng. Motorola StarTAC (1996) Trịnh Công Duy Trước khi Motorola Star TAC được giới thiệu năm 1996. Nokia 6160 (1998) và Nokia 8260 (2000) .Mobile Programming (Private Version) 4. ăng-ten ngoài.

Handspring đã khuấy động thị trường với sản phẩm Treo 180 gồm hai phiên bản: một với bàn pím QWERTY và một Treo 180g với thiết bị nhập liệu Graffiti. Với giá 400 USD và 500 USD. Như Kyocera QCP6035. Sản phẩm có 8 MB bộ nhớ. đây là điện thoại dùng phần mềm Palm đầu tiên được phân phối rộng rãi. nó có màn hình đen trắng nhưng có 16MB bộ nhớ.Mobile Programming (Private Version) 6. màn hình trắng đen. Dế thông minh QCP6035 của công ty này thống lĩnh thị trường bán lẻ năm 2001. Phần mềm Palm với Kyocera QCP6035 (2000) Trịnh Công Duy Nếu bạn là một trong những người hâm mộ điện thoại Treo sử dụng hệ điều hành Palm thì có thể cảm ơn Kyocera. 7. 6 . PDA đến điện thoại: Handspring Treo 180 (2001) Trở lại khi Palm và Handspring còn là đối thủ.

nhưng là một trong những thiết bị đầu tiên cung cấp tính năng duyệt web. Tiên phong về thiết kế xoay: Danger Hiptop (2002) Trịnh Công Duy T-Mobile Sidekick từng được mệnh danh là sản phẩm của năm 2003. nó là người tiên phong về thiết kế xoay tròn. Điện thoại cách tân: BlackBerry 5810 (2002) BlackBerry 5810 được biết đến nhưng một thiết bị điển hình về các khả năng xử lý dữ liệu. Đây là sản phẩm đầu tiên cung cấp các tính năng voice. 7 . 9. Mặc dù các khả năng đàm thoại của nó xoàng. GSM. truy cập email và nhắn tin.Mobile Programming (Private Version) 8. Hơn thế nữa.

nó gây phản cảm bởi thiết kế hình bán nguyệt kỳ quặc. Nhưng thay vào đó. nó không thể sanh với các điện thoại kiêm máy ảnh 5 megapixel. nhưng thật không may.Mobile Programming (Private Version) 10. 8 . đó lại là sản phẩm bất lợi. Sự kết hợp điện thoại di động/thiết bị chơi game được kỳ vọng sẽ quyến rũ được game thủ từ bỏ máy chơi game cầm tay của họ. Đột phá với tính năng chụp ảnh: Sanyo SCP-5300 (2002) Trịnh Công Duy Sanyo SCP-5300 PCS là "dế" đầu tiên có tính năng chụp ảnh được Sanyo và Sprint giới thiệu năm 2002. nhưng đến nay. Điện thoại kiêm máy chơi game Nokia N-Gage (2003) Dòng máy N-Gage tạo được nhiều dư luận khi xuất hiện trên thị trường năm 2003. tất nhiên. Ở độ phân giải cao nhất nó có thể chụp được các ảnh VGA (640 x 480). 11.

Với thiết bị này. Điện thoại nghe nhạc Motorola Rokr (2005) Motorola Rokr – điện thoại di động kiêm máy nghe nhạc đầu tiên sử dụng phần mềm nghe nhạc của Apple. người dùng có thể thưởng thức các bài hát.Mobile Programming (Private Version) 12. nhưng Motorola Razr v3 là sản phẩm đầu tiên gây ấn tượng về thiết kế thời thượng mỏng và nhẹ vào 2004. Siêu mỏng: Motorola Razr v3 (2004) Trịnh Công Duy Điện thoại di động ngày càng mỏng và phong cách hơn. 9 . Ba năm sau "dế" này vẫn được đánh giá là một trong những điện thoại phổ biến nhất trên thị trường. bản nhạc từ dịch vụ iTunes. 13.

10 . xem video. Đây là thiết kế cách tân với màn hình cảm ứng cảm ứng đa điểm sẽ mang lại một thế giới sinh động với e-mail bằng giọng nói. Apple khẳng định tháng 6 là thời điểm quan trọng để iPhone sẽ xuất hiện trên thị trường. dùng hệ điều hành Mac OS X. bàn phím SureType. tính năng chụp ảnh.Mobile Programming (Private Version) 14. 15. Giá bán: 500 USD cho mẫu 4 GB. trình duyệt web Safari. BlackBerry Pearl trở thành viên ngọc quý của không ít người. iPhone là thiết bị gây tò mò nhất trong giới chơi điện thoại di động. Phong cách: BlackBerry Pearl (2006) Trịnh Công Duy Thiết kế mỏng. 600 USD cho mẫu 8 GB. nghe nhạc. hỗ trợ dịch vụ e-mail ấn tượng và nhiều khả năng đa phương tiện khác. Cách tân với Apple iPhone (2007) Sau những tháng chờ đợi. bàn phím "mềm" dạng Qwerty có thể đoán từ. tích hợp camera2-megapixel.

Có tất cả bốn kích thước cell site trong mạng GSM đó là macro. Để nén họ sử dụng hệ thống có tên là linear predictive coding (LPC). Thuận lợi đối với nhà điều hành mạng là khả năng triển khai thiết bị từ nhiều người cung ứng. 2G). GMS: Hệ thống thông tin di động toàn cầu Hệ thống thông tin di động toàn cầu (tiếng Anh: Global System for Mobile Communications.2 kbps. micro cell lại được lắp ở các khu thành thị. GSM là chuẩn phổ biến nhất cho điện thoại di động (ĐTDĐ) trên thế giới. GSM cho phép nhà điều hành mạng có thể sẵn sàng dịch vụ ở khắp nơi. chất lượng cuộc gọi.Mobile Programming (Private Version) Trịnh Công Duy I.833 kbit/s và khoảng thời gian của một khung là 4. Các mạng sử dụng băng tần 900 Mhz thì đường uplink sử dụng tần số trong dãi 890-915 MHz và đường downlink sử dụng tần số trong dãi 935-960 MHz. Hầu hết thì hoạt động ở băng 900 Mhz và 1800 Mhz. mỗi kênh cách nhau 1 khoảng 200 Khz. GSM là một chuẩn mở. Sử dụng công nghệ phân chia theo thời gian TDM (time division multiplexing) để chia ra 8 kênh full rate hay 16 kênh haft rate. Khả năng phú sóng rộng khắp nơi của chuẩn GSM làm cho nó trở nên phổ biến trên thế giới. Tốc độ truyền dữ liệu của một kênh là 270. Và chia các băng tần này thành 124 kênh với độ rộng băng thông 25 Mhz. viết tắt: GSM) là một công nghệ dùng cho mạng thông tin di động. Vài nước ở Châu Mỹ thì sử dụng băng 850 Mhz và 1900 Mhz do băng 900 Mhz và 1800 Mhz ở nơi này đã bị sử dụng trước. lợi thế chính của GSM là chất lượng cuộc gọi tốt hơn. Công suất phát của máy điện thoại được giới hạn tối đa là 2 watt đối với băng GSM 850/900 Mhz và tối đa là 1 watt đối với băng GSM 1800/1900 Mhz. cho phép người sử dụng có thể sử dụng ĐTDĐ của họ ở nhiều vùng trên thế giới.1 Khz đó là mã hoá 6 và 13 kbps gọi là full rate (13 kbps) và haft rate (6 kbps). pico cell 11 . pico và umbrella. Có 8 khe thời gian gộp lại gọi là 1một khung TDMA. Nó được xem như là một hệ thống ĐTDĐ thế hệ thứ hai (second generation. khu dân cư. Dịch vụ GSM được sử dụng bởi hơn 2 tỷ người trên 212 quốc gia và vùng lãnh thổ. tiếng Pháp: Groupe Spécial Mobile. Mạng GSM sử dụng 2 kiểu mã hoá âm thanh để nén tín hiệu âm thanh 3. giá thành thấp và dịch vụ tin nhắn. vì thế người sử dụng có thể sử dụng điện thoại của họ ở khắp nơi trên thế giới. Các mạng thông tin di động GSM cho phép có thể roaming với nhau do đó những máy điện thoại di động GSM của các mạng GSM khác nhau ở có thể sử dụng được nhiều nơi trên thế giới. Macro cell được lắp trên cột cao hoặc trên các toà nhà cao tầng.Vào năm 1997 thì họ cải tiến thêm cho mạng GSM là bộ mã GSM-EFR sử dụng full rate 12. micro.2. Các mạng di động GSM hoạt động trên 4 băng tần. Giao diện radio GSM là mạng điện thoại di động thiết kế gồm nhiều tế bào do đó các máy điện thoại di động kết nối với mạng bằng cách tìm kiếm các cell gần nó nhất. Vùng phủ sóng của mỗi cell phụ thuộc nhiều vào môi trường.615 m. GSM khác với các chuẩn tiền thân của nó về cả tín hiệu và tốc độ. hiện tại nó được phát triển bởi 3rd Generation Partnership Project (3GPP) Đứng về phía quan điểm khách hàng. Các chuẩn công nghệ a.

Trên đường tiến đến 3G. e. và 2100 MHz. CDMA2000. GPRS (General Packet Radio Service) là dịch vụ vô tuyến gói tổng hợp được phát triển trên nền tảng công nghệ thông tin di động toàn cầu (GSM) sử dụng đa truy nhập phân chia theo thời gian (TDMA). d. 700 MHz. Trong thực tế thì khả năng phủ sóng xa nhất của một trạm GSM là 32 km (22 dặm). sân bay.5G. Công nghệ GPRS hay còn biết đến với mạng di động thế hệ 2. Umbrella lắp bổ sung vào các vùng bị che khuất hay các vùng trống giữa các cell. Kỹ thuật CDMA hiện đang được sử dụng rộng rãi trong các hệ thống điện thoại di động: IS95A/B. CDMA2000 1xRRT thường được nhắc đến như là một công nghệ 2. Tuy nhiên.. CDMA2000 EV-DO được QualComm đề xuất vào 3/2000 và được chuẩn hóa bởi 3GPP2. b. CDMA2000 là một họ chuẩn thông tin di động sử dụng công nghệ CDMA được chuẩn hóa bởi 3GPP2. CDMA2000 là công nghệ 3G tiếp nối của công nghệ 2G CdmaOne (hay IS-95) và được xem như là một đối thủ cạnh tranh với công nghệ 3G/UMTS. 1800 MHz. Tín hiệu truyền sẽ là tín hiệu chồng chập của nhiều người dùng khác nhau theo thời gian và trên cùng 1 băng tần số. là một công nghệ di động được nâng cấp từ GPRS cho phép truyền dự liệu với tốc độ có thể lên đến 384 kbit/s cho người dùng cố định hoặc di chuyển chậm và 144kbit/s cho người dùng di chuyển tốc độ cao. Trong kỹ thuật CDMA. CDMA2000 EV-DV. WCDMA(UMTS). 1700 MHz. EDGE được biết đến như một công nghệ 2. thì người ta sẽ dùng các trạm pico để chuyển tiếp sóng từ các anten ngoài trời vào. Một số khu vực trong nhà mà các anten ngoài trời không thề phủ sóng tới như nhà ga. CDMA2000 EV-DO. EDGE (Enhanced Data Rates for GSM Evolution). Chính vì thế. CDMA (Code Division Multiple Access). 800 MHz. Thực tế bên cạnh điều chế GMSK. CDMA2000 EV-DO (Evolution-Data Optimized) là một chuẩn thông tin di động thuộc họ CDMA2000. siêu thị.75G.. CDMA2000 bao gồm 3 chuẩn chính: CDMA2000 1xRTT. tín hiệu của mỗi người dùng (user) sẽ được dàn trãi (spreading) bằng 1 mã xác định trực giao (hoặc giả trực giao) với nhau. 1900 MHz. f. c. áp dụng nguyên lý gói vô tuyến để truyền số liệu của người sử dụng một cách có hiệu quả giữa máy điện thoại di động tới các mạng truyền số liệu.. đôi khi còn gọi là EGPRS. CDMA2000 hoạt động trong dãi tần số 450 MHz.5G (tương ứng với GPRS). là một kỹ thuật đa truy cập vào kênh truyền vật lý sử dụng kỹ thuật phân chia theo mã (code). độ lợi anten thường thì nó có thể từ vài trăm mét tới vài chục km. Bán kính phủ sóng của một cell tuỳ thuộc vào độ cao của anten. 900 MHz. kết hợp giữa kỹ thuật CDMA và TDMA để cung cấp dịch vụ số liệu tốc độ cao..Mobile Programming (Private Version) Trịnh Công Duy thì tầm phủ sóng chỉ khoảng vài chục mét trở lại nó thường được lắp để tiếp sóng trong nhà. Đặc điểm nổi bật của CDMA2000 EV-DO: 12 . tiếng Việt gọi là phương thức Đa truy cập phân chia theo mã. các nhà cung cấp mạng phải thay đổi trạm phát sóng BTS cũng như là thiết bị di động so với mạng GPRS. EDGE dùng phương thức điều chế 8-PSK để tăng tốc độ dự liệu truyền. để triển khai EDGE.

là một kĩ thuật đa truy cập vào kênh truyền vật lý sử dụng kĩ thuật phân chia theo thời gian. Time Division Multiple Access (TDMA).4576 Mbps trên băng thông 1.Kỹ thuật chuyển mạch đó là chuyển mạch gói ghép kênh theo thời gian.25 MHz và tốc độ số liệu hướng lên 153. . .6 Kbps. .Mobile Programming (Private Version) Trịnh Công Duy . tiếng Việt gọi là phương thức Đa truy cập phân chia theo thời gian. g.Trạm gốc linh hoạt dễ dàng phát công suất lớn. 13 .Hỗ trợ sơ đồ điều chế cấp cao kèm với khả năng thực hiện nhiều phương pháp sửa lỗi.EV-DO hỗ trợ tốc độ số liệu hướng xuống 2.

Mobile Programming (Private Version)

Trịnh Công Duy

II. Giới thiệu về J2ME
II.1. Lịch sử
J2ME được phát triển từ kiến trúc Java Card, Embeded Java và Personal Java của phiên bản Java 1.1. Đến sự ra đời của Java 2 thì Sun quyết định thay thế Personal Java và đươc gọi với tên mới là Java 2 Micro Edition, hay viết tắt là J2ME. Đúng với tên gọi, J2ME là nền tảng cho các thiết bị có tính chất nhỏ, gọn:

II.2. Lý do chọn J2ME
a) Java ban đầu được thiết kế dành cho các máy với tài nguyên bộ nhớ hạn chế. b) Thị trường của J2ME được mở rộng ra cho nhiều chủng loại thiết bị như:     Các lọai thẻ cá nhân như Java Card Máy điện thoại di động Máy PDA (Personal Digital Assistant - thiết bị trợ giúp cá nhân) Các hộp điều khiển dành cho tivi, thiết bị giải trí gia dụng …

II.3. Kiến trúc của J2ME
Phần này sẽ trình bày kiến trúc tổng quát của nền tảng Java

14

Mobile Programming (Private Version)

Trịnh Công Duy

a) Giới thiệu các thành phần trong nền tảng J2ME: Định nghĩa về Configuration (Cấu hình): là đặc tả định nghĩa một môi trường phần mềm cho một dòng các thiết bị được phân loại bởi tập hợp các đặc tính, ví dụ như:  Kiểu và số lượng bộ nhớ  Kiểu và tốc độ bộ vi xử lý  Kiểu mạng kết nối Do đây là đặc tả nên các nhà sản xuất thiết bị như Samsung, Nokia …bắt buộc phải thực thi đầy đủ các đặc tả do Sun qui định để các lập trình viên có thể dựa vào môi trường lập trình nhất quán và thông qua sự nhất quán này, các ứng dụng được tạo ra có thể mang tính độc lập thiết bị cao nhất có thể. Ví dụ như một lập trình viên viết chương trình game cho điện thoại Samsung thì có thể sửa đổi chương trình của mình một cách tối thiểu nhất để có thể chạy trên điện thọai Nokia.. Hiện nay Sun đã đưa ra 2 dạng Configuration: CLDC (Connected Limited Device Configuration-Cấu hình thiết bị kết nối giới hạn): được thiết kế để nhắm vào thị trường các thiết bị cấp thấp (low-end), các thiết bị này thông thường là máy điện thọai di động và PDA với khoảng 512 KB bộ nhớ. Vì tài nguyên bộ nhớ hạn chế nên CLDC được gắn với Java không dây (Java Wireless ), dạng như cho phép người sử dụng mua và tải về các ứng dụng Java, ví dụ như là Midlet.  CDC- Connected Device Configuration (Cấu hình thiết bị kết nối): CDC được đưa ra nhắm đến các thiết bị có tính năng mạnh hơn dòng thiết bị thuộc CLDC nhưng vẫn yếu hơn các hệ thống máy để bàn sử dụng J2SE. Những thiết bị này có nhiều bộ nhớ hơn (thông thường là trên 2Mb) và có bộ xử lý mạnh hơn. Các sản phẩm này có thể kể đến như các máy PDA cấp cao, điện thoại web, các thiết bị gia dụng trong gia đình … Cả 2 dạng Cấu hình kể trên đều chứa máy ảo Java (Java Virtual Machine) và tập hợp các lớp (class) Java cơ bản để cung cấp một môi trường cho các ứng dụng J2ME. Tuy nhiên, bạn chú ý rằng đối với các thiết bị cấp thấp, do hạn chế về tài nguyên như bộ nhớ và bộ xử lý nên không thể yêu cầu máy ảo hổ trợ tất cả các tính năng như với máy ảo của 15 

Mobile Programming (Private Version)

Trịnh Công Duy

J2SE, ví dụ, các thiết bị thuộc CLDC không có phần cứng yêu cầu các phép tính toán dấu phẩy động, nên máy ảo thuộc CLDC không được yêu cầu hỗ trợ kiểu float và double.

Bảng dưới là sự so sánh các thông số kỹ thuật của CDC và CLDC CLDC CDC Ram >=32K, <=512K >=256K Rom >=128k, <=512k >=512k Nguồn Năng Có GiớI Hạn (nguồn pin) Không giới hạn Lượng Network Chậm Nhanh Định nghĩa veef Profile: Profile mở rộng Configuration bằng cách thêm vào các class để bổ trợ các tính năng cho từng thiết bị chuyên biệt. Cả 2 Configuration đều có những profile liên quan và từ những profile này có thể dùng các class lẫn nhau. Đến đây ta có thể nhận thấy do mỗi profile định nghĩa một tập hợp các class khác nhau, nên thường ta không thể chuyển một ứng dụng Java viết cho một profile này và chạy trên một máy hỗ trợ một profile khác. Cũng với lý do đó, bạn không thể lấy một ứng dụng viết trên J2SE hay J2EE và chạy trên các máy hỗ trợ J2ME. Sau đây là các profile tiêu biểu:  Mobile Information Device Profile (MIDP): profile này sẽ bổ sung các tính năng như hỗ trợ kết nối, các thành phần hỗ trợ giao diện người dùng … vào CLDC. Profile này được thiết kế chủ yếu để nhắm vào điện thọai di động với đặc tính là màn hình hiển thị hạn chế, dung lượng chứa có hạn. Do đó MIDP sẽ cung cấp một giao diện người dùng đơn giản và các tính năng mạng đơn giản dựa trên HTTP. Có thể nói MIDP là profile nổi tiếng nhất bởi vì nó là kiến thức cơ bản cho lập trình Java trên các máy di động (Wireless Java)   PDA Profile: tương tự MIDP, nhưng với thị trường là các máy PDA với màn hình và bộ nhớ lớn hơn Foundation Profile: cho phép mở rộng các tính năng của CDC với phần lớn các thư 16

Giới thiệu MIDP II. MIDP cung cấp các chức năng cơ bản cho hầu hết các dòng thiêt bị di động phổ biến nhất như các máy điện thoạI di động và các máy PDA.        c) Những chức năng MIDP cung cấp 17 . Hỗ trợ từ khóa finalize() như trong J2SE: Việc “dọn dẹp“ tài nguyên trước khi nó bị xóa được đẩy về phía các lập trình viên.4. Không hỗ trợ JNI Hỗ trợ hạn chế thao tác bắt lỗi. Bộ nạp class (Class Loader). Sun đã cung cấp một chức năng khác tương đương gọi là Record Management system (RMS) để cung cấp khả năng lưu trữ cho các thiết bị này. b) Những chức năng MIDP không thực hiện được: Phép tính dấu phẩy động (floating point): Phép tính này đòi hỏi rất nhiều tài nguyên CPU và phần lớn các CPU cho các thiết bị di động không hỗ trợ phép tính này.3 Ngoài ra còn có Personal Basis Profile. do đó MIDP cũng không có. Tuy nhiên. Giới thiệu về MIDP a) Định nghĩa: Đây là Profile được định nghĩa dành riêng cho các thiết bị di động và là thành phần chính trong J2ME. Trong phần sau tôi sẽ liệt kê qua các tính năng mà MIDP cung cấp và những giới hạn của nó. II.v.4. Tuy nhiên MIDP không phải là cây đũa thần cho mọi lập trình viên vì như chúng ta đã biết. MIDP được thiết kế cho các máy di động có cấu hình rất thấp.Mobile Programming (Private Version) Trịnh Công Duy viện của bộ Core Java2 1. RMI Profile. Phần lớn các thư viện API cho Swing và AWT không thể sử dụng được trong MIDP. Không hỗ trợ các tính năng quản lý file và thư mục: Đây có thể làm bạn ngạc nhiên nhưng thực tế là các thiết bị J2ME không có hỗ trợ các thiết bị lưu trữ thông thường như ổ cứng v. Personal Profile. Game Profile.1. điều đó không có nghĩa là bạn phải mất đi mọi dữ liệu quan trọng mỗi khi tắt máy.

2.0 thì các lập trình viên phải tự mình viết code để quản lý các hành động của nhân vật cũng như quản lý đồ họa.  Hỗ trợ các lập trình viên Game bằng cách tung ra Game API: Có lẽ Sun đã kịp nhận ra thị trường đầy tiềm năng của các thiết bị di động trong lĩnh vực Game.0. Một trong nhưng cải tiến hấp dẫn nhất của MIDP 2. Thuật ngữ MIDlet và MID application được sử dụng như nhau. o Kiểm soát việc kết nối giữa máy di động và server: ví dụ như các chương trình không thể kết nối tới server nếu thiếu sự chấp thuận của người sử dụng.lcdui trong MIDP 2. Được hưởng lợi nhất từ Game API trong MIDP 2.  Hỗ trợ kiểu ảnh RGB: một trong những cải tiến hấp dẫn cho các nhà phát triển MIDP là việc biểu diễn hình ảnh dưới dạng các mảng số nguyên. con đường là một layer và chiếc xe được xem như đang nằm trên layer khác.0 không chỉ là các lập trình viên Game mà còn là các lập trình viên cần sử dụng các tính năng đồ họa cao cấp.  Thêm các API hỗ trợ Multimedia. MIDLet MIDP (Mobile Information Device Profile) là tập các hàm API dành cho thiết bị thông tin di động. 18 . Ý tưởng cơ bản của Game API là việc giả định rằng một màn hình game là tập hợp các layer (lớp). cho phép MIDlet thao tác với dữ liệu hình ảnh một cách trực tiếp.0.  Mở rộng các tính năng của Form. Những cải tiến nổi bật so với MIDP 1. Các MIDlets hình thành nên các khối xây dựng của môi trường thực thi Java 2 Platform Micro Edition (J2ME). Các API này là một tập con chỉ hỗ trợ âm thanh của Mobile Media API (MMAPI). Hỗ trợ đối tượng Display: Đúng như tên gọi một chương trình MIDP sẽ hỗ trợ duy nhất một đối tượng Display là đối tượng quản lý việc hiển thị dữ liệu trên màn hình điện thoại. Hỗ trợ Form và các giao diện người dùng. II.microedition.0 với hàng loạt tính năng khác được cung cấp thêm so với bản 1. nhưng các thay đổi lớn nhất (ngoài API cho game) là trong Form và Item.Mobile Programming (Private Version)  Trịnh Công Duy     Các lớp và kiểu dữ liệu: Phần lớn các lớp mà các lập trình viên Java quen thuộc vẫn còn được giữ lại ví dụ như các lớp trong gói java. Việc này sẽ làm tăng kích thước file của sản phẩm cũng như việc xuất hiện các đoạn mã bị lỗi. Nhiều cải tiến đã được đưa vào API javax.4. MIDlet là ứng dụng MIDP.0 là tập các API media của nó. Hỗ trợ Timer và Alert Cung cấp tính năng Record Management System (RMS) cho việc lưu trữ dữ liệu Ngoài ra vào tháng 11 năm 2003 Sun đã tung ra MIDP 2.0  Nâng cấp các tính năng bảo mật như o Download qua mạng an toàn hơn qua việc hỗ trợ giao thức HTTPS. Ví dụ như: trong một game đua xe thì màn hình nền là một layer. Với MIDP 1. Với Game API nhà phát triển còn được cung cấp các tính năng như quản lý các thao tác bàn phím.util như Stack. Vector và Hastable cũng như Enumeration.

và hủy MIDlet. Lớp javax. Trong ví dụ này MIDletExample là bắt đầu của ứng dụng.midlet. và tạm dừng MIDlet. II. Hình 2. Các phương thức thuộc lớp này cho phép trình quản lý ứng dụng tạo.MIDlet và thực thi (implement) các phương thức startApp(). dừng. (3) Hàm khởi tạo . Hình biểu diễn bộ khung yêu cầu tối thiểu cho một ứng dụng MIDlet (1) Phát biểu import: Các phát biểu import được dùng để include các lớp cần thiết từ các thư viện CLDC và MIDP. và destroyApp().microedition. trong trường hợp có cuộc gọi đến. Bộ khung của MIDlet (MIDlet Skeleton) Một MIDlet là một lớp Java kế thừa (extend) của lớp trừu tượng java.3.Constructor: 19 . pauseApp(). bắt đầu.microedition. (2) Phần chính của MIDlet: MIDlet được định nghĩa như một lớp kế thừa lớp MIDlet.midlet. Hình 1. một dạng đơn giản nhất của máy ảo Java được thiết kế để chạy trên thiết bị di động. Hình biểu diễn MIDlet Thông báo import dùng để truy xuất các lớp của CLDC và MIDP.Mobile Programming (Private Version) Trịnh Công Duy MIDlet được thiết kế để chạy và được điều khiển bởi trình quản lý ứng dụng trong máy ảo K (K Virtual Machine .KVM). Lớp MIDlet được trình quản lý ứng dụng trên điện thoại di động dùng để khởi động. Có thể chỉ có một lớp trong ứng dụng kế thừa lớp này. Ví dụ.4.MIDlet hoạt động như là một giao diện (interface) giữa MIDlet và trình quản lý ứng dụng. tạm dừng. Lớp chính của ứng dụng được định nghĩa là lớp kế thừa lớp MIDlet của MIDP.

Nếu không thì chúng sẽ không được khởi tạo lại bởi ứng dụng. Lớp chính này định nghĩa ba phương thức hành động trong chu kỳ sống của nó: startApp(). MIDlet có thêm tùy chọn từ chối thoát bằng cách ném ra một ngoại lệ MIDletStateChangeException. destroyed: MIDlet đã kết thúc và sẵn sàng cho việc tái chế bởi bộ thu gom rác. (6) destroyApp(): Phương thức destroyApp() được gọi khi thoát MIDlet. Cách thích hợp để sử dụng pauseApp() là giải phóng tài nguyên và các biến để dành cho các chức năng khác trong điện thoại trong khi MIDlet được tạm dừng. Chú ý ở đây không có trạng thái nào tương đương với trạng thái loaded của applet. và destroyApp(). Nếu tham số này là true. pauseApp(). Hàm tạo sẽ không được gọi lại trừ phi MIDlet thoát và sau đó khởi động lại. Có ba trạng thái trong vòng đời của một MIDlet: paused: MIDlet instance đã được tạo xong và chưa hoạt động (inactive) active: MIDlet đang hoạt động. Nó chỉ đơn thuần là thoát MIDlet. Vòng đời của MIDlet (gMIDlet lifecycle) Lớp chính của một MIDlet kế thừa là lớp javax. một MIDlet khởi tạo chính nó ngay lần đầu tiên khi phương thức startApp() được gọi. Thông thường. Việc này không được đề cập trong MIDP mà đó là do nhà sản xuất quyết định sẽ chọn cách nào. Nói chung. trong trường hợp có cuộc gọi hoặc tin nhắn đến). 20 . Ví dụ khi nhấn nút exit trong ứng dụng. (4) startApp(): Phương thức startApp() được gọi bởi bộ quản lý ứng dụng khi MIDlet được khởi tạo.4. Nó không thật sự xóa ứng dụng khỏi điện thoại di động.Mobile Programming (Private Version) Trịnh Công Duy Hàm tạo chỉ được thực thi một lần khi MIDlet được khởi tạo lần đầu tiên. các biến toàn cục sẽ được khởi tạo lại trừ hàm tạo bởi vì các biến đã được giải phóng trong hàm pauseApp(). (5) pauseApp(): Phương thức pauseApp() được gọi bởi bộ quản lý ứng dụng mỗi khi ứng dụng cần được tạm dừng (ví dụ. bởi vì ở đây không có phương thức khởi tạo.4. Cần chú ý rằng khi nhận cuộc gọi đến hệ điều hành trên điện thoại di động có thể dừng KVM thay vì dừng MIDlet. Nếu tham số là false. MIDlet được tắt vô điều kiện. II. Phương thức destroyApp() chỉ nhận một tham số Boolean.microedition. và mỗi khi MIDlet trở về từ trạng thái tạm dừng.midlet.MIDlet.

bộ quản lý ứng dụng sẽ thực thi MIDlet (thông qua lớp MIDlet). II. Các phương thức dùng để điều khiển các trạng thái của MIDlet: resumeRequest(): Yêu cầu vào chế độ hoạt động. Tập tin JAR Các lớp đã biên dịch của ứng dụng MIDlet được đóng gói trong một tập tin JAR (Java Archive File). Hàm destroyApp() chỉ có thể gọi từ trạng thái hoạt động hay tạm dừng. nó sẽ được xem là đang ở trạng thái tạm dừng. Bộ quản lý ứng dụng gọi hàm tạo và hàm startApp(). Khi ứng dụng thực thi. cũng như các tài nguyên cần thiết. MIDP chỉ hỗ trợ định dạng hình . Đây chính là tập tin JAR được download xuống điện thoại di động. Hàm startApp() có thể được gọi nhiều lần trong suốt chu kỳ sống của ứng dụng.5. Hình biểu diễn các trạng thái của MIDlet Khi người dùng yêu cầu khởi động ứng dụng MIDlet. notifyPaused(): Cho biết MIDlet tự nguyện chuyển sang trạng thái tạm dừng Ví dụ: Khi đợi một sự kiện timer. phương thức notifyPaused() sẽ được dùng để yêu cầu bộ quản lý ứng dụng chuyển ứng dụng sang trạng thái tạm dừng. Ví dụ: Khi MIDlet tạm dừng. và một sự kiện timer xuất hiện. Hiện tại. Tập tin JAR chứa tất cả các tập tin class từ một hay nhiều MIDlet. notifyDestroyed(): Sẵn sàng để hủy.Mobile Programming (Private Version) Trịnh Công Duy Hình 3.4. Ví dụ: Xử lý nút nhấn Exit Lập trình viên có thể yêu cầu tạm dừng MIDlet trong khi đợi một sự kiện timer hết hạn. Lập trình viên cũng có thể điều khiển trạng thái của MIDlet.png (Portable Network Graphics). 21 . Trong trường hợp này. Tập tin JAR cũng chứa tập tin kê khai (manifest file) mô tả nội dung của MIDlet cho bộ quản lý ứng dụng.

com/games MIDlet-Jar-URL: http://www.png. MIDP 2. Nhưng nó nằm ngoài tập tin JAR.png. trong MIDP. thì quá trình download sẽ bị hủy bỏ thay vì phải đợi download hết toàn bộ tập tin JAR. Tập tin JAR là toàn bộ ứng dụng MIDlet.Solitaire MIDlet-2: BlackJack. Nói chung. 22 .com/j2me/games MIDlet-Jar-Size: 1063 MicroEdtion-Profile: MIDP-1.0 MIDlet-Vendor: Sony Ericsson MIDlet-Description: Set of Card Games MIDlet-Info-URL: http://www.mf: MIDlet-Name: CardGames MIDlet-Version: 1. /Blkjk. MIDlet có thể load và triệu gọi các phương thức từ bất kỳ lớp nào trong tập tin JAR. nếu người dùng muốn download một ứng dụng không được thiết bị di động hỗ trợ (ví dụ.0.semc.0 MIDlet-1: Solitaire.0 MicroEdtion-Configuration: CLDC-1.semc. cần ít thời gian để download một tập tin văn bản nhỏ hơn là download một tập tin JAR.0).BlackJack Tập tin JAD chứa cùng thông tin như tập tin manifest. Ưu điểm của tập tin JAD là các đặc điểm của MIDlet có thể được xác định trước khi download tập tin JAR.mf) và tập tin JAD (Java Application Descriptor) mô tả các đặc điểm của MIDlet. Sự khác biệt của hai tập tin này là tập tin kê khai là một phần của tập tin JAR còn tập tin JAD không thuộc tập tin JAR. Như vậy.Mobile Programming (Private Version) Trịnh Công Duy Nó cũng phải chứa các tập tin dữ liệu mà MIDlet cần. /Sol. Ví dụ một tập tin manifest. Mô tả nội dung của tập tin JAR: Các trường yêu cầu: Manifest-Version // Phiên bản tập tin Manifest MIDlet-Name // Tên bộ MIDlet (MIDlet suite) MIDlet-Version // Phiên bản bộ MIDlet MIDlet-Vendor // Nhà sản xuất MIDlet MIDlet-<n> for each MIDlet // Tên của MIDlet MicroEdtion-Profile // Phiên bản hiện trạng MicroEdtion-Configuration // Phiên bản cấu hình Tập tin kê khai (manifest) và tập tin JAD Tập tin kê khai (manifest. com.semc.semc. com. hay CLDC. Nó không thể truy xuất các lớp không phải là bộ phận của tập tin JAR hay vùng dùng chung của thiết bị di động.

1 . và dữ liệu của chúng.4 trở lên.5. toàn bộ tập tin JAR phải được cập nhật. Hình biểu diễn hai bộ MIDlet Trong hình trên. II.com/j2me/download. Ngoài ra bạn cần phải có J2SDK 1. Bộ kia chỉ chứa MIDlet4. Môi trường phát triển J2ME II. các hình ảnh.com/products/sjwtoolkit/download2_5_1.4. MIDlet4 cũng không truy xuất được các lớp.sun. Địa chỉ download chương trình: http://java.sun. Giá trị trong tập tin mô tả sẽ đè giá trị của tập tin manifest.Wireless toolkit là công cụ cho phép chúng ta biên dịch và chạy những ứng dụng điện thoại trên điện thoại ảo. Phát triển ứng dụng J2ME với J2ME Wireless Toolkit Có nhiều môi trường phát triển ứng dụng J2ME và mỗi hãng điện thoại cũng đưa ra những môi trường phát triển cho riêng mình.html. và dữ liệu lưu trữ bền vững. Các thuộc tính khác không cần phải lặp lại. có thể download tại http://java.Mobile Programming (Private Version) Trịnh Công Duy Các thuộc tính MIDlet-Name. II.6. MIDlet2.html 23 . Ba MIDlet trong bộ đầu tiên truy xuất các lớp và dữ liệu của nhau nhưng không truy xuất đến các lớp hay dữ liệu của MIDlet4. Các MIDlet trong một bộ MIDlet chia sẻ các lớp.5. Ngược lại. và MIDlet-Vendor phải được lặp lại trong tập tin JAD và JAR. Ở đây tôi sẽ giới thiệu toolkit của Sun: J2ME Wireless Toolkit. Bộ MIDlet (MIDlet Suite) Một tập các MIDlet trong cùng một tập tin JAR được gọi là một bộ MIDlet (MIDlet suite). Cài đặt Wireless tool Kit 2. và MIDlet3. Hình 4. MIDlet-Version. Để cập nhật một MIDlet.5. một bộ MIDlet chứa MIDlet1.1. hình ảnh.

Từ Target Platform chọn Custom. chúng ta có thể tra cứu flatform tại đây http://www.Chạy file sun_java_wireless_toolkit-2_5_1-windows.Mobile Programming (Private Version) Trịnh Công Duy .com/devices/matrix_s60_1.nokia.Chúng ta click chuột vào New project và nhấp vào Project và Class bạn muốn tạo vidu: TestMidlet và không nhất thiết tên Class trùng với tên Project .5. Ví dụ chúng ta muốn ứng dụng thích hợp với điện thoại N70 cho nên trong group MIDP 2.0 và CLDC 1.exe để cài đặt chương trình. bạn sẽ có thư mục chương trình trong menu Start như hình bên dưới Để chạy chương trình chúng ta chọn wireless Toolkit 2.1. Bạn sẽ thấy cửa sổ hiện ra như hình bên dưới Chương trình Hello đầu tiên . Sau khi cài đặt thành công.forum.html 24 .1.

Mobile Programming (Private Version)

Trịnh Công Duy

ToolKit sẽ tự động tạo 1 thư mục con trong thư mục apps có tên trùng với tên Project mà bạn vừa tạo, trong trường hợp này là TestMidlet.

25

Mobile Programming (Private Version)

Trịnh Công Duy

Các thư mục trong dự án: - Bin: Chứa 2 file để cài đặt, .Jar ( Java ARchive) và file miêu tả .jad. - Lib: Chứa các thư viện bổ sung mà bạn muốn thêm vào chương trình. - Res: là thư mục để bạn để các file tài nguyên dành riêng cho chương trình như file văn bản và hình ảnh. - Src: Đây là thư mục quan trọng nhất, là nơi bạn đặt các file chứa mã nguồn. Bây giờ chúng ta dùng chương trình soãn thảo văn bản nào đó và viết:
import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class TestMidle extends MIDlet implements CommandListener { private Form mMainForm; public TestMidlet() { mMainForm = new Form("Chuong trinh so 1"); mMainForm.append(new StringItem(null, "Chao cac ban, MIDP!")); mMainForm.addCommand(new Command("Exit", Command.EXIT, 0)); mMainForm.setCommandListener(this); } public void startApp() { Display.getDisplay(this).setCurrent(mMainForm); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command c, Displayable s) {

26

Mobile Programming (Private Version)
notifyDestroyed(); } }

Trịnh Công Duy

- Sau đó, hãy lưu đoạn code trên vào file tên là TestMidlet.java trong thư mục src. - Bấm vào nút Build, nếu code của bạn không bị lỗi, bạn sẽ nhận được thông báo sau: Project settings saved; Building "TestMidlet"; Build complete. - Nhấn nút Run để chạy Emulator - Nếu bạn thấy trên máy bạn xuất hiện hình như hình bên dưới thì xin chúc mừng bạn. Chương trình J2ME đầu tiên đã chạy thành công.

27

Bước 3: Browse tới thư mục C:\WTK2.1. sau đó chọn Next .5.exe để cài đặt . 28 .5.5.2. bạn có thể download tại đây http://dlc.sun.5. Phát triển ứng dụng J2ME với Netbean Import Wireless toolkit project sang Netbeans .Trước tiên ta cần phải có module Netbeans_mobility 5.Bước 2: trong Categories chọn Mobile và trong Projects chọn Import Wireless Toolkit Project.1\apps (nơi cài đặt Wireless Toolkit) chọn TestMidlet sau đó chọn Open -> Next..Đây là dao diện khi khởi động Netbeans .Bước 1: Chọn File -> New Project . 704122300/ .Mobile Programming (Private Version) Trịnh Công Duy II..1 đề gắn vào Netbeans 5.com/netbeans/download/5_ .Chạy file netbeans_mobility-5_5_1-windows_2.

sau đó nhấn Next.Bước 4: Đặt tên Project và cấu hình như hình dưới đây. Device Configuration và Device Profile chương trinh sẽ tự 29 .Bước 5: cấu hình như hình dưới.Mobile Programming (Private Version) Trịnh Công Duy . .

Người lập trình có thể edit code trên Project nguồn hay Project của Netbeans tùy ý. Một đối tượng Displayable là một thành phần được hiển thị trên một thiết bị.1. tuy nhiên chúng ta có thể chọn theo mục đích riêng của mình mà không làm ảnh hường tới Project nguồn bên Wireless Toolkit vì Netbeans sẽ tự đông copy Project nguồn sang dạng Project của Netbeans sau mỗi lần biên dịch. nhưng nhiều đối tượng trong một MIDlet có thể được hiển thị ra trên thiết bị như Forms. Displayable và Screens Một ứng dụng MIDlet chỉ có 1 đối tượng thể hiện Display. ChoiceGroups. Đối tượng Display. . Cuối cùng là chọn Finish. Các thành phần giao diện ở mức cao của ứng dụng MIDP III.Để build chương trình chúng ta có thể vào Build -> Build Main Project và Run -> Run Main Project để chạy chương trình III.Mobile Programming (Private Version) Trịnh Công Duy động chọn tương ứng với Midlet và CLDC bên Project của Wireless Toolkit. Đối tượng này dùng để lấy thông tin về đối tượng trình bày. và bao gồm các phương thức để yêu cầu các đối tượng được trình bày. . ví dụ màu được hỗ trợ. TextBoxes. mà lớp Screen 30 . MIDP chứa 2 lớp con của lớp Displayable là Screen và Canvas. Hình dưới đây mô tả mối quan hệ trên Một đối tượng Screen không phải là một cái gì đó hiện ra trên thiết bị. Đối tượng Display cần thiết cho bộ quản lý việc trình bày trên thiết bị điều khiển thành phần nào sẽ được hiển thị lên trên thiết bị Mặc dù chỉ có một đối tượng Display ứng với mỗi MIDlet..

chính các thành phần này sẽ được hiển thị ra trên màn hình. Một Form chỉ đơn giản là một khung chứa các thành phần. Tóm lại. Thành phần Form và Items Trong phần này sẽ giới thiệu các thành phần được hiển thị ra trên một Form.Mobile Programming (Private Version) Trịnh Công Duy này sẽ được thừa kế bởi các thành phần hiển thị ở mức cao. phần này chỉ giới thiệu hệ thống phân cấp đối tượng dùng để thể hiện giao diện người dùng trong MIDP. III. Hình dưới đây sẽ mô tả mối quan hệ của lớp Screen và các thành phần thể hiện ở mức cao. mà mỗi thành phần được thừa kế từ lớp Item.2. Chúng ta sẽ xem qua các thành phần hiển thị trên thiết bị trên:  DateField        Gauge StringItem TextField ChoiceGroup Spacer CustomItem Image and ImageItem a) DateField 31 .

Mobile Programming (Private Version)

Trịnh Công Duy

Thành phần DateField cung cấp một phương tiện trực quan để thao tác đối tượng Date được định nghĩa trong java.util.Date. Khi tạo một đối tượng DateField, bạn cần chỉ rõ là người dùng chỉ có thể chỉnh sửa ngày, chỉnh sửa giờ hay đồng thời cả hai. Các phương thức dựng của lớp DateField gồm:
D ateF ield (S tring lab el, in t m o de) D ateF ield (S tring lab el, in t m o de, Tim eZone tim eZone)
Các mode tương ứng của lớp DateField gồm:

D ateF ield .D A T E_T IM E :cho phép thay đổi ngày giờ D ateF ield .T IM E: chỉ cho phép thay đổi giờ D ateF ield .D A T E: chỉ cho phép thay đổi ngày

Ví dụ:
private DateField dfAlarm; // Tạo một đổi tượng DateField với nhãn, cho phép thay đổi cả ngày và giờ Alarm = new DateField("Set Alarm Time", DateField.DATE_TIME); dfAlarm.setDate(new Date());

Dưới đây là đoạn chương trình mẫu thử nghiệm đối tượng DateField
import java.util.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.Timer; import java.util.TimerTask; public class DateFieldTest extends MIDlet implements ItemStateListener, CommandListener { private Display display; // Reference to display object private Form fmMain; // Main form private Command cmExit; // Exit MIDlet private DateField dfAlarm; // DateField component public DateFieldTest() { display = Display.getDisplay(this); // The main form fmMain = new Form("DateField Test"); // DateField with todays date as a default dfAlarm = new DateField("Set Alarm Time", DateField.DATE_TIME); dfAlarm.setDate(new Date()); // All the commands/buttons cmExit = new Command("Exit", Command.EXIT, 1); // Add to form and listen for events fmMain.append(dfAlarm); fmMain.addCommand(cmExit); fmMain.setCommandListener(this); fmMain.setItemStateListener(this);

32

Mobile Programming (Private Version)
} public void startApp () { display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void itemStateChanged(Item item) { System.out.println("Date field changed."); } public void commandAction(Command c, Displayable s) { if (c == cmExit) { destroyApp(false); notifyDestroyed(); } } }

Trịnh Công Duy

b) Gauge
Một thành phần Gauge là một kiểu giao diện thường được dùng để mô tả mức độ hoàn thành một công việc. Có 2 loại Gauge là loại tương tác và loại không tương tác. Loại đầu cho phép người dùng có thể thay đổi Gauge, loại 2 thì đòi hỏi người phát triển phải cập nhật Gauge Dười đây là hàm dựng của lớp Gauge:
G auge(String label, boolean interactive, int maxValue, int initialValue)

Ví dụ:
private Gauge gaVolume; // Điều chỉnh âm lượng gaVolume = new Gauge("Sound Level", true, 100, 4);

Dưới đây là đoạn chương trình mẫu minh họa cách sử dụng lớp Gauge
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class InteractiveGauge extends MIDlet implements CommandListener { private Display display; // Reference to display object private Form fmMain; // The main form private Command cmExit; // Exit the form private Gauge gaVolume; // Volume adjustment

33

Mobile Programming (Private Version)
public InteractiveGauge() { display = Display.getDisplay(this); // Create the gauge and exit command gaVolume = new Gauge("Sound Level", true, 50, 4); cmExit = new Command("Exit", Command.EXIT, 1);

Trịnh Công Duy

// Create form, add commands, listen for events fmMain = new Form(""); fmMain.addCommand(cmExit); fmMain.append(gaVolume); fmMain.setCommandListener(this); } // Called by application manager to start the MIDlet. public void startApp() { display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c, Displayable s) { if (c == cmExit) { destroyApp(false); notifyDestroyed(); } } }

c) StringItem
Một thành phần StringItem được dùng để hiển thị một nhãn hay chuỗi văn bản. Người dùng không thể thay đổi nhãn hay chuỗi văn bản khi chương trình đang chạy. StringItem không nhận ra sự kiện Phương thức dựng của lớp StringItem StringItem(String label, String text) Dưới đây là đoạn mã minh họa việc sử dụng đối tượng StringItem import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class StringItemTest extends MIDlet implements CommandListener { private Display display; // Reference to Display object private Form fmMain; // 34

fmMain. fmMain. Trịnh Công Duy // Create Form.IBM.setCommandListener(this) . add Command and StringItem.com"). // Create text message and commands siMsg = new StringItem("Website: ". "www.setText("developerWor ks").addCommand(cmExit). fmMain.append(siMsg).setCurrent(fmMain). 1). listen for events fmMain = new Form("StringItem Test"). Command.setLabel("Section : ").SCREEN. cmChange = new Command("Change". // StringItem private Command cmChange. } // Called by application manager to start the MIDlet. public void startApp() { display.Mobile Programming (Private Version) Main form private StringItem siMsg. // Change text siMsg. Command.EXIT. // Change the label and message private Command cmExit. fmMain.getDisplay(this).addCommand(cmChange). // Exit the MIDlet public StringItemTest() { display = Display. 1). Displayable s) { if (c == cmChange) { // Change label siMsg. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c. cmExit = new Command("Exit". 35 .

// Main form private Command cmTest.microedition.*. // Textfield public TextFieldTest() { display = Display.*. và loại dữ liệu được phép nhập. public class TextFieldTest extends MIDlet implements CommandListener { private Display display. notifyDestroyed().microedition.midlet.Mobile Programming (Private Version) // Remove the command fmMain. // Command to exit the MIDlet private TextField tfText. 1). Bạn có thể chỉ định một nhãn. int maxSize. } else if (c == cmExit) { destroyApp(false). vì nó là phương tiện để xác định loại dữ liệu nào được phép nhập vào TextField MIDP định nghĩa các tham số ràng buộc sau cho thành phần TextField:  ANY: cho phép nhập bất kỳ ký tự nào  EMAILADDR: chỉ cho phép nhâp vào các địa chỉ email hợp lệ  NUMERIC: chỉ cho phép nhập số  PHONENUMBER: Chỉ cho phép nhập số điện thoại  URL: Chỉ cho phép nhập các ký tự hợp lệ bên trong URL  PASSWORD: che tất cả các ký tự nhập vào Dưới đây là đoạn mã minh họa việc sử dụng thành phần TextField import javax.lcdui. // Reference to Display object private Form fmMain. // Get contents of textfield private Command cmExit.SCREEN. int constraints) Thành phần thứ 3 constraints là thành phần mà chúng ta quan tâm. } } } Trịnh Công Duy d) TextField Một thành phần TextField thì tương tự như bất kỳ các đối tượng nhập văn bản tiêu biểu nào. 36 .removeCommand(cmChange). Command. String text. // Create commands cmTest = new Command("Get Contents". Ngoài ra TextField còn cho phép bạn nhập vào mật khẩu với các ký tự nhập vào sẽ được che bởi các ký tự mặt nạ Phương thức dựng của lớp TextField TextField(String label. số ký tự tối đa được phép nhập.getDisplay(this). import javax.

notifyDestroyed(). fmMain.getString()). } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c.setCommandListener(this). 1).out. fmMain. ChoiceGroup có 2 loại:  multi-selection(cho phép chọn nhiều mục): nhóm này có liên quan đến các 37 . Chúng ta có thể thêm một ràng buộc thứ 2 bằng cách: tfText = new TextField("Phone:".EXIT. "". } } } Đoạn mã trên chỉ mới áp dụng một ràng buộc trên đối tượng TextField.append(tfText). 10. fmMain. Trịnh Công Duy // Textfield for phone number tfText = new TextField("Phone:". e) ChoiceGroup Thành phần ChoiceGroup cho phép người dùng chọn từ một danh sách đầu vào đã được định nghĩa trước. "".PHONENUMBER | TextField. } else if (c == cmExit) { destroyApp(false). TextField. fmMain.setCurrent(fmMain). 10. TextField. public void startApp() { display. Command. listen for events fmMain = new Form("Phone Number").addCommand(cmExit).addCommand(cmTest). add Commands and textfield.PHONENUMBER). } // Called by application manager to start the MIDlet.PASSWORD). // Create Form.println("TextField contains: " + tfText. Displayable s) { if (c == cmTest) { System.Mobile Programming (Private Version) cmExit = new Command("Exit".

// Create Form. Command.2). // Reference to display object private Form fmMain.setItemStateListener(this). null).lcdui. // Index of the "Select All" option private ChoiceGroup cgPrefs. null).midlet.SCREEN. Command.microedition.append("Replace tabs with spaces". 1).getDisplay(this). cmExit = new Command("Exit". import javax. null).append("Save bookmarks".MULTIPLE). // Choice Group of preferences  private int choiceGroupIndex. // View the choice selected private int selectAllIndex. with no associated images cgPrefs.microedition. } public void startApp() 38 .*.EXIT. public class ChoiceGroupTest extends MIDlet implements ItemStateListener. add components. // Main form private Command cmExit. cgPrefs.setCommandListener(this). // A Command to exit the MIDlet private Command cmView. fmMain.*.append(cgPrefs). Choice.addCommand(cmView). cgPrefs. choiceGroupIndex = fmMain. cmView = new Command("View". // Index of choice group on form public ChoiceGroupTest() { display = Display. fmMain. fmMain.append("Detect file type". // Create a multiple choice group cgPrefs = new ChoiceGroup("Preferences". selectAllIndex = cgPrefs. // Append options.Mobile Programming (Private Version) Trịnh Công Duy checkbox exclusive-selection(chỉ được chọn một mục): nhóm này liên quan đến nhóm các radio button Dưới đây là đoạn mã minh họa cho việc sử dụng ChoiceGroup: import javax.append("Select All".addCommand(cmExit). listen for events fmMain = new Form(""). fmMain. null). CommandListener { private Display display.

Mobile Programming (Private Version) { display.setSelectedIndex(i. } } } 39 . notifyDestroyed().println(cgPrefs.setSelectedIndex(selectAllIndex. i++) cgPrefs.size().setCurrent(fmMain). Displayable s) { if (c == cmView) { boolean selected[] = new boolean[cgPrefs. } else if (c == cmExit) { destroyApp(false).getSt ring(i) + (selected[i] ? ": selected" : ": not selected")).isSelected(selectAllIndex)) { // Set all checkboxes to true for (int i = 0.getSelectedFlags(selected). i++) System. for (int i = 0. } } public void itemStateChanged(Item item) { if (item == cgPrefs) { // Is "Select all" option checked ? if (cgPrefs. i < cgPrefs. false). } public void pauseApp() { } public void destroyApp(boolean unconditional) { } Trịnh Công Duy public void commandAction(Command c.size()].size().out. // Remove the check by "Select All" cgPrefs. // Fill array indicating whether each element is checked cgPrefs. true). i < cgPrefs.

lcdui. int x. Chú ý: PNG là loại ảnh duy nhất được hỗ trợ bởi bất kỳ thiết bị MIDP nào Đoạn mã dưới đây mô tả việc sử dụng đối tượng Image và đối tượng ImageItem import javax. boolean processAlpha)  ImageItem(String label.microedition. Bạn có thể dùng Spacer để chỉ rõ khoãng trắng theo chiều dọc và chiều ngang giữa các thành phần. int width. String altText)  Đoạn mã dưới đây mô tả làm thế nào tạo một tấm ảnh từ một tập tin.. hay bên trên của màn hình MIDP đưa ra 2 loại hình ảnh là loại ảnh không biến đổi và ảnh biến đổi. int height. int transform)  Image createImage(InputStream stream)  Image createRGBImage(int[] rgb. null)). // Append to a form fmMain. đơn giản bằng cách chỉ ra chiều dài và chiều rộng cho từng cái. int imageOffset. Image img. Các phương thức dựng cho lớp Image và ImageItem Image createImage(String name) Image createImage(Image source)  Image createImage(byte[] imageDate. int height)  Image createImage(Image image. // Create an image Image img = Image. int width. ví dụ tấm ảnh sẽ được đặt ở trung tâm. int layout.*. được dùng để định vị trí cho các đối tượng khác trên màn hình hiển thị. Chúng ta sẽ làm việc với những tấm ảnh không biến đổi trong bảng sau.*. ImageItem.Mobile Programming (Private Version) } Trịnh Công Duy f) Spacer Spacer là thành phần không nhìn thấy. và dù ảnh có biến đổi hay không. img. Lớp ImageItem mô tả một tấm ảnh sẽ được hiển thị như thế nào. Đặc trưng của loại ảnh này là được đọc từ một tập tin. import javax.midlet.append(new ImageItem(null. Một tấm ảnh biến đổi về cơ bản là một vùng nhớ. Một tấm ảnh không biến đổi thì không thể bị thay đổi kể từ lúc nó được tạo ra.. Image được dùng để tạo ra một đối tượng hình ảnh và giữ thông tin như là chiều cao và chiều rộng.png"). và gắn nó với một đối tượng ImageItem và thêm một bức ảnh vào một Form Form fmMain = new Form("Images").LAYOUT_CENTER. int y. . Vì Spacer là thành phần không nhìn thấy nên nó không có sự kiện g) Image and ImageItem Hai lớp được dùng để hiển thị hình ảnh là: Image và ImageItem. int imageLength)  Image createImage(int width. Điều này tùy thuộc vào việc bạn tạo nội dung của tấm ảnh bằng cách ghi nó lên vùng nhớ.microedition.createImage("/house. hay đặt về phía bên trái. 40  . int height.

null)). display.io.png":"/image_bw. fmMain.LAYOUT_CENTER. Command.addCommand(cmExit).png file").setCurrent(fmMain).setCommandListener(this).append(new ImageItem(null. try { // Read the appropriate image based on color support Image im = Image.Mobile Programming (Private Version) Trịnh Công Duy public class ImageTest extends MIDlet implements CommandListener { private Display display. } } public void startApp() { display. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } 41 . fmMain = new Form(""). fmMain. ImageItem.isColor()) ? "/image_color. fmMain.err.png ").setCurrent(fmMain). // The main form private Command cmExit. 1).createImage((display. im. cmExit = new Command("Exit".IOException e) { System. } catch (java.EXIT. // Reference to Display object private Form fmMain. // Command to exit the MIDlet public ImageTest() { display = Display.println("Unable to locate or read .getDisplay(this).

Quá trình tạo ra một đối tượng CustomItem cũng không khác các đối tượng có sẵn trên nền Java.Mobile Programming (Private Version) Trịnh Công Duy public void commandAction(Command c. . Những thành phần này cũng giống như những Item khác là cũng có thể được đặt vào trong Form và có thể nhận biết và xử lý sự kiện CustomItem được vẽ lên màn hình hiển thị bằng phương thức paint()... Displayable s) { if (c == cmExit) { destroyApp(false)... CustomItem Thành phần CustomItem cho phép bạn tạo ra những thành phần Item của chính bạn. } } } h.. int height) { ... } protected int getPrefContentHeight(int width) { .. Vì thế nó sẽ tùy thuộc vào đoạn mã được bạn hiện thực bên trong phương thức paint().. } protected int getMinContentWidth() { .. int width. } protected int getMinContentHeight() { .. } protected void paint(Graphics g. notifyDestroyed(). Đoạn mã dưới đây minh họa sườn của việc tạo ra một đối tượng CustomItem public class NewItem extends CustomItem { public NewItem(String label) { super(label). } protected int getPrefContentWidth(int height) { 42 .

và Ticker trong các thành phần giao diện cấp cao của ứng dụng M ID P. TextBox. Dạng thứ 3 là là dạng không tường minh.*. // Main list private Command cmExit.*. Textbox. Alert..3. Các List không tường minh đuợc dùng để thể hiện một thực đơn các chọn lựa.. Chúng ta đã thấy loại cho phép nhiều lựa chọn và loại chỉ được phép chọn một khi làm việc với ChoiceGroup..midlet. // Command to exit 43 . và Ticker Trong phần này chúng ta sẽ xem xét các đối tượng ListBox.lcdui. import javax. Alert. } Trịnh Công Duy III.Mobile Programming (Private Version) . public class ImplicitList extends MIDlet implements CommandListener { private Display display. C húng ta hãy cũng xem lại cây phân cấp các thành phần trình bày trên thiết bị hoàn chỉnh hơ n a) List Một List chứa một dãy các lựa chọn được thể hiện một trong ba dạng.microedition. } . Thành phần List. // Reference to Display object private List lsDocument.microedition.. Đoạn mã dưới đây minh họa việc sử dụng một danh sách không tường minh import javax.

List. options.png")}.png"). // Create array of corresponding string objects String options[] = {"Next".IOException e) { System.png "). "New"}. null).io.createImage("/next.png file").addCommand(cmEx it).Mobile Programming (Private Version) public ImplicitList() { display = Display. options. lsDocument. images).getDisplay(this).setCommandListener (this). "Previous".IMPLICIT. Command. try { // Create array of image objects Image images[] = {Image. } } public void startApp() { display. use this line to create the list // lsDocument = new List("Document Option:".println("Unable to locate or read . Trịnh Công Duy // Create list using arrays. // If you have no images.setCurrent(lsDocument).createImage("/new. // Create the Commands cmExit = new Command("Exit".IMPLICIT. List.EXIT. lsDocument. } catch (java.err.createImage("/previous. Image. add commands. listen for events lsDocument = new List("Document Option:". 1). } public void pauseApp() { } public void destroyApp(boolean unconditional) { } 44 . Image.

break.SELECT_COMMAND) { switch (lsDocument. break. break. case 1: System.out.println("Previous selected"). // Reference to 45 .getSelected Index()) { case 0: System. import javax. int constraints).*. URI… Dưới đây là phương thức dựng của một TextBox: TextBox(String title. int maxSize.midlet. String text. EMAIL. case 2: System.lcdui. Thành phần TextBox và TextField có những ràng buộc giống nhau trong việc chỉ định loại nội dung được phép nhâp vào.println("Next selected"). Ví dụ ANY.println("New selected"). public class TextBoxTest extends MIDlet implements CommandListener { private Display display.microedition. } } else if (c == cmExit) { destroyApp(false). } } } b) TextBox TextBox được dùng để cho phép nhập nhiều dòng.Mobile Programming (Private Version) Trịnh Công Duy public void commandAction(Command c.out.*. Dưới đây là đoạn mã minh họa cho việc sử dụng TextBox: import javax.out. Displayable s) { // If an implicit list generated the event if (c == List. notifyDestroyed().microedition.

Trịnh Công Duy tbClip = new TextBox("Textbox Test". } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c. 1).addCommand(cmExit). // Create the Commands.. tbClip.setCommandListener(this). } public void startApp() { display. // Command to exit public TextBoxTest() { display = Display.Mobile Programming (Private Version) Display object private TextBox tbClip. } } } c) Alert và AlertType Một Alert đơn giản là một hộp thoại rất nhỏ. tbClip. "Contents go here. // Main textbox private Command cmExit. Có 2 loại Alert:  Modal: là loại hộp thoại thông báo được trình bày cho đến khi người dùng ấn nút đồng ý  Non-modal: là loại hộp thoại thông báo chỉ được trình bày trong một số giây nhất 46 . Displayable s) { if (c == cmExit) { destroyApp(false). Notice the priorities assigned cmExit = new Command("Exit".EXIT.getDisplay(this).125.". notifyDestroyed().setCurrent(tbClip).ANY). TextField. Command.

public class AlertTest extends MIDlet implements ItemStateListener.*. Choice. import javax.*. báo lỗi.addCommand(cmExit) . 1). add components. null). Command. Dưới đây là đoạn mã minh họa việc sử dụng Alert và AlertType import javax.microedition. with no associated images cgSound. // Main form private Command cmExit. Thành phần AlertType bao gồm 5 loại âm thanh định sẵn là: thông báo. cgSound. listen for events fmMain = new Form(""). null). Ta thấy các phương thức dựng của Alert cho biết là Alert có thể bao gồm 1 tham chiếu đến một đối tượng AlertType. AlertType alertType) Thành phần AlertType sử dụng âm thanh để thông báo cho người dùng biết có một sự kiện xảy ra. xác nhận. null).append("Info". cgSound. // Append options.Mobile Programming (Private Version) Trịnh Công Duy định Các phương thức dựng của Alert: Alert(String title) Alert(String title. String alertText.append("Error". null).getDisplay(this). // Reference to display object private Form fmMain. Ví dụ bạn có thể sử dụng AlertType để mở một đoạn âm thanh nào đó báo hiệu cho người dùng biết khi có lỗi xảy ra. fmMain.EXCLUSIVE). // Create Form. thông báo và cảnh báo. cgSound. Image alertImage.EXIT.append("Alarm".append("Warning". fmMain.setCommandListener 47 . null). cgSound.append("Confirmation". cmExit = new Command("Exit". // Create an exclusive (radio) choice group cgSound = new ChoiceGroup("Choose a sound".append(cgSound). CommandListener { private Display display.midlet.microedition. // Command to exit the MIDlet private ChoiceGroup cgSound.lcdui. // Choice group public AlertTest() { display = Display. fmMain.

} } public void itemStateChanged(Item item) { Alert al = null. AlertType. case 1: al = new Alert("Alert sound". "Confirmation sound". null.INFO). case 3: 48 . break. fmMain. null. Displayable s) { if (c == cmExit) { destroyApp(false).setItemStateListen er(this).Mobile Programming (Private Version) (this).setCurrent(fmMain). switch (cgSound. "Warning sound".INFO). AlertType. "Info sound". break.INFO). } public void startApp() { display. null.getSelectedIndex()) { case 0: al = new Alert("Alert sound". break. notifyDestroyed(). AlertType. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } Trịnh Công Duy public void commandAction(Command c. case 2: al = new Alert("Alert sound".

// Products private Ticker tkSale.INFO). // Command to exit the MIDlet 49 . } } } Trịnh Công Duy d) Ticker Thành phần Ticker đuợc dùng để thể hiện một đoạn chuỗi chạy theo chiều ngang. show main form when done display.setCurrent(al. Từ cây phân cấp các thành phần thể hiện trên thiết bị. ta thấy là thành phần Ticker không là lớp con của lớp Screen mà Ticker là một biến của lớp Screen. null.setTimeout(Alert. import javax. // Ticker private Command cmExit. null. Tốc độ và chiều cuốn được xác định bởi việc cài đặt trên thiết bị nào. public class TickerTest extends MIDlet implements CommandListener { private Display display. "Alarm sound".microedition. fmMain). AlertType. case 4: al = new Alert("Alert sound".Mobile Programming (Private Version) al = new Alert("Alert sound". break. break.midlet. "Error sound".FOREVER). // Reference to Display object private List lsProducts.microedition. AlertType.INFO). } if (al != null) { // Wait for user to acknowledge the alert al. Tham số duy nhất của thành phần Ticker là đoạn văn bản được trình bày.lcdui.*.*. Điều này có nghĩa là một Ticker có thể được gắn vào bất cứ lớp con của lớp Screen bao gồm cả Alert Dưới đây là đoạn mã minh họa việc sử dụng một Ticker import javax. // Display alert. Phương thức dựng của Ticker Ticker(String str).

Command. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c.println("Table selected").IMPLICIT).getSelectedIndex()) { case 0: System.SELECT_COMMAND) { switch (lsProducts. lsProducts = new List("Products".append("Wicker Chair".Mobile Programming (Private Version) public TickerTest() { display = Display. Choice.. } } 50 . case 1: System.setCommandListener (this). lsProducts..setCurrent(lsProducts). null). 1).addCommand(cmExit) . Displayable s) { if (c == List.append("Coffee Table". lsProducts. lsProducts.println("Chair selected"). lsProducts. cmExit = new Command("Exit". lsProducts. null). break.10 for $10").setTicker(tkSale).getDisplay(this).SCREEN.out. break.out. } public void startApp() { display. Trịnh Công Duy tkSale = new Ticker("Sale: Real Imitation Cuban Cigars.

Ứng dụng còn lại là ScratchPad sẽ minh họa làm thế nào để thao tác các sự kiện con trỏ để tạo ra một chương trình vẽ đường thẳng đơn giản a) Hệ thống trục tọa độ Mục tiêu đầu tiên của chúng ta là làm quen với hệ thống trục tọa độ để làm 51 . Bạn sẽ làm tất cả các công việc bằng tay. Ứng dụng đầu tiên là KeyMapping sẽ minh họa làm thế nào để chụp. vẽ ảnh và chuỗi lên thiết bị hiển thị. Tuy nhiên các thành phần cấp cao không cung cấp phương tiện để vẽ trực tiếp lên thiết bị thể hiện. notifyDestroyed().2. Các thành phần giao diện ở mức thấp của ứng dụng MIDP IV. Trong phần này sẽ bao gồm các mục  Hệ thống tọa độ  Tạo đối tượng Canvas  Vẽ lên trên đối tượng Canvas  Xử lý các sự kiện hành động  Xử lý các sự kiện phím nhấn  Xử lý sự kiện hành động của Game  Xử lý sự kiện con trỏ Chúng ta sẽ tạo ra 2 ứng dụng MIDlet để minh họa khả năng của lớp Canvas. thì các hàm API cấp thấp cho phép chúng ta có thể thể hiện các ý tưởng của mình Canvas và Graphics là 2 lớp trái tim của các hàm API cấp thấp. Canvas là một khung vẽ cho phép người phát triển có khả năng vẽ lên thiết bị trình bày cũng như là việc xử lý sự kiện. Còn lớp Graphics cung cấp các công cụ thật sự để vẽ như drawRoundRect() và drawString() IV. Vì thiếu khả năng này nên các ứng dụng được tạo ra sẽ gặp nhiều giới hạn.Mobile Programming (Private Version) else if (c == cmExit) { destroyApp(true).1. Các hàm API ở mức thấp Mặc dù các hàm API cấp cao cung cấp một tập đầy đủ các thành phần để xây dựng giao diện ứng dụng người dùng. Lớp Canvas Lớp Canvas cung cấp một khung vẽ cho phép tạo ra giao diện tùy biến người dùng. nhận ra và xử lý mã phím nhấn và các sự kiện có liên quan đến Game. Một số lượng lớn các phương thức trong lớp này được dùng để xử lý sự kiện. } } } Trịnh Công Duy IV. Ví dụ hầu hết các nhà phát triển game di động dựa trên khả năng vẽ các đường thẳng và các hình dạng như là một phần tích hợp quá trình phát triển Nếu các hàm API cấp cao cho phép chúng ta tạo ra giao diện cho các ứng dụng theo chuẩn.

cmdExit = new Command("Exit".. 1). Giá trị x tăng dần về phía phải.. Nói cách khác... Command.EXIT. setCommandListener(this). Khi vẽ độ dày bút vẽ là một điểm ảnh Các phương thức sau đây sẽ giúp xác định chiều rộng và chiều cao của canvas:  int getWidth(): xác định chiều rộng của canvas  int getHeight (): xác định chiều cao của canvas Chiều rộng và chiều cao của Canvas cũng đại diện cho toàn bộ diện tích khung vẽ có thể trên thiết bị trình bày. protected void paint(Graphics g) { // Draw onto the canvas . . . mà phần mềm trên một thiết bị MIDP sẽ trả về diện tích lớn nhất có thể có đối với một thiết bị cho trước b) Tạo một đối tượng Canvas Bước đầu tiên để làm việc với một lớp Canvas là tạo ra một lớp thừa kế từ lớp Canvas class TestCanvas extends Canvas implements CommandListener { private Command cmdExit. bạn không thể chỉ định kích thước cho canvas..Mobile Programming (Private Version) Trịnh Công Duy việc với thiết bị thể hiện..getDisplay(this). addCommand(cmdExit). Hệ thống tọa độ cho lớp Canvas có tâm tọa độ là điểm trái trên của thiết bị trình bày. 52 . giá trị y tăng dần khi đi xuống phía dưới. display = Display.

. List.. Trịnh Công Duy c) Vẽ trên đối tượng Canvas Phương thức paint của lớp Canvas cho phép bạn vẽ các hình dạng... } } 53 . cmdExit = new Command("Exit"..getDisplay(this). 255. Command..EXIT.. xuất chuỗi.Mobile Programming (Private Version) } } TestCanvas canvas = new TestCanvas(this).. // Fill the entire canvas g. addCommand(cmdExit). } Chúng ta có thể sử dụng một tham chiếu đến một đối tuợng Graphics bên trong thân phương thức paint() để thực hiện công việc vẽ thực sự d) Sự kiện hành động Cũng như các thành phần Form. getWidth(). Đoạn mã sau minh họa việc xóa màn hình thể hiện bằng một màu trắng protected void paint(Graphics g) { // Set background color to white g. và TextBox. } public void commandAction(Command c. setCommandListener(this). Chúng ta có thể xử lý các sự kiện Command trên thành phần Canvas cung cách như các thành phần khác Đoạn mã sau minh họa việc xử lý sự kiện Command trên thành phần Canvas class TestCanvas extends Canvas implements CommandListener { private Command cmdExit. getHeight()). 255). protected void paint(Graphics g) { // Draw onto the canvas . 1). vẽ ảnh. . 0. một Canvas có thể xử lý các sự Command.fillRect(0. display = Display. Displayable d) { if (c == cmdExit) .setColor(255..

Hình dưới đây cho thấy các hành động của trò chơi sẽ được ánh xạ lên một thiết bị di động dựa trên khả năng của các phím chỉ hướng 54 . boolean hasRepeatEvents().Mobile Programming (Private Version) Trịnh Công Duy e) Mã phím Trong trường hợp xử lý các hành động của các phím mềm. và cứ tiếp tục như thế. Những mã này được đảm bảo luôn luôn có trên bất kỳ các thiết bị MIDP nào K E Y _N U M 0 K E Y _N U M 1 K E Y _N U M 2 K E Y _N U M 3 K E Y _N U M 4 K E Y _N U M 5 K E Y _N U M 6 K E Y _N U M 7 K E Y _N U M 8 K E Y _N U M 9 K E Y _S TA R K E Y _P O U N D Năm phương thức để xử lý các mã phím là: void keyPressed(int keyCode). Nếu một thiết bị di động thiếu các phím mũi tên thì các hành động của trò chơi sẽ được ánh xạ vào các nút bấm. void keyRepeated(int keyCode). một Canvas có thể truy cập đến 12 mã phím. nhưng không phải tất cả các thiết bị di động đều có những giá trị này. phím phải được ánh xạ vào phím số 5. f) Các hành động trong xử lý các trò chơi MIDP thường được sử dụng để tạo các trò chơi trên nền Java. void keyReleased(int keyCode). Các hằng số sau đã được định nghĩa để xử lý các sự kiện có liên quan đến trò chơi trong MIDP UP DOWN LEFT RIGHT FIRE GAME_A GAME_B GAME_C GAME_D Nói một cách đơn giản thì các giá trị này được ánh xạ thành các phím mũi tên chỉ hướng của thiết bị. String getKeyName(int keyCode). ví dụ phím trái được ánh xạ vào phím số 2.

RIGHT: goRight(). .Mobile Programming (Private Version) Trịnh Công Duy g) Xác định các hành động của trò chơi Đoạn mã sau đây mô tả một cách xác định các hành động của trò chơi để từ đó gọi các phương thức thích hợp dựa trên các hành động xảy ra protected void keyPressed(int keyCode) { switch (getGameAction(keyCode)) { case Canvas.FIRE: shoot()... 55 . break. case Canvas. break. } } Một lựa chọn nữa là có thể tạo một tham chiếu cho mỗi hành động của trò chơi thông qua quá trình khởi tạo giá trị cho các biến // Initialization keyFire = getKeyCode(FIRE).

} protected void startApp() { display.microedition.getDisplay(this). } protected void pauseApp() { } protected void destroyApp( boolean unconditional ) { } public void exitMIDlet() { destroyApp(true).lcdui. keyLeft = getKeyCode(LEFT).Mobile Programming (Private Version) keyRight = getKeyCode(RIGHT).. import javax.microedition. canvas = new KeyCodeCanvas(this). notifyDestroyed().*.. Trịnh Công Duy // Runtime protected void keyPressed(int keyCode) { if (keyCode == keyFire) shoot(). // Canvas public KeyMapping() { display = Display. else if (keyCode == keyRight) goRight() . } } 56 . public class KeyMapping extends MIDlet { private Display display.setCurrent( canvas ).midlet. . } Đoạn mã dưới đây minh họa một số chức năng của Canvas và cách xử lý phím import javax.*.. // The display private KeyCodeCanvas canvas..

getWidth()/2. 0. 1).drawString(keyText. // Key code text private KeyCodes midlet. getWidth(). Command.midlet = midlet.setColor(255. setCommandListener(this). 0).TOP | Graphics. 0. // Exit midlet private String keyText = null. // Center text g.-------------------------* Constructor *------------------------.------------------------*/ public KeyCodeCanvas(KeyCodes midlet) { this.HCENTER). } } 57 . g.-------------------------* Paint the text representing the key code *------------------------. addCommand(cmExit). // Create exit command and listen for events cmExit = new Command("Exit".EXIT.fillRect(0. 255. getHeight()/2. } /*-----------------------.Mobile Programming (Private Version) Trịnh Công Duy /*-------------------------------------------------* Class KeyCodeCanvas *-------------------------------------------------*/ class KeyCodeCanvas extends Canvas implements CommandListener { private Command cmExit. getHeight()). 255). Graphics.setColor(0. // Set color and draw text if (keyText != null) { // Draw with black pen g.------------------------*/ protected void paint(Graphics g) { // Clear the background (to white) g. /*-----------------------.

} /*-----------------------.Mobile Programming (Private Version) Trịnh Công Duy /*-----------------------.microedition.midlet.------------------------*/ protected void keyPressed(int keyCode) { keyText = getKeyName(keyCode). // Canvas public ScratchPad() { display = Display. import javax. // Display object private ScratchPadCanvas canvas. Phương thức hasPointerMotionEvents() trả về một giá trị có kiểu boolean nhằm chỉ rõ răng thiết bị di động có hỗ trợ khái niệm “nhấp chuột và rê” hay không. public class ScratchPad extends MIDlet { private Display display.getDisplay(this). int y). void pointerReleased(int x. Các phương thức trên có thể tự giải thích chức năng thông qua tên của chính mình. Displayable d) { if (c == cmExit) midlet. 58 .*. int y).lcdui. } } h) Sự kiện con trỏ Trong phần này chúng ta sẽ quản lý sự kiện con trỏ trong một Canvas. Những sự kiện này được thiết kế để làm thuận tiện cho việc tương tác với các thiết bị có dạng con trỏ. repaint(). boolean hasPointerMotionEvents() . void pointerPressed(int x.*. Một số phương thức được cung cấp nhằm hỗ trợ cho việc xử lý sự kiện con trỏ: boolean hasPointerEvents() .-------------------------* Command event handling *------------------------.-------------------------* Key code event handling *------------------------. int y).exitMIDlet().------------------------*/ public void commandAction(Command c. Đoạn chương trình dưới đây minh họa việc sử dụng các sự kiện con trỏ để thực hiện một chương trình vẽ đơn giản import javax.microedition. void pointerDragged(int x.

EXIT. } protected void pauseApp() { } protected void destroyApp( boolean unconditional ) { } public void exitMIDlet() { destroyApp(true). // Create exit command and listen for events cmExit = new Command("Exit". } } /*-------------------------------------------------* Class ScratchPadCanvas * * Pointer event handling *-------------------------------------------------*/ class ScratchPadCanvas extends Canvas implements CommandListener { private Command cmExit. Command. addCommand(cmExit). /*-------------------------------------------------* Constructor *-------------------------------------------------*/ public ScratchPadCanvas(ScratchPad midlet) { this. addCommand(cmClear).Mobile Programming (Private Version) canvas } = new ScratchPadCanvas(this). private ScratchPad midlet.setCurrent( canvas ). private boolean clearDisplay = true. // Where pointer was clicked starty = 0. 1). // Clear display private int startx = 0.midlet = midlet.SCREEN. cmClear = new Command("Clear". // Current location currenty = 0. Trịnh Công Duy protected void startApp() { display. Command. currentx = 0. // Exit midlet private Command cmClear. notifyDestroyed(). 59 . 1).

} /*-------------------------------------------------* Draw line based on start and ending points *-------------------------------------------------*/ protected void paint(Graphics g) { // Clear the background (to white) if (clearDisplay) { g. getHeight()). int y) Trịnh Công Duy 60 . 0. 255). // New starting point is the current position startx = currentx. currenty). } // Draw with black pen g.setColor(255. startx = currentx = starty = currenty = 0.setColor(0. 255. // Draw line g. Displayable d) { if (c == cmExit) midlet. getWidth(). currentx. 0.drawLine(startx. } } /*-------------------------------------------------* Pointer pressed *-------------------------------------------------*/ protected void pointerPressed(int x. return. starty = currenty. } /*-------------------------------------------------* Command event handling *-------------------------------------------------*/ public void commandAction(Command c.Mobile Programming (Private Version) setCommandListener(this ). g. starty. else if (c == cmClear) { clearDisplay = true. repaint(). clearDisplay = false. 0).fillRect(0.exitMIDlet() .

void setGrayScale(int value).Mobile Programming (Private Version) { startx = x. Phương thức đầu tiên cho biết thiết bị có hỗ trợ hiển thị màu hay không. int y) { currentx = x. Đối tượng này đuợc dùng để lấy thông tin của màn hình hiển thị hiện tại.3. currenty = y. Nếu có thì phương thức thứ 2 sẽ được gọi để xác định số màu được hỗ trợ. Các phương thức tiếp theo dưới đây để lấy về màu và thiết lập màu ưa thích của bạn. void setColor(int RGB). int getRedComponent(). } /*-------------------------------------------------* Pointer moved *-------------------------------------------------*/ protected void pointerDragged(int x. Đối tượng Display đơn giản là một bộ quản lý sự hiển thị của thiết bị và điều khiển những gì sẽ được hiển thị ra trên thiết bị. int getGrayScale(). int green. a) Hỗ trợ màu Một ứng dụng MIDP chỉ có một đối tượng Display. int getBlueComponent(). starty = y. int getColor(). repaint(). ví dụ như số màu hỗ trợ và các phương thức để yêu cầu các đối tượng được hiển thị. Lớp Graphics Chúng ta sử dụng đối tượng Graphics để vẽ lên một Canvas. int blue). boolean isColor(). int numColors(). 61 . void setColor(int red. int getGreenComponent(). } } Trịnh Công Duy IV.

. xanh lá cây và xanh dương với 8 bit cho mỗi màu. Hay cách 2 là bạn có thể dùng từng tham số riêng biệt để xác định mỗi màu. g. đi theo ngược chiều kim đồng hồ. void setStrokeStyle(int style).setColor((red << 16) | (green << 8) | blue). cung này được bao bởi một hình chữ nhật có tọa độ điểm trái trên là (10.setStrokeStyle(Graphics.DOTTED). 10. Để hiểu rõ những phần này chúng ta hãy cùng xem1 ví dụ sau: g. Giá trị dương tính theo ngược chiều kim đồng hồ. green. Hai kiểu nét vẽ được định nghĩa trong lớp Graphics là nét chấm. Bạn có thể bắt đầu bằng cách chỉ định chiều bao quanh bên ngoài của một hình hộp chữ nhật tưởng tượng. 10). bạn có thể vẽ nó chỉ có đường bao xung quanh hay yêu cầu nó được tô bên trong. 100.. tiếp theo là 8 bit dành cho màu xanh lá cây. b) Loại nét vẽ Bạn có thể chọn nét khi vẽ đường thẳng. Cách 1: bạn có thể xác định một số nguyên đại diện cho 3 giá trị của màu là đỏ.Mobile Programming (Private Version) Trịnh Công Duy Chú ý bạn có thể xác định màu bằng 2 cách. 100. góc bắt đầu là 0. Đoạn mã trên yêu cầu vẽ một cung. Góc của cung chỉ ra rằng có bao nhiêu độ được vẽ tính từ góc ban đầu. green = 128. Góc bắt đầu xác định vị trí bắt đầu vẽ khung. c) Vẽ cung Khi vẽ một cung. sau cùng là màu xanh dương. với giá trị 0 được xác định tại thời điểm 3 giờ. cung và hình chữ nhật trên thiết bị hiển thị.draw A rc(10. chiều rộng và chiều dài là 100. blue). Dưới đây sẽ hướng dẫn bạn cách thiết lập màu chỉ sử dụng một số nguyên: int red = 0.. Dưới đây là các phương thức dùng để thiết lập loại nét vẽ int getStrokeStyle(). và nét liền g.setColor(red. thì màu đỏ sẽ chiếm 8 bit đầu kể từ bên trái. blue = 255.setStrokeStyle(Graphics. 150). hay g.SOLID). Và bạn có thể xác định màu bằng cách thiết lập giá trị cho 3 tham số: g. Khi sử dụng một giá trị để lưu giữ màu. góc kết thúc là 150 62 . 0.

} protected void startApp() { display. } } 63 . int width. int width. int height. Dưới đây là đoạn mã minh họa viêc sử dụng các hàm trên để vẽ một cung import javax. int y. // The display private ShapesCanvas canvas.getDisplay(this).lcdui. int arcAngle). import javax. int y.midlet. // Canvas public DrawShapes() { display = Display. void fillArc(int x.*.microedition. int startAngle. int startAngle. int height. public class DrawShapes extends MIDlet { private Display display.microedition.Mobile Programming (Private Version) Trịnh Công Duy Một số các phương thức dùng để vẽ cung void drawArc(int x. int arcAngle). } protected void pauseApp() { } protected void destroyApp( boolean unconditional ) { } public void exitMIDlet() { destroyApp(true). canvas = new ShapesCanvas(this).*.setCurrent( canvas ). notifyDestroyed().

public ShapesCanvas(DrawShapes midlet) { this. 150). g. // Change the size of the bounding box // Start at 12 o'clock and rotate 150 degrees 64 . 100. setCommandListener(this). 90. 0.Mobile Programming (Private Version) Trịnh Công Duy /*-------------------------------------------------* Class ShapesCanvas * * Draw arcs *-------------------------------------------------*/ class ShapesCanvas extends Canvas implements CommandListener { private Command cmExit.setColor(0. 1).setColor(255. 0). 0. // Fill the arc // g. getHeight()).midlet = midlet. 10.fillArc(10. 100. 100. 10.fillRect(0. addCommand(cmExit). 0. 255. // Black pen g. 150). } /*-------------------------------------------------* Draw shapes *-------------------------------------------------*/ protected void paint(Graphics g) { // Clear background to white g. // Start at 3 o'clock and rotate 150 degrees g. // Start at 12 o'clock and rotate 150 degrees // g. 100. 0. getWidth(). 150).drawArc(10. 10.EXIT. 100. 255).drawArc(10. // Exit midlet private DrawShapes midlet. 100. Command. // Create exit command and listen for events cmExit = new Command("Exit".

SIZE_MEDIUM). int y. } } d) Vẽ hình chữ nhật Cũng giống như cung thì hình chữ nhật có thể chỉ được vẽ viền bao quanh hoặc tô bên trong. int width. Một số thuộc tính của lớp Font FACE_SYSTEM FACE_MONOSPACE FACE_PROPORTIONAL STYLE_PLAIN STYLE_BOLD STYLE_ITALIC STYLE_UNDERLINED SIZE_SMALL SIZE_MEDIUM SIZE_LARGE Các tham số kiểu dáng có thể được kết hợp thông qua toán tử hay. void fillRoundRect(int x. Font getDefaultFont() .getFont(Font. int arcWidth. Khi vẽ hình chữ nhật có 4 góc là tròn thì bạn phải xác định đường kính theo chiều ngang(arcWidth) và đường kính theo chiều dọc(arcHeight).Mobile Programming (Private Version) // } g. Sau đây là một số các phương thức dựng của lớp Font Font getFont(int face. bạn có thể truy vấn nó để xác định thông tin của các thuộc tính của nó. int style. Font getFont(int fontSpecifier) . int width. void fillRect(int x. Font. 150). Dưới đây là một số phương thức để vẽ hình chữ nhật: void drawRect(int x. int y. 65 . int arcHeight).STYLE_ITALIC. Bên cạnh đó bạn có thể vẽ hình chữ nhật đó có 4 góc là tròn hoặc là vuông. int height). int y. Sau khi bạn có một tham chiếu đến một đối tượng Font. Những tham số này được định nghĩa độ sắc nét của cung theo mỗi chiều. 70. 90. int size) . Giá trị càng lớn thể hiện một cung tăng dần. 30. int y. Trịnh Công Duy public void commandAction(Command c.drawArc(15. int arcHeight). ngược lại là một đường cong hẹp e) Font chữ Phần sau đây cũng quan trọng không kém là cách sử dụng font chữ được hỗ trợ bởi giao diện cấp thấp của ứng dụng MIDP. int width. Displayable d) { if (c == cmExit) midlet. int height. Font. int height.exitMIDlet(). int arcWidth.STYLE_BOLD | Font. int height). int width.FACE_PROPOR TIONAL. 45. void drawRoundRect(int x. Ví dụ Font font = Font.

Graphics. int getSize(). boolean isPlain(). Kích thước của các font chữ được xác định bởi chiều cao của font chữ.TOP | Graphics. int charsWidth(char[] ch. int getBaselinePosition(). int stringWidth(String str). Các điểm neo được định nghĩa như ở dưới đây Chiều ngang LEFT (Bên trái) CENTER (Chính giữa của chiều ngang) RIGHT (Bên phải) Chiều dọcs TOP (Ở trên) BASELINE (Đường thẳng cơ sở) BOTTOM (Ở dưới) Khi sử dụng điểm neo thì bạn cần phải chỉ ra tọa độ x.Mobile Programming (Private Version) Trịnh Công Duy int getFace(). boolean isItalic(). bạn phải chọn một điểm hoành độ và một điểm tung độ. Khi xác định điểm neo để vẽ chuỗi (các điểm neo thường được sử dụng thành từng cặp). int offset. int length). f) Điểm neo Để xác định tọa độ x. thì điểm neo cho phép bạn chỉ ra vị trí muốn đặt tọa độ x. 66 .drawString("developerWorks". int getStyle().LEFT). y của chuỗi ký tự được hiển thị. 0. int charWidth(char ch). int substringWidth(String str. Ví dụ g. int length). 3 theo chiều dọc và 3 theo chiều thẳng đứng. boolean isBold(). boolean isUnderlined(). y trên hình chữ nhật bao quanh chuỗi ký tự Có 6 điểm neo được định nghĩa trước. bề dài tính bằng điểm ảnh của một chuỗi ký tự trong một font xác định. Một số các phương thức sau hỗ trợ khi tương tác với một đối tương font int getHeight(). 0 . y của hình chữ nhật bao quanh. int offset.

67 . bạn đã có thể vẽ chuỗi ký tự ra màn hình thông qua một số các phương thức sau: void drawChar(char character. int x. int anchor) void drawChars(char[] data. int offset. int y. 0. int anchor) Ví dụ: protected void paint(Graphics g) { // Get center of display int xcenter = getWidth() / 2.drawString("developerWorks".HCENTER). int x. int x. int y. Ví dụ tiếp theo chúng ta sẽ minh họa tiếp khi thay đổi điểm neo thì vị trí của chuỗi ký tự cũng thay đổi theo: g. Graphics.Mobile Programming (Private Version) Trịnh Công Duy Hình dưới đây mô tả kết quả của hàm trên Bằng cách thay đổi điểm neo. int x. g) Vẽ các chuỗi ký tự Sau khi tìm hiểu về font và các điểm neo. int length. chúng ta có thể thay đổi vị trí hiển thị của chuỗi ký tự trên thiết bị di động. int anchor) void drawString(String str. int offset. int y. int y. int len. ycenter = getHeight() / 2.TOP | Graphics. int anchor) void drawSubstring(String str. 0 .

Graphics.lcdui.HCENTER). // Form to choose font prefs protected FontCanvas cvFont. xcenter.*.STYLE_ITALICS. cvFont = new FontCanvas(this).*.microedition. Font.drawString("developerWorks". ycenter. // Specify the center of the text (bounding box) using the anchor point g. Font.BASELINE | Graphics. } protected void startApp() { showCanvas().midlet. this). // Canvas to display text (in preferred font) public FontViewer() { display = Display.getDisplay(this).setCurrent(cvFont). } protected void pauseApp() { } protected void destroyApp( boolean unconditional ) { } public void exitMIDlet() { destroyApp(true). notifyDestroyed(). // The display protected PrefsForm fmPrefs. } protected void showCanvas() { display.SIZE_MEDIUM)).Mobile Programming (Private Version) Trịnh Công Duy // Choose a font g. public class FontViewer extends MIDlet { protected Display display.microedition. import javax. fmPrefs = new PrefsForm("Preferences".setFont(Font.getFont(Font. } Tiếp theo là ví dụ minh họa việc sử dung font và xuất chuỗi ra thiểt bị hiển thị import javax.FACE_SYSTEM. } } 68 .

// White pen g. size)). // Draw text at center of display 69 . // Reference to the main midlet public FontCanvas(FontViewer midlet) { this. setCommandListener(t his). size.EXIT.Mobile Programming (Private Version) Trịnh Công Duy /*-------------------------------------------------* FontCanvas. g. // Call the preferences form private FontViewer midlet.SCREEN.setColor(0. 255. 0. Command. // le siz e private String text = "developerWorks". addCommand(cmPrefs). cmPrefs = new Command("Prefs". // Exit midlet private Command cmPrefs. 255).midlet = midlet. 1). // Text to display in preferred font private Command cmExit. getHeight()).setFont(Font. 0.setColor(255. // Font face style // sty .microedition. style.getFont(face. } /*-------------------------------------------------* Draw text *-------------------------------------------------*/ protected void paint(Graphics g) { // Clear the display g. addCommand(cmExit). 0). Command. getWidth().lcdui. // Create commands and listen for events cmExit = new Command("Exit".fillRect(0. // Black pen // Use the user selected font preferences g. class FontCanvas extends Canvas implements CommandListener { private int face. 2).java * * Draw text using specified Font *-------------------------------------------------*/ import javax.*.

BASELINE | Graphics. } protected void setFace(int face) { this. } protected void setSize(int size) { this.HCENTER). } public int getSize() { return size.exitMIDle t(). } public int getStyle() { return style.text = text. } public int getFace() { return face. } public void commandAction(Command c.face = face. getHeight() / 2.drawString(text.setCurrent(midlet.display.size = size.style = style. } protected void setStyle(int style) Trịnh Công Duy { this.fmPrefs). } public void setText(String text) { this. getWidth() / 2. Graphics. Displayable d) { if (c == cmExit) midlet. } 70 . else if (c == cmPrefs) midlet.Mobile Programming (Private Version) g.

Mobile Programming (Private Version) } Trịnh Công Duy /*-------------------------------------------------* PrefsForm.SCREEN. // Save reference to MIDlet so we can // access the display manager class this. 2). // Commands cmSave = new Command("Save". 71 . cgFace.append("System".append("Monospace". FontViewer midlet) { // Call the form constructor super(title). // Font face cgStyle.microedition.EXCLUSIVE).lcdui. private String text = null. // Values for font attributes style = 0. // Save new font prefs protected ChoiceGroup cgFace. // Value for text string from the textfield public PrefsForm(String title.BACK. // style cgSize. // Text string to display on canvas // (with the requested font prefs) private int face = 0. cmBack = new Command("Back".midlet = midlet. // size protected TextField tfText. Command. null). ItemStateListener { private FontViewer midlet. Choice. cgFace.java * * Form that allows user to select font preferences *-------------------------------------------------*/ import javax. 1). // that are obtained from the choicegroups size = 0. Command. public class PrefsForm extends Form implements CommandListener. // Main midlet private Command cmBack. // Exclusive choice group for font face cgFace = new ChoiceGroup("Face Options". // Back to canvas cmSave.*.

cgStyle. append(cgSize). cgSize.EXCLUSIVE). cgStyle. Choice.append("Proportiona l".MULTIPLE). cgSize. TextField.append("Medium". cgFace. // Exclusive choice group for font size cgSize = new ChoiceGroup("Size Options". cgSize. null). null).append("Bold".append("Small". null). Displayable s) { if (c == cmSave) { // Set the values in canvas class to those selected here 72 . append(tfText). addCommand(cmSave) . null). cgStyle. null). append(cgStyle).append("Large". // Textfield for text to display on canvas tfText = new TextField("". 20.ANY). setCommandListener(this).append("Plain".Mobile Programming (Private Version) null).append("Underline d". Trịnh Công Duy // Multiple choice group for font style cgStyle = new ChoiceGroup("Style Options". null). "developerWorks". Choice. } /*-------------------------------------------------* Command event handling *-------------------------------------------------*/ public void commandAction(Command c. // Create form contents and listen for events append(cgFace). null). null). cgStyle. addCommand(cmBack). setItemStateListener(this).append("Italic".

break. cgStyle.setStyle(style).cvFont. // STYLE_PLAIN has a value of 0 // No need to check for b[0] // If bold selected if (b[1]) style = Font. } public void itemStateChanged(Item item) { if (item == cgFace) { switch (cgFace. break. case 2: face = Font.FACE_PROPORTIONAL.getSelectedFlags(b). case 1: face = Font. style = 0.setSize(size). midlet. // If italic selected if Trịnh Công Duy 73 . } // No specific check needed for "Back" command // Always return to the canvas at this point midlet.cvFont. break.setFace(face).FACE_MONOSPACE.FACE_SYSTEM.setText(tfText.getString() : "developerWorks").ge tString() != null ? tfText.cvFont. // Make sure we aren't passing a null value midlet.showCanvas(). midlet. } } else if (item == cgStyle) { boolean[] b = new boolean[4].cvFont.getSelectedIndex()) { case 0: face = Font.Mobile Programming (Private Version) midlet.STYLE_BOLD.

STYLE_ITALIC. case 2: size = Font.SIZE_MEDIUM.getString(). y cũng như điểm neo. tuy nhiên không giống với việc xuất chuỗi thì một bức ảnh có một điểm trung tâm. case 1: size = Font. } } else if (item == tfText) { text = tfText.Mobile Programming (Private Version) (b[2]) style |= Font. // If underlined selected if (b[3]) style |= Font. break. int y. break. Đối với cả 2 thì chúng ta đều phải bắt đầu bằng việc thiết lập tọa độ x. int x. break.getSelectedIndex() ) { case 0: size = Font.SIZE_LARGE.SIZE_SMALL. int anchor) Chúng ta cũng áp dụng từng bước khi vẽ ảnh cũng giống như khi xuất chuỗi ra màn hình. } } } Trịnh Công Duy h) Vẽ ảnh Lớp Graphics cung cấp 1 phương thức dùng để vẽ ảnh: drawImage(Image img. } else if (item == cgSize) { switch (cgSize. Ví thế VCENTER được thay thế cho gia trị BASELINE khi làm việc với ảnh Chiều ngang LEFT (Bên trái) HCENTER (Điểm chính giữa theo chiều ngang) RIGHT (Bên phải) Chiều dọc TOP (Điểm trên) VCENTER (Điểm chính giữa theo chiều dọc) BOTTOM (Bên dưới 74 .STYLE_UNDERLINED. Danh sách các điểm neo cho việc hiển thị ảnh cũng không khác mấy so với việc xuất chuỗi.

setCurrent( canvas ).*. canvas = new ImageCanvas(this). chúng ta đã tạo ra các ứng dụng MIDP cho việc trình bày một tấm ảnh đọc từ một nguồn tài nguyên là một tập tin. Loại ảnh này không cho phép thay đổi.Mobile Programming (Private Version) Trịnh Công Duy Trong các phần trước. Loại ảnh này còn được biết với một cái tên là “ảnh có thể biến thay đỏi được” import javax. chúng ta sẽ cấp phát bộ nhớ cho tấm ảnh.getDisplay(this). import javax. 75 . } protected void startApp() { display.midlet. } protected void pauseApp() { } protected void destroyApp( boolean unconditional ) { } public void exitMIDlet() { destroyApp(true). notifyDestroyed(). // The display private ImageCanvas canvas. // Canvas public DrawImage() { display = Display. public class DrawImage extends MIDlet { private Display display. và chúng ta sẽ tự vẽ nội dung tấm ảnh.*. Đối với ví dụ sau đây. và vì vậy còn được biết với tên là “ảnh không thể thay đổi”.lcdui. chúng ta sẽ tạo ra một tấm ảnh từ những đống tạp nham.microedition. để lấy tham chiếu đến một đối tượng Graphics.microedition.

midlet = midlet. // Specify a font face.stringWidth(message) / 76 . graphics. 20). Draw text in white graphics.setColor(255.setColor(0. graphics.FACE_SYSTEM. try { // Create mutable image im = Image. graphics. 255).createImage(100. // Get graphics object to draw onto the image Graphics graphics = im.EXIT.STYLE_PLAIN.0.drawString(message.getWidth()-1.SIZE_MEDIUM). setCommandListener(this).getGraphics(). 0. Command. private String message = "developerWorks".Mobile Programming (Private Version) } } Trịnh Công Duy /*-------------------------------------------------* Class ImageCanvas * * Draw mutable image *-------------------------------------------------*/ class ImageCanvas extends Canvas implements CommandListener { private Command cmExit.setFont(font). Font.getFont(Font. // Exit midlet private DrawImage midlet. Font. im. 20). 1). addCommand(cmExit). public ImageCanvas(DrawImage midlet) { this. // Center text horizontally in the image. style and size Font font = Font. im. with rounded corners graphics.getHeight()-1.(font. 20. // Draw a filled (blue) rectangle. private Image im = null.getWidth() / 2) . 255). // Create exit command and listen for events cmExit = new Command("Exit". (im. 255.fillRoundRect(0.

getHeight() / 2) (font.VCENTER | Graphics. // Center the image on the display if (im != null) g. int getClipX(). int y. 77 . Displayable d) { if (c == cmExit) midlet. int getClipY(). int y. } public void commandAction(Command c. void clipRect(int x. 255.exitMIDlet(). Một vùng hiển thị được cắt xén được định nghĩa là khu vực hiển thị của thiết bị di động.setColor(255.println("Error during image creation"). vùng này sẽ được cập nhật trong suốt thao tác vẽ lại. int width.drawImage(im. 255). getHeight() / 2. 0.err.LEFT).Mobile Programming (Private Version) 2). int height). (im.HCENTER). getHeight()). } } i) Một số các phương thức khác của lớp Graphics clip() và translate() là 2 phương thức của lớp Graphics. } } /*-------------------------------------------------* Draw mutable image *-------------------------------------------------*/ protected void paint(Graphics g) { // Clear the display g.TOP | Graphics. Dưới đây là một số phương thức hỗ trợ cho việc xén một vùng hiển thị void setClip(int x. int height) . Trịnh Công Duy } catch (Exception e) { System.getHeight() / 2). Graphics. Graphics. int width. g. getWidth().fillRect(0. getWidth() / 2.

Các hàm API dùng để lập trình Game Các hàm API dành để lập trình Game được giới thiệu trong bản MIDP 2. Chúng ta có thể tịnh tiến hệ trục tọa độ đến một điểm x. IV. int y) . Sprite là một lớp con của lớp Layer. translate() là một phương thức được sử dụng có liên quan đến hệ thống trục tọa độ. GameCanvas chứa một vùng nhớ tách rời với vùng nhớ màn hình cho mỗi thể hiện và cung cấp các phương tiện tích hợp để xác định tình trạng các phím trò chơi.lc dui.Mobile Programming (Private Version) Trịnh Công Duy int getClipWidth(). các hàm API dành cho việc lập trình trò chơi lại có thê lớp LayoutManager.m icroed ition. Dưới đây là lớp GameCanvas: public abstract class GameCanvas extends Canvas Layer là một lớp trừu tượng được sử dụng để thể hiện một đối tượng trực quan trong một trò chơi. y khác. Để thực hiện khả năng chuyển động. những hàm này là phương tiện để phát triển game với nhiều phần đồ họa. public class TiledLayer extends Layer Để đơn giản xử lý cho việc vẽ nhiều lớp trong một trò chơi. javax. như là nền của một trò chơi.0.ga m e Lớp GameCanvas gần như giống lớp Canvas. nhưng trong trường hợp này chỉ cho việc tạo games. int getClipHeight(). Ngoài ra Sprite còn có thể bao gồm một dãy các khung ảnh. Một số phương thức hỗ trợ cho việc tịnh tiến hệ trục tọa độ void translate(int x. int getTranslateX(). GameCanvas cung cấp nền tảng để tạo giao diện người dùng. int getTranslateY(). Dưới đây là mô tả cho 2 lớp Layer và Sprite public abstract class Layer extends Object public class Sprite extends Layer TiledLayer là một lớp tương tự như một bảng tính. với mỗi ô đại diện cho một tấm ảnh. Một TiledLayer đơn giản được dùng để thể hiện các phần tử trực quan có kích thước lớn. lớp này được cung cấp để thể hiện cho một bức ảnh. các khung ảnh được thể hiện theo một thứ tự để tạo hiệu ứng di chuyển ảnh. Các hàm API dành cho Game là một phần của gói. Các phép biến đổi như là phép quay và phép lật ảnh có thể được áp dụng đối với một đối tượng Sprite. Lớp LayoutManager này chứa một danh sách có thứ tự các đối tượng Layers và xác định khu vực nào cần được vẽ lại và thể hiện theo đúng trật tự. Lớp LayoutManager được thể hiện như dưới đây: public class LayerManager extends Object 78 .4.

thiết bị sẽ tạo ra một thực đơn để chứa các hành động. hay Canvas  Tạo một bộ lắng nghe Khi có một sự kiện xảy ra. // Create Form and give it a title // Create Command object. with label. public void commandAction(Command c..EXIT. 1). List. Thông tin này bao gồm loại hành động thực thi.. List và C anvas.1. Xử lý sự kiện V. Trong J2ME... Command. TextBox. fmMain. // Command to exit the MIDlet . bộ lắng nghe sẽ phát sinh một lời gọi đến phương thức commandAction(). // Add Command to Form fmMain. Đối tượng Command Khi một đối tượng xảy ra trên thiết bị di động. một đối tượng Command giữ thông tin về sự kiện đó. type and priority cmExit = new Command("Exit".addCommand(cmExit). // Form private Command cmExit. fmMain = new Form("Core J2ME").setCommandListener(this).Mobile Programming (Private Version) Trịnh Công Duy V. các hành động nói chung được thể hiện dưới dạng các nút trên thiết bị. // Listen for Form events .. Nếu có quá nhiều hành động được hiển thị trên thiết bị. TextB ox. Chỉ có các thành phần MIDP sau đây mới có thể chứa các đối tượng C om m a nd là: F orm. Các bước cơ bản để xử lý các sự kiện với một đối tượng Command:  Tạo một đối tượng Command  Đặt đối tượng Command lên trên một đối tượng Form. Displayable s) { if (c == cmExit) { 79 . nhãn của mệnh lệnh và độ ưu tiên của chính nó. . Dưới đây là đoạn mã minh họa việc tạo ra sự kiện Command và xử lý sự kiện: private Form fmMain. Trong thân phương thức này bạn có thể xác định đối tượng nào phát sinh ra sự kiện và tạo ra các xử lý tương ứng..

fmMain. Ví dụ đối tượng DateField cho phép người sử dụng chọn lựa ngày và giờ trên màn hình.2. fmMain = new Form("Core J2ME").. thì khi có một thay đổi xảy ra đối với bất kỳ thành phần Item nào thì phương thức itemStateChanged() sẽ được gọi.. } } Trịnh Công Duy V.microedition. Item import javax.append(dfToday).midlet. Nhiều đối tượng trong MIDP đã được định nghĩa trước cho việc xử lý các sự kiện cụ thể nào đó. // Listen for Form events .microedition.Mobile Programming (Private Version) destroyApp(true). // Form private DateField dfToday. public class EventProcessing extends MIDlet implements 80 . // Create Form object dfToday = new DateField("Today:".. số và các ký tự đặc biệt Tương tự như việc xử lý sự kiện bằng đối tượng Command. // DateField item . Ví dụ Dưới đây là đoạn mã minh họa việc tạo ra và xử lý các sự kiện Command.*.. notifyDestroyed(). DateField. Dưới đây là đoạn mã minh họa việc tạo ra sự kiện Command và xử lý sự kiện: private Form fmMain..DATE).lcdui.. đối tượng TextField cho phép người dùng nhập vào một chuỗi các ký tự..3. public void itemStateChanged(Item item) { // If the datefield initiated this event if (item == dfToday) . } V. // Create DateField .*.setItemStateListener(this). Đối tượng Item Ngoài việc xử lý sự kiện bằng đối tượng Command ta có thể xử lý sự kiện bằng đối tượng Item. // Add DateField to Form fmMain. Và chúng ta sẽ thực hiện các xử lý bên trong phương thức này. import javax..

fmMain. "". Displayable s) { System.getDisplay(this). if (c == cmExit) { 81 .setItemStateListener(this). fmMain. 10. CommandListener { private Display display.out. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c. 1).addCommand(cmExit).EXIT. // Main Form private Command cmExit.append(tfText). // Capture Item events (dfDate) } // Called by application manager to start the MIDlet. Command. TextField. public void startApp() { display. // Capture Command events (cmExit) fmMain.setCurrent(fmMain). // Command to exit the MIDlet private TextField tfText. add Command and DateField // listen for events from Command and DateField fmMain = new Form("Event Processing Example"). // TextField component (item) public EventProcessing() { display = Display.setCommandListener(this). cmExit = new Command("Exit". fmMain.Mobile Programming (Private Version) ItemStateListener. // Reference to Display object for this MIDlet private Form fmMain. // Create the Form. Trịnh Công Duy // Create the date and populate with current date tfText = new TextField("First Name:".println("Inside commandAction()").ANY).

Nếu bạn mã hoá dữ liệu của bạn dưới dạng nhị phân (binary). các thao tác trên Record phải thông qua RecordStore chứa nó. Mỗi bản ghi (sau này gọi là Record) có thể chứa bất kỳ loại dữ liệu nào.println("Inside itemStateChanged()"). mỗi dòng lại có một số định danh duy nhất: A record store database Record Data ID 1 Array of bytes 2 Array of bytes 3 Array of bytes … Một tập các bản ghi (sau này gọi là RecordStore) là tập hợp các Record được sắp xếp có thứ tự. Record đầu tiên được tạo ra sẽ được gán Record ID là 1 và sẽ tăng thêm 1 cho các Record tiếp theo. Tất nhiên kích thước dữ liệu của bạn không được vuợt quá giới hạn qui định của thiết bị di động. các thao tác xóa Record trong RecordStore sẽ không gây nên việc tính toán lại các Record ID của các 82 . } } Trịnh Công Duy VI. Record Management System MIDP không sử dụng hệ thống file để lưu trữ dữ liệu.1. Cần chú rằng Record ID không phải là chỉ mục (index). notifyDestroyed().Mobile Programming (Private Version) destroyApp(false). chuỗi ký tự hay có thể là một ảnh và kết quả là một Record là một chuỗi (mảng) các byte. Record được gán một số định danh kiểu số nguyên gọi là Record ID. RMS lưu dữ liệu gần như một cơ sở dữ liệu. bao gồm nhiều dòng. } } public void itemStateChanged(Item item) { System. Khi tạo ra một Record trong RecordStore. Mỗi Record không thể đứng độc lập mà nó phải thuộc vào một RecordStore nào đó. Thay vào đó MIDP lưu toàn bộ thông tin vào non-volatile memory bằng hệ thống lưu trữ gọi là Record Management System (RMS).out. VI. chúng có thể là kiểu số nguyên. bạn có thể lưu trữ dữ liệu bằng Record sau đó đọc dữ liệu từ Record và khôi phục lại dữ liệu ban đầu. Persistent Storage Through the Record Store RMS là hệ thống được tổ chức và quản lý dưới dạng các record (bản ghi).

Cần chú ý khái niệm MIDlet suite là tập các MIDlet có chung không gian tên (name space). 83 . Tên được dung để phân biệt các RecordStore trong bộ các MIDlet (MIDlet suite).jar khi triển khai. Tên của RecordStore có thể dài đến 32 ký tự Unicode và là duy nhất trong một MIDlet suite. Data là một dãy các byte đại diện cho dữ liệu cần lưu. Nếu ứng dụng của bạn chỉ có một MIDlet thì các RecordStore được sử dụng cũng phân biệt lẫn nhau bằng các tên. có thể chia sẽ cùng tài nguyên (như RecordStore). các biến tĩnh (static variable) trong các lớp và các MIDlet này sẽ được đóng gói trong cùng một file .Mobile Programming (Private Version) Trịnh Công Duy Record hiện có cũng như không làm thay đổi Record ID của các Record được tạo mới. ví dụ: khi ta xóa record id 3 khi thêm một record mới sẽ có id là 4.

Nó sẽ giống như tốc độ đọc ổ cứng và tốc độ đọc từ RAM của máy tính bạn. bạn không phải lo lắng về vấn đề này vì RMS sẽ dành riêng một Thread để thực hiện các thao tác trên RecordStore. để biết giá trị khởi đầu bạn cần gọi hàm getVersion() sau khi tạo một Record store. là số miliseconds kể từ ngày 1 tháng 1 năm 1970. Date/time Stamp là số long integer. Trong MIDLET Suite One. ngoài ra còn có thể dùng cơ chế event handler (Listener) để phát hiện mỗi khi Record store bị thay đổi. Đặc tả MIDP yêu cầu rằng các nhà sản xuất thiết bị di động phải dành ra vùng nhớ có kích thước ít nhất 8K cho việc lưu trữ dữ liệu trong RMS. Các vấn đề liên quan đến RMS a) Hạn chế về khả năng lưu trữ của thiết bị di động Dung lượng vùng nhớ (non-volatile memory) dành riêng cho việc lưu trữ dữ liệu trong RMS thay đổi tùy theo thiết bị di động. c) Cơ chế luồng an toàn Nếu RecordStore của bạn chỉ được sử dụng bởi một MIDlet. tuy nhiên Record store trong các MIDlet Suite khác nhau có thể dùng chung một tên. thay thế hay xóa một record. MIDlet #1 và MIDlet #2 cùng có thể truy xuất 4 Record store. RMS cung cấp các API để xác định kích thước của mỗi Record. Tuy nhiên nếu có nhiều MIDlet và Thread cùng chia sẻ một RecordStore thì phải chú ý đến kỹ thuật lập trình Thread để đảm bảo không có sự xung đột dữ liệu. bạn có thể biết được giá trị này thông qua hàm getLastModified(). VI. Do đó trong quá trình phát triển các ứng dụng J2ME bạn phải cân nhắc trong việc sử dụng vùng nhớ này. VI. Đặc tả không nêu giới hạn trên cho mỗi Record. Các hàm API trong RMS 84 . Version number là một số integer. MIDLET Suite One không thể truy xuất Record store trong Suite Two.3. Trong MIDlet Suite One tên của các Record store là duy nhấy. các giá trị này thay đổi khi thực hiện thêm. b) Tốc độ truy xuất dữ liệu Các thao tác trên vùng nhớ này (non-volatile memory) tất nhiên sẽ chậm hơn nhiều khi bạn truy xuất dữ liệu trên bộ nhớ RAM (volatile memory). Vì vậy trong kỹ thuật lập trình bạn phải thường xuyên cache dữ liệu và các thao tác liên quan đến RMS chỉ thực hiện tập trung một lần (lúc khởi động hay đóng ứng dụng). Record Store còn có 2 thuộc tính là Version Number và Date/time Stamp. đường nét đứt là Record store do MIDlet khác tạo ra.Mobile Programming (Private Version) Trịnh Công Duy Đường liền thể hiện việc truy xuất Record store do MIDlet đó tạo ra.2. tổng dung lượng của RecordStore và kích thước còn lại của vùng nhớ này.

RecordComparator dùng để duyệt recordstore comparator. boolean Record store nếu nó chưa createIfNecessary) tồn tại.rms.microedition. int getRecordSize(int recordId) Kích thước record. chứa record. int getVersion() Version của RecordStore. int getNextRecordID() Lấy record id của record int getNumRecords() mớilượng các record. recordStoreName) static String[] listRecordStores() Danh sách các RecordStore trong int addRecord(byte[] data. int numBytes) record trong RecordStore. void deleteRecord(int recordId) Xóa một record trong byte[] getRecord(int recordId) RecordStore. static void deleteRecordStore(String Xóa RecordStore. int getSize() Kích thước của int getSizeAvailable() RecordStore. boolean keepUpdated) void addRecordListener (RecordListener listener) void removeRecordListener (RecordListener listener) Add a listener to detect record store Remove listener. byte[] Đặt hoặc thay thế một newData. void setRecord(int recordId. Lấy dãy byte int getRecord(int recordId. Ví dụ 1: đọc và ghi đối tượng string (ReadWrite. void closeRecordStore() Đóng RecordStore.Mobile Programming (Private Version) RecordStore không có hàm khởi tạo. long getLastModified() Thời gian thay đồi gần nhất. Chúng ta hãy cùng xem qua 2 ví dụ đơn giản của việc đọc ghi record trong RecordStore. Trịnh Công Duy LỚP: RecordStore Class: javax. int offset) dãy byte. có openRecordStore(String tham số tạo recordStoreName. byte[] Lấy nội dung của record vào buffer. Số byte trống cho RecordEnumeration enumerateRecords( RecordStore.RecordStore Mô tả Phương thức static RecordStore Mở một Recordstore.java) /*-------------------------------------------------85 . Xây dựng enumeration RecordFilter filter. int numBytes) RecordStore. Số . MIDletmột record vào Thêm suite. int offset. String getName() Tên của RecordStore. int offset.

io. } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore. public ReadWrite() { openRecStore(). go ahead and shutdown destroyApp(false). all output is to the console *-------------------------------------------------*/ import java. // Create the record store // Write a few records and read them back writeRecord("J2ME and MIDP"). notifyDestroyed().*. // Close record store deleteRecStore(). import javax.*. import javax. readRecords().openRecordStore(REC_S TORE. } catch (Exception e) 86 . true ).Mobile Programming (Private Version) * ReadWrite. closeRecStore(). writeRecord("Wireless Technology").rms. static final String REC_STORE = "db_1".microedition.*. * * No GUI interface.midlet.java * * Read and write to the record store. // Remove the record store Trịnh Công Duy } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user interface. public class ReadWrite extends MIDlet { private RecordStore rs = null.microedition.

listRecordStores() != null) { try { RecordStore. } catch (Exception e) { db(e.addRecord(rec. i++) { 87 . i <= rs. try { rs.toString()).closeRecordStore().Mobile Programming (Private Version) { db(e. 0. } Trịnh Công Duy } public void closeRecStore() { try { rs.toString()). } } public void readRecords() { try { byte[] recData = new byte[50].getNumRecords(). rec. } } public void deleteRecStore() { if (RecordStore. } catch (Exception e) { db(e. int len. } catch (Exception e) { db(e.length). for (int i = 1.getBytes().deleteRecordStore(REC_STORE).toString()).toString()). } } } public void writeRecord(String str) { byte[] rec = str.

toString()).println("-----------------------------").out. System.Mobile Programming (Private Version) Trịnh Công Duy len = rs. 0 ).err. } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner. 0.println("Rec ord #" + i + ": " + new String(recData. len)). System. recData.getRecord( i. } } catch (Exception e) { db(e.println("Msg: " + str). *-------------------------------------------------*/ private void db(String str) { System.out. } } Đây là output của ví dụ 1: Hàm để mở một recordstore public void openRecStore() { try { 88 .

chúng ta cũng cần đọc một dãy byte: byte[] recData = new byte[50].length) recData = new byte[rs.. boolean. i++) { if (rs. int. rec.java) /*-------------------------------------------------* ReadWriteStreams.length).v… Trong ví dụ 2 này.. recData. i <= rs. 0.addRecord(rec.getRecordSize(i) > recData. Việc sử dụng stream giúp chúng ta linh động và nâng cao hiệu quả của việc đọc và ghi dữ liệu vào RecordStore. 0.out.getRecordSize(i)]. 0 ). } } Với tham số true. } catch (Exception e) { db(e. trước khi lưu vào RecordStore..Mobile Programming (Private Version) Trịnh Công Duy // Create record store if it does not exist rs = RecordStore.toString()).java * * Use streams to read and write Java data types * to the record store. len)).getRecord(i. . Tuy nhiên. true ). thực tế là ta cần lưu những giá trị khác: String. } Nếu chỉ cần đọc ghi những đoạn text vào record. System. Trong hàm ReadRecord.println("Record #" + i + ": " + new String(recData. len = rs.getNumRecords(). chương trình sẽ sử dụng stream để đọc và ghi record. hàm sẽ tạo một RecordStore nếu nó chưa tồn tại.getRecord( i. rs. do đó hàm ReadRecord có thể sửa lại như sau: for (int i = 1. v. .println("------------------------------"). 0).out. Ví dụ 2: đọc và ghi sử dụng stream (ReadWriteStreams. System. thì ví dụ trên là quá đủ. Cần lưu ý là trong ví dụ trên do biết trước kích thước của string nên khai báo dãy byte vừa đủ.getBytes().openRecordStore(REC_S TORE. len = rs. Trong hàm WriteRecord. cần phải chuyển đổi kiểu string thành dãy byte: byte[] rec = str. 89 . recData.. trong thực tế ta nên kiểm tra kích thước của record để khai báo dãy byte cần thiết để tránh phát sinh lỗi.

*.microedition.microedition. } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore. import javax. // Create the record store writeTestData(). import javax.openRecordStore(REC_S TORE.*.rms. // Read back the records closeRecStore().io. // Record store static final String REC_STORE = "db_1". } catch (Exception e) { db(e. all output is to the console *-------------------------------------------------*/ import java. go ahead and shutdown destroyApp(false). true ). // Write a series of records readStream(). notifyDestroyed(). // Remove the record store } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user interface. public class ReadWriteStreams extends MIDlet { private RecordStore rs = null.*. } } public void closeRecStore() { try { 90 .midlet. // Close record store deleteRecStore().Mobile Programming (Private Version) Trịnh Công Duy * * No GUI interface. // Name of record store public ReadWriteStreams() { openRecStore().toString()).

// Write Java data types into the above byte array DataOutputStream strmDataType = new DataOutputStream(strmBytes). 2}. } } } /*-------------------------------------------------* Create three arrays to write to record store *-------------------------------------------------*/ public void writeTestData() { String[] strings = {"Text 1". true}. 91 .toString()).listRecordStores() != null) { try { RecordStore.Mobile Programming (Private Version) rs. boolean[] bData. } Trịnh Công Duy } public void deleteRecStore() { if (RecordStore. "Text 2"}. integers). } catch (Exception e) { db(e.toString()). } catch (Exception e) { db(e. *-------------------------------------------------*/ public void writeStream(String[] sData.int[] iData) { try { // Write data into an internal byte array ByteArrayOutputStream strmBytes = new ByteArrayOutputStream().deleteRecordStore(REC_STORE). booleans. writeStream(strings. } /*-------------------------------------------------* Write to record store using streams. byte[] record. boolean[] booleans = {false.closeRecordStore(). int[] integers = {1 .

length. 0. } strmBytes. rs.writeInt(iData[i] ).Mobile Programming (Private Version) Trịnh Công Duy for (int i = 0.length). for (int i = 1.toByteArray(). // Read from the specified byte array ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData). // Clear any buffered data strmDataType.close( ).reset(). i++) { // Write Java data types strmDataType. strmDataType. i++) { 92 .writeBoolean(bDat a[i]). strmDataType.getNumRecords().addRecord(record.close(). i <= rs. i < sData. } catch (Exception e) { db(e. // Get stream data into byte array and write record record = strmBytes.flush().writeUTF(sData[i] ). test and reallocate if necessary byte[] recData = new byte[50]. strmDataType. // Toss any data in the internal array so writes // starts at beginning (of the internal array) strmBytes. record.toString()). // Read Java data types from the above byte array DataInputStream strmDataType = new DataInputStream(strmBytes). } } /*-------------------------------------------------* Read from the record store using streams *-------------------------------------------------*/ public void readStream() { try { // Careful: Make sure this is big enough! // Better yet.

reset().out.out. System. } catch (Exception e) { db(e. *-------------------------------------------------*/ private void db(String str) { System. } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner.err.println("-------------------"). // Read back the data types System.Mobile Programming (Private Version) Trịnh Công Duy // Get data into the byte array rs. } } 93 .out.readInt()). recData. // Reset so read starts at beginning of array strmBytes. } strmBytes.out.println("UTF: " + strmDataType.println("Boolean: " + strmDataType.println("Int: " + strmDataType. System. System. System.readBoolean()).close ().cl ose().println("Msg: " + str).println("Record #" + i).toString()).getRecord(i.out. strmDataType. 0).readUTF()).

Mobile Programming (Private Version) Output của ví dụ 2: Trịnh Công Duy Dữ liệu ghi vào record trong ví dụ 2 gồm: String[] strings = {"Text 1". i++) { // Write Java data types strmDataType. "Text 2"}.toByteArray().addRecord(record.length. 2}.writeInt(iData [i]). strmDataType.writeUTF(sData [i]). boolean[] booleans = {false.flush (). Hàm WriteStream sẽ ghi dữ liệu vào RecordStore // Write data into an internal byte array ByteArrayOutputStream strmBytes = new ByteArrayOutputStream(). // Clear any buffered data strmDataType. 94 . int[] integers = {1 . 0. i < sData. strmDataType. // Write Java data types into the above byte array DataOutputStream strmDataType = new DataOutputStream(strmBytes). // Get stream data into byte array and write record record = strmBytes. true}. for (int i = 0. byte[] record. rs.writeBoolean(b Data[i]).

readUTF()). Ghi mảng byte vào RecordStore. recData. sau đó dùng 95 . System.println("UTF: " + strmDataType. for (int i = 1.out. i <= rs. recData.println("Boolean: " + strmDataType. 0).length).out. Flush stream.getRecord(i.readInt()).println("Int: " + strmDataType.println("Record #" + i).Mobile Programming (Private Version) record. System. Đóng stream. // Toss any data in the internal array so writes // starts at beginning (of the internal array) strmBytes.getRecord(i. Việc đọc các dữ liệu từ record đơn giản chỉ cần thay OutStream thành InputStream: byte[] recData = new byte[50].println("--------------------").out. System.getNumRecords(). 0).readBoolean()). Như vậy quá trình ghi dữ liệu vào RecordStore được thực hiện thông qua các bước:       Cấp phát stream. chúng ta khai báo một mảng byte recData (giống với strmBytes). } Ở đây. sau khi gọi hàm rs.out.reset(). Chuyển đổi stream data thành mảng byte. Chúng ta đã có mảng byte lúc đầu chương trình ghi vào record. // Reset so read starts at beginning of array strmBytes.out. i++) { // Get data into the byte array rs.reset(). Trịnh Công Duy } Hàm đã sử dụng hai stream là strmBytes là một mảng byte sẽ ghi vào RecordSotre và strmDataType để chứa dữ liệu. System. // Read Java data types from the above byte array DataInputStream strmDataType = new DataInputStream(strmBytes). Ghi dữ liệu vào stream. // Read back the data types System. // Read from the specified byte array ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData).

0). nó sẽ trả về dòng cuối cùng. Duyệt Record với RecordEnumeration Trong các ví dụ trước để duyệt các record ta đã sử dụng vòng lặp: for (int i = 1.rms. cần phải đọc và ghi theo đúng thứ tự. recData.RecordEnumeration Phương thức Mô tả int numRecords() Số lượng record trong enumeration byte[] nextRecord() Record tiếp theo int nextRecordId() Record ID của record tiếp theo byte[] previousRecord() Record trước đó int previousRecordId() Record ID của record trước đó boolean hasNextElement() Kiểm tra enumeration có record kế tiếp 96 . Dưới đây là đoạn code duyệt toàn bộ RecordStore: RecordEnumeration re = rs.readBoolean()).getRecord(i.out.f alse).readInt()).. ngoài ra còn có previousRecord() giúp duyệt về record trước đó.nextRecord()).enumerateRecords(null. Lưu ý: khi sử dụng DataOutputStream và DataInputStream. i++) { rs. do something .hasNextElement()) { // Get the next record into a String String str = new String(re. Lớp này cung cấp các phương thức để duyệt các record trong RecordStore một cách nhanh chóng.. RecordEnumeration có duy trì một index của các record.readUTF()).. do đó chúng ta cần phải gọi hàm reindex() mỗi khi recordstore có sự thay đổi.4. VI. Các phương thức của giao diện Record Enumeration RecordEnumeration Interface: javax. i <= rs.out. } Trong đoạn code trên đã sử dụng nextRecord() để duyệt đến record sau đó. Nếu muốn bắt đầu tại vị trí cuối cùng của recordstore ta chỉ cần gọi hàm previousRecord() ngay khi mở recordstore.println("Int: " + strmDataType.getNumRecords(). nếu không sẽ không ra kết quả mong muốn.println("UTF: " + strmDataType..microedition.out.Mobile Programming (Private Version) InputStream strmDataType để đọc đúng kiểu dữ liệu: Trịnh Công Duy System.println("Boolean: " + strmDataType. . Khi recordstore có sự thay đổi thì RecordEnumeration có thể hoạt dộng không chính xác. System. } Ngoài ra còn một cách khác để duyệt RecordStore là sử dung RecordEnumeration.null. System. while (re.

else if (result < 0) return RecordComparator.PRECEDES. byte[] rec2) { String str1 = new String(rec1). Phương thức này trả về các trị sau được định nghĩa trong interface:    EQUIVALENT: Nếu hai Record bằng nhau FOLLOWS: Nếu Record thứ 1 đứng sau Record thứ 2 PRECEDES: Nếu Record thứ 1 đứng trước Record thứ 2 Do RecrdComparator là một interface nên khi sử dụng cần phải implements nó: public class Comparator implements RecordComparator { public int compare(byte[] rec1.5.EQUIVALENT. if (result == 0) return RecordComparator. Interface này định nghĩa phương thức compare với trị đầu vào là hai mảng các byte thể hiện hai Record cần so sánh.compareTo(str2).FOLLOWS } } 97 . else return RecordComparator. Sắp xếp các record với interface RecordComparator Interface này giúp người lập trình so sánh hai Record theo một tiêu chí nào đó.Mobile Programming (Private Version) boolean hasPreviousElement() void keepUpdated(boolean keepUpdated) boolean isKeptUpdated() void rebuild() void reset() void destroy() Trịnh Công Duy Kiểm tra enumeration có record trước Đó Đặt enumeration reindex sau khi co sự thay đổi Kiểm tra enumeration có tự động reindex() Tạo lại index Đưa enumeration về record đầu tiên Giải phóng tài nguyên được sử dụng bởi enumeration VI. str2 = new String(rec2). int result = str1.

hasNextElement()) { String str = new String(re.rms. public SimpleSort() { openRecStore(). So sánh để quyết định thứ tự sắp xếp byte[] rec2) Ví dụ 3: chương trình sắp xếp cơ bản /*-------------------------------------------------* SimpleSort.nextRecord()). all output is to the console *-------------------------------------------------*/ import java. // Reference the comparator when creating the result set RecordEnumeration re = rs.Mobile Programming (Private Version) Trịnh Công Duy Sau đó ta sử dụng lớp Comparator bằng cách gắn kết nó với RecordEnumeration: // Create a new comparator for sorting Comparator comp = new Comparator().microedition.enumerateRecords(null. sorting the results readRecords(). closeRecStore().f alse).RecordComparator Phương thức Mô tả int compare(byte[] rec1.microedition. writeRecord("Fiv e Iron"). import javax.rms. // Create the record store // Write a few records writeRecord("San d Wedge").*. // Read back with enumerator.microedition.*. import javax. public class SimpleSort extends MIDlet { private RecordStore rs = null.midlet. RecordComparator Interface: javax.java * * No GUI interface. static final String REC_STORE = "db_1".*.io. } Enumeration sẽ sử dụng hàm compare trong class Comparator để sắp xếp các record trong RecordStore.comp. // Iterate through the sorted results while (re. // Close record store 98 . writeRecord("One Wood"). writeRecord("Put ter").

} } public void deleteRecStore() { if (RecordStore. // Remove the record store } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user interface. } catch (Exception e) { 99 .listRecordStores() != null) { try { RecordStore.deleteRecordStore(REC_STORE). go ahead and shutdown destroyApp(false). true ). notifyDestroyed().Mobile Programming (Private Version) deleteRecStore().closeRecordStore(). } catch (Exception e) { db(e. } } public void closeRecStore() { try { rs.toString()).openRecordStore(REC_S TORE. } catch (Exception e) { db(e. } public void pauseApp() Trịnh Công Duy { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore.toString()).

toString()). try { rs.out.nextRecord()).err. *-------------------------------------------------*/ private void db(String str) { System.println(str). } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner. } catch (Exception e) { db(e.out.comp.getBytes().println("Msg: " + str). System. while (re.enumerateRecords(null.toString()).println("-----------------------------"). rec. false).Mobile Programming (Private Version) db(e. 0. } } Trịnh Công Duy public void readRecords() { try { if (rs.addRecord(rec. RecordEnumeration re = rs.hasNextElement()) { String str = new String(re. } } } catch (Exception e) { db(e.getNumRecords() > 0) { Comparator comp = new Comparator(). System. } } } public void writeRecord(String str) { byte[] rec = str. } 100 .length).toString()).

else return RecordComparator.FOLLOWS. RecordEnumeration re = rs. comp. false). int result = str1. else if (result < 0) return RecordComparator. nếu quay lại ví dụ 2 ta đã ghi nhiều kiểu dữ liệu vào trong một record: 101 .EQUIVALENT.enumerateRecords(null. str2 = new String(rec2). byte[] rec2) { String str1 = new String(rec1). if (result == 0) return RecordComparator. khi tạo Enumeration ta đã tham chiếu đến đối tượng comp của lớp Comparator Comparator comp = new Comparator().compareTo(str2).java | | Compares two records to determine sort order *-------------------------------------------------*/ class Comparator implements RecordComparator { public int compare(byte[] rec1.hasNextElement()) { . Output của vi dụ 3: Ví dụ trên đúng trong trường hợp dữ liệu lưu vào record là dạng text...Mobile Programming (Private Version) } Trịnh Công Duy /*-------------------------------------------------| Comparator. while (re.PRECEDES. } Khi enumerator tạo index cho RecordStore nó sẽ sử dụng hàm compare() ở trên để sắp xếp các record. } } Trong đoạn code trên trong hàm readRecord().

true}. Do đó. record. do dữ liệu lưu vào không còn là dạng text. 0. int[] rank = {3. "spike". Trong ví dụ 4 sẽ lấy kiểu String ở đầu mỗi record. bạn cần phải lấy ra từ mỗi record trường dữ liệu mà bạn muốn sắp xếp. chúng ta cần phải lưu nhiều trường dữ liệu trong một record như trong ví dụ 2 (lưu dữ liệu kiểu String. Trịnh Công Duy Các kiểu dữ liệu trên sẽ được lưu vào một stream ở dạng binary. tuy nhiên ví dụ 4 sẽ sắp xếp dựa vào kiểu String. Những ví dụ này sẽ sử dụng cùng dữ liệu đầu vào.CompareTo() trên nội dung của record không thể sắp xếp dữ liệu theo mong muốn. Đoạn code trong ví dụ 3 sẽ chạy sai khi áp dụng với kiểu dữ liệu binary. Trong thực tế. Sau đó các stream này sẽ được chuyển thành mảng và đưa vào recordstore: // Get stream data into an array record = strmBytes. Đây là dữ liệu sẽ lưu vào recordstore: String[] pets = {"duke". "beauregard"}.writeInt(1). ví dụ 5 sẽ lấy kiểu integer ở cuối mỗi record.length). Trong trường hợp này sẽ có nhiều lựa chọn để sắp xếp các record. 2}. và việc lựa chọn này tùy thuộc vào ứng dụng của bạn. boolean.writeBoolean(true).addRecord(record. Ví dụ 4: StringSort. integer). 1. true. Khi lưu vào recordstore sẽ có dạng như sau: Record #1 "duke" true 3 Record #2 "tiger" false 0 Record #3 "spike" true 1 Record #4 "beauregard" true 2 Đây là lý do ví dụ 3 không đáp ứng được yêu cầu.writeUTF("Text 1"). và hàm String. strmDataType. false.java /*-------------------------------------------------102 .toByteArray(). Trong 2 ví dụ sau đây sẽ thực thi interface RecordComparator để sắp xếp record chứa nhiều kiểu dữ liệu. 0.Mobile Programming (Private Version) // Write Java data types to stream strmDataType. "tiger". // Write the array to a record rs. ta cần phải viết lại hàm compare() thự c hiện chức năng chuyển đổi chuỗi byte và sắp xếp đúng kiểu dữ liệu. strmDataType. Để giải quyết. boolean[] dog = {true. trong khi ví dụ 5 sẽ sắp xếp dựa vào kiểu integer.

public class StringSort extends MIDlet { private RecordStore rs = null.rms. * * Uses: Streams. all output is to the console *-------------------------------------------------*/ import java.microedition. // Create the record store writeTestData().*. } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore.toString()). } 103 . import javax.Mobile Programming (Private Version) * StringSort.microedition. // Name of record store public StringSort() { openRecStore(). // Read back the records closeRecStore(). notifyDestroyed(). RecordComparator * * No GUI interface.io.*. // Write a series of records readStream().*. import javax. } catch (Exception e) { db(e. // Remove the record store Trịnh Công Duy } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user interface.java * * Sort records that contain multiple Java * data types.midlet. // Record store static final String REC_STORE = "db_3". // Close record store deleteRecStore(). Sort using String type.openRecordStore(REC_S TORE. go ahead and shutdown destroyApp(false). true ). Enumeration.

"spike". boolean[] bData.Mobile Programming (Private Version) } public void closeRecStore() { try { rs. rank). *-------------------------------------------------*/ public void writeStream(String[] sData. 2}. 0. } catch (Exception e) { db(e. // Write Java data types into the above byte array 104 . true. "tiger". } Trịnh Công Duy } } /*-------------------------------------------------* Create three arrays to write to record store *-------------------------------------------------*/ public void writeTestData() { String[] pets = {"duke". 1. } /*-------------------------------------------------* Write to record store using streams. } } public void deleteRecStore() { if (RecordStore. true}. false. boolean[] dog = {true.toString()). "beauregard"}. } catch (Exception e) { db(e. int[] rank = {3.toString()). dog. writeStream(pets.closeRecordStore().listRecordStores() != null) { try { RecordStore.int[] iData) { try { // Write data into an internal byte array ByteArrayOutputStream strmBytes = new ByteArrayOutputStream().deleteRecordStore(REC_STORE).

0. strmDataType.reset(). // Read from the specified byte array ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData). i++) { // Write Java data types strmDataType. i < sData. } catch (Exception e) { db(e. for (int i = 0.toByteArray().close(). record. strmDataType.flush(). } } /*-------------------------------------------------* Read from the record store using streams *-------------------------------------------------*/ public void readStream() { try { // Careful: Make sure this is big enough! // Better yet. // Toss any data in the internal array so writes // starts at beginning (of the internal array) strmBytes. } strmBytes. rs.length. byte[] record.writeInt(iData[i]) . // Read Java data types from the above byte array 105 .Mobile Programming (Private Version) Trịnh Công Duy DataOutputStream strmDataType = new DataOutputStream(strmBytes).addRecord(record. // Get stream data into byte array and write record record = strmBytes. // Clear any buffered data strmDataType.writeBoolean(bData [i]).writeUTF(sData[i]) . test and reallocate if necessary byte[] recData = new byte[50].length). strmDataType.close() .toString()).

// Read back the data types System.println("Dog: " + strmDataType. RecordEnumeration re = rs.compareStringClose(). 0). comp. recData.Mobile Programming (Private Version) Trịnh Công Duy DataInputStream strmDataType = new DataInputStream(strmBytes). // Free enumerator re.out . } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner.out.println("Msg: " + str).enumerateRecords(null.readUTF()). } comp.destroy(). System.out.readBoolean()).hasNextElement()) { // Get data into the byte array rs. } catch (Exception e) { db(e. // Reset so read starts at beginning of array strmBytes. if (rs. 106 . while (re. strmDataType. false).getRecord(re.readInt()).close() . *-------------------------------------------------*/ private void db(String str) { System.close().println("-------------------"). int i = 1. } strmBytes. System.nextRecordId(). System.println("Record #" + i++).toString()).reset().println("Name: " + strmDataType.println("Rank: " + strmDataType. System.err.getNumRecords() > 0) { ComparatorString comp = new ComparatorString().out.out.

length) recData = new byte[maxsize]. if (strmDataType != null) strmDataType. // Read record #1 // Only need one read because the string to // sort on is the first "field" in the record strmBytes = new ByteArrayInputStream(rec1). rec2. // Read from a specified byte array private ByteArrayInputStream strmBytes = null.length). reallocate int maxsize = Math.max(rec1. } catch (Exception e) { } } public int compare(byte[] rec1. public void compareStringClose() { try { if (strmBytes != null) strmBytes.use only the String data for sorting *-------------------------------------------------*/ class ComparatorString implements RecordComparator { private byte[] recData = new byte[10]. str2. strmDataType = new DataInputStream(strmBytes).readUTF().close(). if (maxsize > recData.Mobile Programming (Private Version) } Trịnh Công Duy } /*-------------------------------------------------| Compares two strings to determine sort order | Each record passed in contains multiple Java data | types . 107 . // Read Java data types from the above byte array private DataInputStream strmDataType = null. try { // If either record is larger than our buffer.length.close(). str1 = strmDataType. // Read record #2 strmBytes = new ByteArrayInputStream(rec2). byte[] rec2) { String str1.

else return RecordComparator. 108 . str1 = strmDataType. . // Compare record #1 and #2 int result = str1. } } } Trịnh Công Duy Trường dữ liệu đầu tiên trong các record là kiểu string. // Compare record #1 and #2 int result = str1. str2 = strmDataType. . .Mobile Programming (Private Version) strmDataType = new DataInputStream(strmBytes). else if (result < 0) return RecordComparator.FOLLOWS. Trước hết ta lấy chuỗi cần so sánh trong dãy byte bằng hàm readUTF() .EQUIVALENT.compareTo(str2). . str2 = strmDataType. . // Read record #2 .readUTF(). } catch (Exception e) { return RecordComparator.readUTF().compareTo(str2). if (result == 0) return RecordComparator.readUTF().PRECEDES.dùng làm tiêu chí sắp xếp. rồi dùng compareTo() trong class String để sắp xếp: // Read record #1 // Only need one read because the string to // sort on is the first "field" in the record .EQUIVALENT.

// Write a series of records readStream(). all output is to the console *-------------------------------------------------*/ import java. Trịnh Công Duy Output của ví dụ 4: Ví dụ 5: /*-------------------------------------------------* IntSort.midlet . public class IntSort extends MIDlet { private RecordStore rs = null. // 109 .microedition.java * * Sort records that contain multiple Java * data types. // Name of record store public IntSort() { openRecStore(). Sort using integer type. * * Uses: Streams. // Read back the records closeRecStore(). RecordComparator * * No GUI interface.. import javax. // Create the record store writeTestData().*. import javax..Mobile Programming (Private Version) . // Record store static final String REC_STORE = "db_4". Enumeration.microedition.rms.*.io.*.

notifyDestroyed().listRecordStores() != null) { try { RecordStore.openRecordStore(REC_S TORE. go ahead and shutdown destroyApp(false). } catch (Exception e) { db(e. } } public void closeRecStore() { try { rs. } catch (Exception e) { db(e.Mobile Programming (Private Version) Trịnh Công Duy Close record store deleteRecStore().toString()). } } public void deleteRecStore() { if (RecordStore.toString()).deleteRecordStore(REC_STORE). true ). } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore. } catch (Exception e) { 110 .closeRecordStore(). // Remove the record store } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user interface.

*-------------------------------------------------*/ public void writeStream(String[] sData. writeStream(pets. } 111 . 1.flush(). } } Trịnh Công Duy } /*-------------------------------------------------* Create three arrays to write to record store *-------------------------------------------------*/ public void writeTestData() { String[] pets = {"duke". "beauregard"}. rs. boolean[] bData. // Toss any data in the internal array so writes // starts at beginning (of the internal array) strmBytes. rank). false.writeUTF(sData[i]) . true. "spike".length).toByteArray(). 0. // Clear any buffered data strmDataType. true}. 0.Mobile Programming (Private Version) db(e. for (int i = 0.length. dog. boolean[] dog = {true. record. i < sData. i++) { // Write Java data types strmDataType.int[] iData) { try { // Write data into an internal byte array ByteArrayOutputStream strmBytes = new ByteArrayOutputStream().toString()). } /*-------------------------------------------------* Write to record store using streams.addRecord(record.writeInt(iData[i]) . int[] rank = {3. byte[] record. strmDataType. 2}. strmDataType.reset(). // Write Java data types into the above byte array DataOutputStream strmDataType = new DataOutputStream(strmBytes). // Get stream data into byte array and write record record = strmBytes.writeBoolean(bData [i]). "tiger".

System.comp. RecordEnumeration re = rs.getNumRecords() > 0) { ComparatorInt comp = new ComparatorInt().println("-------------------112 .out. // Read from the specified byte array ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData).Mobile Programming (Private Version) strmBytes.readBoolean()).out. // Read back the data types System.out. int i = 1. // Read Java data types from the above byte array DataInputStream strmDataType = new DataInputStream(strmBytes).out. if (rs.println("Record #" + i++). } Trịnh Công Duy } /*-------------------------------------------------* Read from the record store using streams *-------------------------------------------------*/ public void readStream() { try { // Careful: Make sure this is big enough! // Better yet.println("Dog: " + strmDataType. while (re. recData.close(). test and reallocate if necessary byte[] recData = new byte[50]. } catch (Exception e) { db(e. System. false). 0).getRecord(re.out. System.hasNextElement()) { // Get data into the byte array rs.readInt()). strmDataType.readUTF()).nextRecordId().println("Rank: " + strmDataType.println("Name: " + strmDataType.enumerateRecords(null. System.toString()).close() .

destroy(). strmDataType. } } /*-------------------------------------------------| Compares two integers to determine sort order | Each record passed in contains multiple Java data | types .close( ). } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner. // Free enumerator re.compareIntClose(). } catch (Exception e) { db(e.err. // Read Java data types from the above byte array private DataInputStream strmDataType = null.Mobile Programming (Private Version) ").toString()).println("Msg: " + str). if (strmDataType != null) Trịnh Công Duy 113 .clo se().use only the integer data for sorting *-------------------------------------------------*/ class ComparatorInt implements RecordComparator { private byte[] recData = new byte[10]. } comp. *-------------------------------------------------*/ private void db(String str) { System. // Reset so read starts at beginning of array strmBytes. } strmBytes. public void compareIntClose() { try { if (strmBytes != null) strmBytes.reset().close(). // Read from a specified byte array private ByteArrayInputStream strmBytes = null.

strmDataType = new DataInputStream(strmBytes).readBoolean(). strmDataType.readUTF(). strmDataType.Mobile Programming (Private Version) strmDataType.PRECEDES.readBoolean(). // Here's our data // Read record #2 strmBytes = new ByteArrayInputStream(rec2). } catch (Exception e) { } Trịnh Công Duy } public int compare(byte[] rec1.FOLLOWS. // Here's our data // Compare record #1 and #2 if (x1 == x2) return RecordComparator. x1 = strmDataType. rec2. strmDataType.readUTF().readInt(). x2. if (maxsize > recData.max(rec1.close().readInt(). strmDataType.length. else if (x1 < x2) return RecordComparator. else return RecordComparator. try { // If either record is larger than our buffer. // Read record #1 // We want the integer from the record.EQUIVALENT. x2 = strmDataType. strmDataType = new DataInputStream(strmBytes).length). reallocate int maxsize = Math.length) recData = new byte[maxsize]. byte[] rec2) { int x1. which is // the last "field" thus we must read the String // and boolean to get to the integer strmBytes = new ByteArrayInputStream(rec1). } catch (Exception e) { 114 .

Output của ví dụ 5: 115 . } } } Trịnh Công Duy Trong ví dụ này ta vẫn sẽ dùng dữ liệu của ví dụ 4.Mobile Programming (Private Version) return RecordComparator. which is // the last "field" thus we m ust read the String // and boolean to get to the integer ..readBoolean().readBoolean().EQUIVALENT. tức là phải đọc kiểu String.readInt(). // Here's our data // Read record #2 ..readUTF().. boolean rồi mới đến integer: // Read record #1 // W e want the integer from the record. strmDataType. strmDataType..có một lưu ý là do dữ liệu ta cần lấy nằm cuối cùng trong dãy byte do đó ra cần phải đọc theo thứ tự. tiêu chí sắp xếp là theo kiểu integer.. x1 = strmDataType.readUT F(). // Here's our data // Com pare record #1 and #2 .. strmDataType..readInt(). strmDataType. x2 = strmDataType. Tuy nhiên. do đó trước hết ta phải lấy dữ liệu trong dãy byte.

public SearchFilter(String searchText) { // This is the text to search for this.searchText = searchText. chỉ có những thỏa mãn điều kiện mới có trong enumerator result set.Mobile Programming (Private Version) Trịnh Công Duy VI. els e return false. enumerator còn cung cấp cơ chế lọc (tìm kiếm các record thỏa mãn một điều kiện nào đó).6. Khi sử dụng RecordComparator tất cả các record trong RecordStore đều được lưu trong một result set. } } 116 . class SearchFilter implements RecordFilter { private String searchText = null. Searching with RecordFilter Ngoài việc sắp xếp các record (sử dụng RecordComparator). Nhưng khi dùng RecordFilter. } public boolean matches(byte[] candidate) { String str = new String(candidate). // Look for a match if (searchText != null && str.indexOf(searchText) != -1) return true.toLowerCase().toLowerCase().

import javax. // The matching text. // If there is at least one record in result set.enumerateRecords(search.lcdui. Class này sẽ được gắn với một enumerator.RecordFilter Phương thức Mô tả boolean matches(byte[] candidate) Tìm kiếm record thỏa mãn một điều kiện nào đó Sau đây ta sẽ xem qua chương trình tìm kiếm đơn giản sử dụng interface RecordFilter: Ví dụ 6: /*-------------------------------------------------* SimpleSearch.null. và khi đó enumerator sẽ dùng hàm matches() duyệt hết recordstore lấy ra những record cần tìm: // Create a new search filter SearchFilter search = new SearchFilter("search text").Trên đây là một class đơn giản thực thi interface RecordFilter.numRecords() > 0) // Do something RecordFilter Interface: javax. * * Uses: Enumeration.*.microedi tion.f alse). import javax.*.rms. // The main form private StringItem siMatch. public class SimpleSearch extends MIDlet implements CommandListener { private Display display. a match was found if (re.*.rms.microedi tion. // Reference the filter when creating the result set RecordEnumeration re = rs. import javax.*.microedi tion. // Reference to Display object private Form fmMain. RecordFilter *-------------------------------------------------*/ import java.java * * Display a Form and Textbox for searching records * Each record contains a String object.midlet. // Command Trang 117 .microedition. if any private Command cmFind.io.

cmFind = new Command("Find". // Command to insert items private TextField tfFind. // Create the record store writeTestData(). add commands fmMain = new Form("Record Search"). null).EXIT.ANY). // Write a series of records } public void destroyApp( boolean unconditional ) { closeRecStore(). fmMain.setCommandLis tener(this). "".SCREEN. // Record store static final String REC_STORE = "db_6".addCommand(cmEx it). // Capture events fmMain. 1). // Append textfield and stringItem fmMain. //-------------------------------// Open and write to record store //-------------------------------openRecStore(). siMatch = new StringItem(null.append(tfFi nd).getDisplay(this).addCommand(cmFi nd). // Close record store deleteRecStore(). // Create the form. fmMain. fmMain.append(siMa tch). // Name of record store public SimpleSearch() { display = Display.Mobile Programming (Private Version) Trịnh Công Duy to search record store private Command cmExit. Command. } public void startApp() { 118 . TextField. // Search text as requested by user private RecordStore rs = null. stringItem and commands tfFind = new TextField("Find". 10. // Define textfield. Command. cmExit = new Command("Exit". 2).

} catch (Exception e) { db(e.toString()). } } public void closeRecStore() { try { rs. } } } /*-------------------------------------------------* Create array of data to write into record store *-------------------------------------------------*/ public void writeTestData() { String[] golfClubs = { "Wedge..setCurrent(fmMain). } catch (Exception e) { db(e. } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore.deleteRecordStore(REC_STORE).good from the sand trap". 119 ..toString()). "Truong dai hoc khoa hoc tu nhien".Mobile Programming (Private Version) Trịnh Công Duy display.closeRecordStore(). true ). } } public void deleteRecStore() { if (RecordStore.openRecordStore(REC_S TORE. } catch (Exception e) { db(e.listRecordStores() != null) { try { RecordStore.toString()).

getNumReco rds() > 0) { // Setup the search filter with the user requested text SearchFilter search = new SearchFilter(tfFind. rs.getS tring()). } } /*-------------------------------------------------* Search using enumerator and record filter *-------------------------------------------------*/ private void searchRecordStore() { try { // Record store is not empty if (rs. false).. i < sData. writeRecords(golfClubs). 120 .length. RecordEnumeration re = rs. null. for (int i = 0. try { // Only add the records once if (rs. *-------------------------------------------------*/ public void writeRecords(String[] sData) { byte[] record. i++) { record = sData[i]. } /*-------------------------------------------------* Write to record store.Mobile Programming (Private Version) Trịnh Công Duy "Putter. record..addRecord(record.toString()). 0.getNumReco rds() > 0) return. } } catch (Exception e) { db(e.only on the green". "Hoc mon LTUDM rat bo ich!"}.enumerateRecords(sear ch.length).getBytes().

notifyDestroyed().setText(new String(re.nextRecord())).toLowerCase(). *-------------------------------------------------*/ private void db(String str) { System. } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner. re.destroy().err.Mobile Programming (Private Version) Trịnh Công Duy // A match was found using the filter if (re. } 121 .toString()). } else if (c == cmExit) { destroyApp(false). public SearchFilter(String searchText) { // This is the text to search for this. // Free enumerator } } catch (Exception e) { db(e. } } /*-------------------------------------------------* Search for text within a record * Each record passed in contains only text (String) *-------------------------------------------------*/ class SearchFilter implements RecordFilter { private String searchText = null. Displayable s) { if (c == cmFind) { searchRecordStore().numRecords() > 0) // Show match in the stringItem on the form siMatch.println("Msg: " + str). } } public void commandAction(Command c.searchText = searchText.

} } Sau khi viết class SearchFilter. RecordEnumeration re =rs.Mobile Programming (Private Version) Trịnh Công Duy public boolean matches(byte[] candidate) { String str = new String(candidate).setText(new String(re. 122 .enumerateRecords(search. Khi đó chỉ có những record thỏa mãn điều kiện (trong hàm matches()) mới hiển thị trong result set: // Setup the search filter with the user requested text SearchFilter search = new SearchFilter(tfFind. // A match was found using the filter if (re.numRecords() > 0) siMatch. // Look for a match if (searchText != null && str. else return false. Output: Lưu ý ví dụ trên không phân biệt chữ hoa thường. khi khai báo class RecordEnumeration sẽ tham chiếu đến instance trên.toLowerCase().null.getString()). ta tạo một instance search.indexOf(searchText) != -1) return true.false).nextRecord())).

// Define textfield.microedit ion. // The matching text. Command. 10. import javax. public class SearchStreams extends MIDlet implements CommandListener { private Display display. if any private Command cmFind. boolean and integer).lcdui. // Search text private RecordStore rs = null. // Record store static final String REC_STORE = "db_7".SCREEN. RecordFilter *-------------------------------------------------*/ import java. add commands fmMain = 123 . Ví dụ 7: /*-------------------------------------------------* SearchStreams.microedit ion. // Name of record store public SearchStreams() { display = Display. siMatch = new StringItem(null. // Reference to Display object private Form fmMain.rms.Mobile Programming (Private Version) Trịnh Công Duy Tiếp theo đây chúng ta lại xét thêm một ví dụ về tìm kiếm record.*. Command.ANY). // Command to search record store private Command cmExit.*. stringItem and commands tfFind = new TextField("Find". 2). null). // Create the form.microedit ion.io. Use the String * data for searching * * Uses: Enumeration.*.EXIT. // The main form private StringItem siMatch. import javax. 1). // Command to insert items private TextField tfFind. cmExit = new Command("Exit".midlet. import javax. cmFind = new Command("Find".java * * Display a Form and Textbox for searching records * Each record contains multiple Java data types * (String.getDisplay(this). TextField.*. "".

// Close record store deleteRecStore().append(siMa tch).addCommand(cmEx it). //-------------------------------// Open and write to record store //-------------------------------openRecStore().toString()).Mobile Programming (Private Version) Trịnh Công Duy new Form("Record Search").setCommandLis tener(this).setCurrent(fmMain). fmMain. // Create the record store writeTestData(). } catch (Exception e) { db(e. fmMain. } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore.append(tfFi nd).openRecordStore(REC_S TORE. // Write a series of records } public void destroyApp( boolean unconditional ) { closeRecStore().addCommand(cmFi nd). fmMain. // Append textfield and stringItem fmMain. // Capture events fmMain. } } public void closeRecStore() { 124 . } public void startApp() { display. true ).

loves chasing car tires". "spike .closeRecordStore(). goofy golden retriever".int[] iData) { try { // Only add the records once if 125 .toString()). writeStream(pets.deleteRecordStore(REC_STORE). } catch (Exception e) { db(e.barks at everything"}. } catch (Exception e) { db(e. "beauregard . boolean[] bData. "tiger . } } public void deleteRecStore() { if (RecordStore. false.listRecordStores() != null) { try { RecordStore. rank).toString()). int[] rank = {3. 1.Mobile Programming (Private Version) Trịnh Công Duy try { rs. 2}. true}. } /*-------------------------------------------------* Write to record store using streams.big. true. dog. *-------------------------------------------------*/ public void writeStream(String[] sData. boolean[] dog = {true. 0.we found in a field". } } } /*-------------------------------------------------* Create three arrays to write into record store *-------------------------------------------------*/ public void writeTestData() { String[] pets = {"duke .

// Write Java data types into the above byte array DataOutputStream strmDataType = new DataOutputStream(st rmBytes). } strmBytes.length.writeInt (iData[i]). // Toss any data in the internal array so writes // starts at beginning (of the internal array) strmBytes. 0. flush(). i++) { // Write Java data types strmDataType.toByteArray().close(). // Write data into an internal byte array ByteArrayOutputStream strmBytes = new ByteArrayOutputStream( ).writeBoo lean(bData[i]).toString()). strmDataType. // Clear any buffered data strmDataType. } catch (Exception e) { db(e. strmDataType. rs.Mobile Programming (Private Version) Trịnh Công Duy (rs. record. byte[] record.addRecord(record.length).writeUTF (sData[i]).getNumRecor ds() > 0) return. i < sData. strmDataType. } } /*-------------------------------------------------* Search using enumerator and record filter *-------------------------------------------------*/ private void searchRecordStore() { try 126 .close(). for (int i = 0.reset(). // Get stream data into byte array and write record record = strmBytes.

RecordEnumeration re = rs. null. // Close stream strmDataType.getNumReco rds() > 0) { // Setup the search filter with the user requested text SearchFilter search = new SearchFilter(tfFind. // A match was found using the filter if (re.close().numRecords() > 0) { // Read from the specified byte array ByteArrayInputStream strmBytes = new ByteArrayInputStream(re. search.nextRecord()).toString()). // Show matching result in stringItem component on form siMatch.getS tring()). // Close stream re.searchFilterClose(). // Free enumerator } } } catch (Exception e) { db(e. // Read Java data types from the above byte array DataInputStream strmDataType = new DataInputStream(strmBytes).enumerateRecords(sear ch. false). Displayable s) { if (c == cmFind) 127 .setText(strmDataType. // Close record filter strmBytes.destroy().Mobile Programming (Private Version) Trịnh Công Duy { // Record store is not empty if (rs. } } public void commandAction(Command c.readUT F()).close().

} else if (c == cmExit) { dest royA pp(f alse ). // Read Java data types from the above byte array private DataInputStream strmDataType = null.err.searchText = searchText. } } /*-------------------------------------------------* Search for text within a record * Each record passed in contains multiple Java data * types (String. noti fyDe stro yed( ).Mobile Programming (Private Version) Trịnh Công Duy { searchRecordStore().toLowerCase(). boolean and integer) *-------------------------------------------------*/ class SearchFilter implements RecordFilter { private String searchText = null. *-------------------------------------------------*/ private void db(String str) { System. } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner.println("Msg: " + str). } // Cleanup public void searchFilterClose() 128 . public SearchFilter(String searchText) { // This is the text to search for this. // Read from a specified byte array private ByteArrayInputStream strmBytes = null.

toLowerCas e(). else return false.Mobile Programming (Private Version) Trịnh Công Duy { try { if (strmBytes != null) strmBytes. strmDataType = new DataInputStream(strmBytes). // Write integers 129 . // Write Strings strmDataType.writeUTF(sData[i] ). } } Cùng có chức năng tìm kiếm nhưng ví dụ 7 khác ví dụ 6 ở chỗ dữ liệu lưu vào record. try { strmBytes = new ByteArrayInputStream(candidate). // Although 3 pieces of data were written to // the record (String.close(). } // Look for a match if (str != null && str. } catch (Exception e) { } } public boolean matches(byte[] candidate) { String str = null. // Write booleans strmDataType.indexOf(searchText) != -1) return true.close().writeBoolean(bDat a[i]). } catch (Exception e) { return false. boolean and integer) // we only need one read because the string to // search is the first "field" in the record str = strmDataType.readUTF(). và trong dãy byte này ta lại lưu nhiều trường dữ liệu: strmDataType. if (strmDataType != null) strmDataType. Ở ví dụ này dữ liệu lưu vào record dưới dạng dãy byte.writeInt(iData[i] ).

Phương thức matches() (của class SearchFilter) sẽ được gọi bởi enumerator và được áp dụng cho mỗi record trong RecordStore. int recordId) 130 .indexOf(searchText) != -1) return true. str = strmDataType. Sau đó chuỗi này sẽ được so sánh với searchText: if (str != null && str. RMS cung cấp giao diện RecordListener. các phương thức có 2 trị vào là một đối tượng kiểu RecordStore và một số int chứa recordID.readUTF(). Output: VI. strmBytes = new ByteArrayInputStream(candidate).Mobile Programming (Private Version) Trịnh Công Duy Trong hàm searchRecordStore() ta tạo một bộ tìm kiếm và enumerator.RecordListener Phương thức Mô tả void recordAdded(RecordStore Được gọi khi thêm 1 record recordStore.microedition. strmDataType = new DataInputStream(strmBytes).toLowerCase(). Giao diện này định nghĩa 3 phương thức. Các phương thức đó là: RecordListener Interface: javax.7. Notification of Changes with RecordListener Để phát hiện các thay đổi cũng như thêm vào các Record trong RecordStore. else return false.rms. Để đọc được đúng dữ liệu cần dùng ta cần dùng hai stream – một dùng để đọc dãy byte trong record và một dùng để đọc đúng kiểu dữ liệu trong dãy byte đó.

addRecordListener(new TestRecordListener()).microedition. // Close record store deleteRecStore(). updateRecord("MIDP and J2ME"). public class RmsListener extends MIDlet { private RecordStore rs = null.io.*.java * * Test the RMS listener methods * * No GUI interface. rs. // Remove the record store } public void destroyApp( boolean unconditional ) { } public void startApp() { // There is no user 131 . // Initiate actions that will wake up the listener writeRecord("J2ME and MIDP"). public RmsListener() { // Open record store and add listener openRecStore (). import javax. int recordId) Được gọi khi record bi thay đổi Được gọi khi record bị xóa Ví dụ 8: sử dụng RecordListener /*-------------------------------------------------* RmsListener.*. deleteRecord(). static final String REC_STORE = "db_8".*. import javax.Mobile Programming (Private Version) Trịnh Công Duy void recordChanged(RecordStore recordStore. int recordId) void recordDeleted(RecordStore recordStore. all output is to the console *-------------------------------------------------*/ import java.rms.midlet. closeRecStore().microedition.

} } } public void writeRecord(String str) { byte[] rec = str. } catch (Exception e) { db(e.toString()).deleteRecordStore(REC_STORE). try 132 . notifyDestroyed().listRecordStores() != null) { try { RecordStore.getBytes().openRecordStore(REC_S TORE. } catch (Exception e) { db(e.Mobile Programming (Private Version) Trịnh Công Duy interface. } catch (Exception e) { db(e.toString()). } public void pauseApp() { } public void openRecStore() { try { // Create record store if it does not exist rs = RecordStore. } } public void closeRecStore() { try { rs. } } public void deleteRecStore() { if (RecordStore. true ).toString()).closeRecordStore(). go ahead and shutdown destroyApp(false).

addRecord(rec.getBytes().toString()). str.length()). } catch (Exception e) { db(e.deleteRecord(1).setRecord(1.toString()). str.length). 0. } catch (Exception e) { db(e.err. *-------------------------------------------------*/ public void db(String str) { System.println("Msg: " + str). } } /*-------------------------------------------------* Listen for updates to the record store *-------------------------------------------------*/ class TestRecordListener implements RecordListener { public void recordAdded(RecordStore recordStore. } catch (Exception e) { db(e. } } /*-------------------------------------------------* Simple message to console for debug/errors * When used with Exceptions we should handle the * error in a more appropriate manner. 0.toString()). } } public void updateRecord(String str) { try { rs. int recordId) { try { 133 . } } public void deleteRecord() { try { rs. rec.Mobile Programming (Private Version) Trịnh Công Duy { rs.

println("Record with ID#: " + recordId + "added to RecordStore: " + recordStore. } } public void recordDeleted(RecordStore recordStore.getName()). } catch (Exception e) { System.Mobile Programming (Private Version) Trịnh Công Duy System. } } } Output: 134 .getName()).println("Record with ID#: " + recordId + "changed in RecordStore: " + recordStore.println(e). } catch (Exception e) { System. int recordId) { try { System.println("Record with ID#: " + recordId + "deleted from RecordStore: " + recordStore.out.println(e). int recordId) { try { System.out.err.getName()). } } public void recordChanged(RecordStore recordStore.out. } catch (Exception e) { System.err.err.println(e).

Generic Connection Framework bao gồm một tập hợp các interfaces được khai báo trong package javax. ngoại lệ này mô tả lỗi chung nhất trong quá trình thao tác với RMS.io. Hình vẽ sau thể hiện mối quan hệ giữa các interfaces: 135 . Kết nối mạng với Generic Connection Framework Với kích thước hơn 200 kb và hơn 100 class và interfaces trong gói java.8.  RecordStoreNotOpenException: Ngoại lệ này phát sinh ra khi thao tác trên một RecordStore đã bị đóng.io. GCF sẽ giúp cho các thiết bị di động có thể truy xuất được các tài nguyên mạng.net của J2SE sẽ chiếm hầu hết bộ nhớ vốn dĩ đã nhỏ bé của những thiết bị đi động. cũng như các tài nguyên khác mà địa chỉ của chúng được xác định bằng URL. Do đó Sun không thể kế thừa những gói này vào trong J2ME.  RecordStoreException: Đây là lớp cha của 4 lớp trên. Exception Handling Các phương thức trong API của RMS ngoài việc phát sinh các ngoại lệ thông thường đến môi trường chạy (runtime enviroment). mà họ đã xây dựng một chuẩn là Generic Connection Framework (GCF). RMS còn định nghĩa thêm các ngoại lệ trong gói javax. java.  RecordStoreNotFoundException: Ngoại lệ này phát sinh ra khi mở một RecordStore không tồn tại.rms như sau:  InvalidRecordIDException: Ngoại lệ này phát sinh ra khi không thể thao tác trên Record vì RecordID không thích hợp.microedition.Mobile Programming (Private Version) Trịnh Công Duy VI.  RecordStoreFullException: Ngoại lệ này phát sinh ra khi không còn đủ vùng nhớ. VII.microedition.

Mobile Programming (Private Version)

Trịnh Công Duy

Có một class chính là Connector và 7 interfaces đã được định nghĩa trong GCF. Từ hình vẽ ta dễ dàng nhận thấy GCF hỗ trợ kết nối dạng datagram(packet) và stream. Class Connector được dùng để mở kết nối đến một tài nguyên nào đó thông qua phương thức open được khai báo như sau: Connector.Open("protocol:address ;parameters");

VII.1. Những protocol được hỗ trợ trong GCF
GCF hỗ trỡ nhiều loại protocol khác nhau. Khi có yêu cầu tạo một kết nối, class Connector sẽ sử dụng phương thức Class.forName() để tìm kiếm một class phù hợp với protocol đó. Nếu tìm thấy một đối tượng sẽ được trả về và thực thi interface Connection (hình vẽ). Dưới đây là cách để mở các kết nối khác nhau: Connector.Open("socket://www.corej2me.com.c om:55"); Connector.Open("http://www.corej2me.com"); Connector.Open("datagram://www.corej2me.com :1000"); Connector.Open("file://makefile.txt"); Mở một kết nối Để tạo kết nối, GCF đã cung cấp cho ta đến 7 phương thức: Connector (public class Connector); public static Connection open(String name); public static Connection open(String name); public static Connection open(String name, int mode, boolean timeouts); public static DataInputStream openDataInputStream(String name);

136

Mobile Programming (Private Version)

Trịnh Công Duy

public static DataOutputStream openDataOutputStream(String name); public static InputStream openInputStream(String name); public static OutputStream openOutputStream(String name);

Dưới đây là đoạn code mở kết nối thông qua stream // Create a ContentConnection String url = "http://www.corej2me.com" ContentConnection connection = (ContentConnection) Connector.open(url); // With the connection, open a stream InputStream iStrm = connection.openInputStream(); // ContentConnection includes a length method int length = (int) connection.getLengt h(); if (length != -1) { byte imageData[] = new byte[length]; // Read the data into an array iStrm.read(imageData); } Tuy nhiên, ngoài class ContentConnection, ta cũng có thể mở một kết nối trực tiếp bằng InputStream. Sau đây là đoạn code cho phép tải về một bức ảnh và sau đó tạo lại bức ảnh đó: InputStream iStrm = (InputStream) Connector.openInputStream(url); Image img = null; try { ByteArrayOutputStream bStrm = new ByteArrayOutputStream(); int ch; while ((ch = iStrm.read()) != -1) bStrm.write(ch); // Place into image array byte imageData[] = bStrm.toByteArray();

137

Mobile Programming (Private Version)

Trịnh Công Duy

// Create the image from the byte array img = Image.createImage(imageData, 0, imageData.length); } finally { // Clean up if (iStrm != null) iStrm.close(); } Lưu ý, trong đoạn code trên do không sử dụng class ContentConnection, nên ta sẽ không biết được kích thước của dữ liệu sẽ tải về. Để khắc phục vấn đề này, người ta sẽ dùng ByteArrayOutputStream để lưu dữ liệu tải về. Dưới đây là ví dụ đơn giản, đầu tiên MIDlet sẽ download và hiển thị hình ảnh đã tải về. MIDlet sẽ dử dụng ByteArrayOutputStream để chứa dữ liệu tải về: /*-------------------------------------------------* DownloadImage.java * * Download and view a png file *-------------------------------------------------*/ import javax.micr oedition.m idlet.*; import javax.micr oedition.l cdui.*; import javax.micr oedition.i o.*; import java.io.*; public class DownloadImage extends MIDlet implements CommandListener { priva te Displ ay displ ay; priva te

138

tbMain.setC ommandListe ner(this). priva te Comma nd cmExi t. // Create the textbox.addCommand(cmView ). priva te Comma nd cmBac k.EXIT. // Set up a listener for textbox tbMain. 139 . "http://localhost/intel.SCREEN. 2). priva te Comma nd cmVie w. Command. cmView = new Command("View". 0). public DownloadImage() { display = Display.Mobile Programming (Private Version) Trịnh Công Duy TextB ox tbMai n. allow maximum of 50 characters tbMain = new TextBox("Enter url".addCommand(cmExit). tbMain. Command. 55.p ng". 1). priva te Form fmVie wPng. // Create commands and add to textbox cmExit = new Command("Exit".getDisplay(this).

// Set up a listener for form fmViewPng. null). Displayable s) { // If the Command button pressed was "Exit" if (c == cmExit) { destroyApp(false). set (replace) it if (fmViewPng. if ((im = getImage(tbMain.setCurrent(tbMain).addCommand(cmB ack). } public void pauseApp() { } public void destroyApp(boolean unconditional) { } /*-------------------------------------------------* Process events *-------------------------------------------------*/ public void commandAction(Command c. notifyDestroyed(). ImageItem. // Create commands and add to form cmBack = new Command("Back". else // Append 140 . Command. // If there is already an image.Mobile Programming (Private Version) Trịnh Công Duy // Create the form that will hold the image fmViewPng = new Form("").getString())) != null) { ImageItem ii = new ImageItem(null. } else if (c == cmView) { // Download image and place on the form try { Image im.size() != 0) fmViewPng. fmViewPng. ii).LAYOUT_DEFAULT.BACK. im. } public void startApp() { display.setCommandListener(this).set(0. 1).

} } /*-------------------------------------------------* Open connection and download png into a byte array.").err.println("Msg: " + e. // Place into image array byte imageData[] = bStrm.append("Unsuccessful download. // Display the form with image display. } else fmViewPng.setCurrent(tbMain). 0.setCurrent(fmViewPng).length).createImage(imageData. } } else if (c == cmBack) { display. // Create the image from the byte array im = Image.close(). *-------------------------------------------------*/ private Image getImage(String url) throws IOException { InputStream iStrm = (InputStream) Connector. while ((ch = iStrm. } catch (Exception e) { System.toString()).Mobile Programming (Private Version) Trịnh Công Duy the image to the empty form fmViewPng. int ch.write(ch).toByteArray(). } finally { // Clean up if (iStrm != null) iStrm. Image im = null.openInputStream(url). imageData.read()) != -1) bStrm.append( ii). try { ByteArrayOutputStream bStrm = new ByteArrayOutputStream(). } 141 .

hình ảnh sẽ được hiển thị VII. Hỗ trợ giao thức HTTP trong MIDP Đến đây thì bạn dã biết được rằng GCF hỗ trợ nhiều kiểu kết nối và đã phát triển ứng dụng MIDlet tải về và hiển thị hình ảnh. } } Một textbox sẽ cho phép nhập địa chỉ URL Sau khi tải về.2.Mobile Programming (Private Version) Trịnh Công Duy return (im == null ? null : im). hãy cập nhật lại sơ đồ quan hệ giữa các interfaces trong GCF để thấy được vị trí của http: 142 . Trong phần này. sẽ bàn sâu hơn về sự hỗ trợ HTTP của GCF. Đầu tiên.

Máy client gửi request. Ví dụ về việc mở HTTP Connection thông qua GET String url = "http://www. thay vào đó header chỉ request những meta information về server. POST.Mobile Programming (Private Version) Trịnh Công Duy Đặc tả của phiên bản MIDP 1. còn POST sử dụng những stream riêng biệt nên sẽ khắc phục được hạn chế này. Với Post dữ liệu gửi từ client sẽ được phân thành các stream riêng biệt. HEADER. Trong khi đó. cung cấp khả năng bảo mật tốt hơn. Khi sử dụng Get. Có 3 phương thức được cung cấp sẵn là GET. a) Request and response protocols Cả HTTP và HTTPS đều gửi request và response. Client request bao gồm 3 phần sau:    Request method Header Body Request method định nghĩa cách mà dữ liệu sẽ được gửi đến server. GET và POST là hai phương thức request khá giống nhau. Các hàm API được khai báo trong HttpConnection (cho HTTP) và trong HttpConnections (cho HTTP và HTTPS). tuy nhiên do GET gửi dữ liệu thông qua URL nên sẽ bị giới hạn. dữ liệu cần request sẽ nằm trong URL.corej2me.0 chỉ hỗ trợ HTTP. còn server sẽ trả về response. trong khi đó phiên bản hiện tại MIDP 2. Header sẽ không gửi dữ liệu yêu cầu lên server.0 hỗ trợ HTTP và HTTPS.c om?size=large". HttpConnection http = 143 .

setRequestMethod(HttpConnection. http. // Set header field as key-value pair http. server cũng gồm 3 phần sau:    Status line Header Body Status line sẽ thông báo cho client kết quả của request mà client gửi cho server.open(url).com \somefile. } os.setRequestProperty("If -Modified-Since". http = (HttpConnection) Connector. Body chứa nội dung mà bạn muốn gửi lên server. http = (HttpConnection) Connector. HTTP phân loại status line thành các nhóm sau đây:  1xx is informational  2xx is success  3xx is redirection 144 . "Sat.getBytes().openOutputStream(). Những Header field sẽ cho phép ta truyền các tham số từ client đến server. // Send client body ostrm = http. OutputStream ostrm = null. Bạn có thể đặt các Field này thông qua phương thức setRequestProperty(). http. http.corej2me. http = (HttpConnection) Connector.GET).flush().open(url).setRequestMethod(HttpConnection. i < bytes.length. Dưới đây là ví dụ dụng setRequestProperty(). i++) { os. HttpConnection http = null. byte bytes[] = tmp. and User Agent. 1 Jan 2005 12:00:00 GMT"). server sẽ đóng gói và gửi về phía client. Các header field thường dùng là If-Modified-Since. for(int i = 0. Cũng như client request.com". tmp = "test data here". POST).corej2me. chỉ có những dữ liệu thay đổi sau ngày 1 tháng 1 năm 2005 mới được gửi về từ server: String url = "http://www.GET). Ví dụ về sử dụng POST và gửi dữ liệu từ client thông qua stream: String url = "http://www. HttpConnection http = null.open(url).setRequestMethod(HttpConnection.write(bytes[i]).txt".Mobile Programming (Private Version) Trịnh Công Duy null. Accept. Sau khi nhận được và sử lý yêu cầu từ phía client.

Mobile Programming (Private Version) Trịnh Công Duy 4xx is client error  5xx is server error Status line bao gồm version của HTTP trên server. Trong trường hợp này. phương thức đầu tiên sẽ cho lấy header field thông qua index của nó. ta sẽ sử dụng HttpConnection API để thiết lập kết nối trong MIDP.1 200 O K " "H TTP /1 . Không giống như header của client. //Get header field value looking up by name String getHeaderFieldKey(int n). b) The HttpConnection API Như đã đề cập ở trên. Còn phương thức thứ hai lấy nội dung header field dựa vào tên của header field. và đoạn text đại diện cho status code. server gửi hầu hết những thông tin trong phần body cho client. status code.getHeaderField(0) http. trong trường hợp server gửi về chuỗi "content-type=text/plain".getHeaderField("contenttype") http. có thể dùng phương thức thứ 3 ở trên. //Get header field value looking up by index String getHeaderField(String name). Phương thức http. Dưới đây là những API trong HttpConnection: 145 .getHeaderFieldKey(0) Giá trị trả về "text-plain" "text-plain" "content-type" Body: Cũng giống như client.1 400 B ad R equest" "H TTP /1 . server có thể gửi data thông qua header.1 500 Interna l S erver E rror"  Header. //Get header field key using index Server có thể trả về nhiều Header field. Còn nếu muốn biết tên (key) của header field. Ví dụ: "H TTP /1 . Sau đây là những phương thức dùng để lấy thông tin Header mà server gửi về: String getHeaderField(int n). Sau đây là ví dụ về 3 phương thức trên. Client dùng input stream để đọc kết quả trả về từ server.

import javax.Mobile Programming (Private Version) Trịnh Công Duy Phương thức long getDate() long getExpiration() String getFile() int getHeaderField(int n) String getHeaderField(String name) long getHeaderFieldDate(String name. POST or HEAD) Gets the current setting of a request property Gets the response code (numeric value) Gets the response message (text value) Gets the entire URL Sets the request method (GET.microedition. import java.io.*. String value) Mô tả Get header field date Gets header field expiration Gets filename from the URL Gets header field value looking up by index Gets header field value looking up by name Gets named field as a long (representing the date) Gets named field as an integer Gets header field key using index Gets host from the URL Gets last-modified field value Gets port from the URL Gets protocol from the URL Gets the query string (only valid with GET request) Gets the reference portion of URL Gets the current setting of the request method (GET.lcdui..java *-------------------------------------------------*/ import javax. MIDlet cũng sử dụng một số phương thức trong HttpConnection để thu thập thông tin về server: port.*.midlet.*.*. import javax. POST or HEAD) Sets a request property (header information) Ví dụ: Chúng ta sẽ tạo một MIDlet sử dụng HttpConnection để tải về và hiển thị nội dung của file text.io. Qua ví dụ bạn sẽ hiểu cách client gửi request và lấy response của server. /*-------------------------------------------------* FileViewer. content type . 146 . int def) String getHeaderFieldKey(int n) String getHost() long getLastModified() String getPort() String getProtocol() String getQuery() String getRef() String getRequestMethod() String getRequestProperty(String key) int getResponseCode() String getResponseMessage() String getURL() void setRequestMethod(String method) void setRequestProperty(String key.microedition. .microedition. long def) int getHeaderFieldInt(String name.

fmMain.setCurrent(fmMain).addCommand(cmExit). 1).getDisplay(this).EXIT. private Command cmView. 2).SCREEN. fmMain. private Form fmMain. // Create the form and add commands fmMain = new Form("File Viewer"). public FileViewer() { display = Display. } /*-------------------------------------------------* Process events *-------------------------------------------------*/ public void commandAction(Command c. // Create exitcommand cmExit = new Command("Exit". notifyDestroyed().Mobile Programming (Private Version) Trịnh Công Duy public class FileViewer extends MIDlet implements CommandListener { private Display display. fmMain. Command. private Command cmExit.addCommand(cmView). private String url = "http://localhost/text.txt". } public void startApp() { display.setCommandListener(this). Displayable s) { // If the Command button pressed was "Exit" if (c == cmExit) { destroyApp(false). Command. } else if (c == cmView) { // Download image and place on the form try { 147 . cmView = new Command("View".

// 3) Send body/data . } } catch (Exception e) { System. } } } /*-------------------------------------------------* Read file *-------------------------------------------------*/ private String readFile() throws IOException { HttpConnection http = null. String str = null.append("" + str).0 Configuration/CLDC-1.setRequestMethod( HttpConnection. //---------------// Client Request //---------------// 1) Send request method http. i--) fmMain.setRequestProperty("User-Agent".size(). // Append downloaded string fmMain. try { // Create the connection http = (HttpConnection) Connector. InputStream iStrm = null.No data for this request //---------------// Server Response //---------------148 .err.println("Msg: " + e.open(url). i > 0.GET).delete(0).0").Mobile Programming (Private Version) Trịnh Công Duy String str.toString()). // 2) Send header information (this header is optional) http. if ((str = readFile()) != null) { // Delete form contents for (int i = fmMain. "Profile/MIDP-1.

HTTP_OK) { System. System.println("value (field) 2: " + http.out. if (length != -1) { // Read data in one chunk byte serverData[] = new byte[length]. // 2) Get header information if (http.read()) != -1) bStrm. while ((ch = iStrm..println("key 1 : " + http. { ByteArrayOutputStream bStrm = new ByteArrayOutputStream(). str = new String(bStrm..out.getResponseCode() == HttpConnection.out.getHeaderFieldKey(0)).println( "value (field) 1: " + http.println("url: " + url).println("key 2: " + http.getLength().out. } else // Length not available.out. str = new String(serverData).println("Msg: " + http.out.println("-------------------------"). // Read data one character at a time int ch.getHeaderFieldKey(1)).out.getHeaderField(1)). } 149 = .out. System. System. System.close().getHeaderField(0)). System.read(serverData).out. System.out.println( "------------------------").println("Code: " + http.Mobile Programming (Private Version) Trịnh Công Duy System. System.write(ch). int length (int) http.out.out. System. bStrm.getResponseMessage()). System. System.out. iStrm.println("key 0: " + http.toByteArray()).println("-------------------------").openInputStream().println("value (field) 0: " + http. // 1) Get status Line System.getHeaderField(2)). // 3) Get data (show the file contents) iStrm = http.getHeaderFieldKey(2)).getResponseCode()).println( "------------------------").

close().getType()).close().o ut.println("Host: " + http. } } finally { // Clean up if (iStrm != null) iStrm.out. } return str.Mobile Programming (Private Version) Trịnh Công Duy //----------------------.println( "Type: " + http.-----// Show connection information //----------------------. System. System. if (http != null) http.----System. } public void pauseApp() { } public void destroyApp(boolean unconditional) { } } Output 150 .println("Port: " + http.out.getHost()).getPort()).

Mobile Programming (Private Version) Trịnh Công Duy Console output 151 .

do ta đã truyền tham số “zzz”. Accessing a Java servlet Ví dụ dưới đây sẽ chỉ cho bạn từng bước cách truy truy xuất java servlet. Servlet sẽ trả về kết quả. Đây là kết quả cho chuỗi: “yyyy. ta thấy ở phần kết quả có time zones là ICT. Bước đầu. nếu chuyển thành “zzzz” để lấy full text của time zones thì kết quả sẽ thay đổi như sau: Gọi Servlet Yêu cầu gọi servlet sẽ đuợc khai báo bên trong commandAction(). và sẽ được thực hiện khi button cmRqst được kích hoạt: 152 .3.Mobile Programming (Private Version) Trịnh Công Duy VII. và sẽ hiện lên màn hình.dd+'at'+hh:mm:ss+zzz" Trong hình trên.MM. client sẽ truyền tham số cho servlet yêu cầu trả về ngày tháng theo định dạng.

} catch (Exception e) { System.yyyy+'-'+hh:mm+aa". Displayable s) { if (c == cmRqst) { try { serverMsg = null. if (serverMsg != null) fmMain.dd+'at'+hh:mm:ss+zzz". chú ý rằng request của client sử dụng request m ethod là GET.println("Msg: " + e.err. } } Dưới đây là đoạn code cho phương thức callServlet().open(url).Data is passed at the end of url for GET String url = "http://localhost:8080/j2me/dateformatservlet?format= MMMM.append(serverMsg).: /*-------------------------------------------------* Call the servlet *-------------------------------------------------*/ private void callServlet() throws IOException { HttpConnection http = null. String url= "http://localhost:8080/j2me/dateformatservlet?format=yyyy.dd. InputStream iStrm = null.toString()). callServlet(). notifyDestroyed(). //---------------- 153 . try { http = (HttpConnection) Connector. } } else if (c == cmExit) { destroyApp(false).Mobile Programming (Private Version) Trịnh Công Duy /*-------------------------------------------------* Call the servlet *-------------------------------------------------*/ public void commandAction(Command c. boolean ret = false. // Examples .M M.

close(). } // Process request failed.*. // Three steps are processed in this method call ret = processServerResponse(ht tp.SimpleDateFormat. } public void doGet(HttpServletRequest request. sau đó servlet sẽ gọi SimpleDateFormat để tạo ra kết quả phù hợp với request: import import import import import import javax.servlet.*.http. IOException { 154 . if (http != null) http. java. } finally { // Clean up if (iStrm != null) iStrm.setRequestMethod(HttpConnection.GET). iStrm). java.servlet.openInputStream(). // 2) Send header information . java.close(). public class DateFormatServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config).text.*.none // 3) Send body/data . import javax. hầu hết các phương thức đều được biết trong doPost().Locale. java.data is at the end of URL //---------------// Server Response //---------------iStrm = http. biến format dùng để lưu request từ client.io.TimeZone. } Code cho servlet.Date.Mobile Programming (Private Version) Trịnh Công Duy // Client Request //---------------// 1) Send request method http.util. java. show alert if (ret == false) showAlert(errorMsg).util. HttpServletResponse response) throws ServletException.util.

format(dt)). Date dt = new Date(). } else // Length not available. } } Để MIDlet nhận kết quả trả về từ servlet. trước hết phải kiểm tra status line với server và có kết trả về kết quả hay không (HTTP_OK). response). out. } public String getServletInfo() { return "DateFormatServlet". 155 .. InputStream iStrm) throws IOException { //Reset error message errorMsg = null. } public void doPost(HttpServletRequest request. String str.println(simpleDate.Mobile Programming (Private Version) Trịnh Công Duy doPost(request.read(servletData).none // 3) Get body (data) int length = (int) http.getResponseCode() == HttpConnection.getWriter().getParameter("format").close().getLength(). str = new String(servletData). // 1) Get status Line if (http.setContentType("text /html"). response. if (length != -1) { byte servletData[] = new byte[length]. PrintWriter out = response. iStrm. Do không có thông tin header nên sẽ tiến hành lấy kết quả từ input stream: /*-------------------------------------------------* Process a response from a server *-------------------------------------------------*/ private boolean processServerResponse(HttpConnection http. SimpleDateFormat simpleDate = new SimpleDateFormat(format).HTTP_OK) { // 2) Get header information . out.. IOException { String format = request. HttpServletResponse response) throws ServletException.

write(ch). return true.getResponseMessage()). } // Save the server message serverMsg = str.close().Mobile Programming (Private Version) Trịnh Công Duy { ByteArrayOutputStream bStrm = new ByteArrayOutputStream(). } 156 . while ((ch = iStrm.read()) != -1) bStrm. int ch. return false. bStrm. } else // Use message from the servlet errorMsg = new String( http. str = new String(bStrm.toByteArray()).

microedition.lang.security.microedition Quản lý việc xuất nhập của hệ thống Các lớp Java cơ bản Các lớp hỗ trợ việc tính thời gian.ref java. quản lý tập hợp v. Phụ lục Tổng số class và interface trong CLDC Bảng 1: Tổng số class và interface trong CLDC Tên gói Miêu tả java.math java.text java.v Số lượng class và interface 62 77 5 12 1 23 36 4 13 47 157 .io java.Mobile Programming (Private Version) Trịnh Công Duy VIII.cert java.net java.midlet Giao diện trung gian giữa Midlet và môi trường Midlet đang chạy javax.io Hỗ trợ HTTP javax. quản lý tập hợp v.v Quản lý kết nối Số lượng class và interface 18 38 10 10 Bảng 2: Các lớp bổ sung trong MIDP 1.lang java.lang java.microedition.util javax.lcdui Hỗ trợ giao diện người dùng Số lượng class và interface 10 2 1 24 Bảng 3: Tổng số class và interface trong CDC Tên gói Miêu tả java.reflect java.rms Quản lý việc lưu trữ dữ liệu javax.io java.0 Tên gói Miêu tả javax.microedition. VIII.microedition.util Quản lý việc xuất nhập của hệ thống Các lớp Java cơ bản Các lớp hỗ trợ bộ thu gom “rác” của JVM Hỗ trợ tính năng reflection trong Java Hỗ trợ các phép tính trong toán học Hỗ trợ mạng Cung cấp các tính năng bảo mật Hỗ trợ các chứng thực trong bảo mật Cung cấp các lớp hỗ trợ thao tác trên text Các lớp hỗ trợ việc tính thời gian.security java.lang.1.

util.1 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.zip javax.0 MIDP-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.2.microedition Hỗ trợ thao tác trên file Jar Hỗ trợ thao tác trên file Zip Quản lý kết nối 7 9 10 VIII.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy java.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 .0 CLDC-1.0 MIDP-1.0 MIDP-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.util.1 CLDC-1.0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 1 4 0 0 0 6 15 1 1 0 6 1 1 0 2 0 0 1 0 0 0 0 158 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2. LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG Thông tin của các dòng điện thoại thông dụng A7110 A7150 B2000 B2050 B2100 B2150 C1100 C1150 C1300 C1300i C1500 C2000 C2200 C2500 C3100 C3300 C3310 C3320 C3380 C3400 CE110 CE500 CG225 CG300 CU320 CU320 Profile CU400 CU500 CU575 CU720 F2100 F2400 F3000 F7200 F7250 F9100 G1600 G1610 G4015 G4020 G4050 G5300i G5400 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.jar java.

0 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.Mobile Programming (Private Version) Trịnh Công Duy LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG G5500 G5600 G7050 G7070 G7100 G7120 HB620T KE800 KE820 KE850 KE970 KF510 KF600 KF600d KF700 KF700-Orange KF750 KG195 KG200j KG220 KG245 KG290 KG300j KG320 KG800 KG920 KP230-V10a KP235-orange KP320 KT520 KU250 KU311 KU380 KU730 KU800 KU950 KU990 KU990-Orange L1100 L1150 L1400 L1400i L3100 L600V LX150 LX160 LX260 LX350 LX550 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 0 0 0 0 0 0 0 0 0 0 4 2 4 1 0 0 0 2 3 0 0 0 3 0 2 0 1 1 0 0 4 1 1 1 3 2 1 2 1 0 2 1 0 2 0 3 0 6 8 159 .1 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 MIDP-2.0 CLDC-1.0 MIDP-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.

0 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 2 1 2 1 0 0 0 0 8 6 8 5 0 2 5 1 3 2 0 5 0 2 1 1 0 0 0 2 160 .1 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.Mobile Programming (Private Version) Trịnh Công Duy LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG LG Mitsubishi Motorola LX570 M4410 M6100 ME500c ME770 MG100a MG105 MG130 MG200 MG200c MG200d MG210 MG220 MG295 MG610c MG810C P7200 S5200 T5100 TU515 TU915 U250 U300 U310 U400 U450 U8100 U8100 UP.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 CLDC-1.1 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.Link U8110 U8120 U8130 U8138 U8150 U8180 U8210 U8290 U830 U8330 U8360 U8380 U8500-V10 U8550 U880 U890 U900 U960 U970 M900 A-5B MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.

1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.1 161 .0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 11 5 1 4 2 1 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1. Accept-language:ru C261 C380 C381p C385 C390 C450 C550 C650 C975 C980M Canary E1 iTunes E1 ROKR E1000 E1000M E1060 E1070 E1070R E2 E380 E398 E398B E550 E770 E770v E790 E825 K1 K3 L2 L6 L6i L7 L71 L7i L9 M930 MOTORAZRmaxxV3 MIDP-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola A1000 A1200 A1200r A630 A768 A780 A835 A845 A925.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.

0 MIDP-2.1 CLDC-1.1.0 1 0 1 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 4 2 0 1 0 0 0 0 0 0 6 0 0 2 1 0 0 0 162 .1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 VSCL-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola MOTORAZRV9 MOTOROKR E6 MOTOROKRE6 MPx220 MPx230 PEBL U6 RAZRV3x RAZRV3xv RAZRV3xx RAZRV6 RAZRV6v Sirius T720 U15 V1050 V1075 V180 V180ENS V185 V186 V186Q V188 V190 V195 V220 V220ENS V235 V280 V3 V300 V330 V360 V360i V360v V361 V365 V3i V3i_iTunes V3r V3t V3v V400 V500 V505 V525M V535 MIDP-2.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 VSCL-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 VSCL-1.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.

0 MIDP-1.1 MIDP-1.0 CLDC-13 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Motorola Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia V540 V545 V547 V550 V551 V551J V555 V600 V620 V635 V66M V80 V975 V980 V980M W490 W510 W510-orange Z3 2600c 2610 2626 2630 2650 2760 3100 3100b 3108 3110c 3120 3120b 3125 3155i 3200 3220 3230 3250 3300 3410 3500c 3510i 3555b 3560 3590 3595 3620 3650 3660 5100 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-1.0 CLDC-1.0 MIDP-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.1 MIDP-1.0 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 MIDP-1.0 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-1.1 MIDP-2.0 MIDP-2.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 4 6 2 19 2 34 9 0 0 14 0 0 0 22 45 11 8 0 1 0 77 0 0 0 4 0 17 4 7 163 .0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.

0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.Browser 6230i 6233 6234 6235 6255 6260 6263 6265 6265i 6267 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 20 2 2 0 2 1 0 0 4 30 8 21 1 1 1 6 1 1 0 49 9 1 1 0 1 0 0 0 0 0 0 0 0 0 10 2 4 238 0 114 0 0 2 5 2 0 0 0 1 164 .0 MIDP-2.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 MIDP-2.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia 5140 5140i 5200 5300 5310XpressMusic 5500d 5610 XpressMusic 5610d-1 6010 6020 6021 6030 6030b 6060 6061 6070 6080 6085 6086 6100 6101 6102 6102i 6103 6108 6111 6125 6126 6131 6131NFC 6133 6136 6151 6165i 6170 6200 6220 6230 6230 UP.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 MIDP-1.

0 CLDC-1.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 0 0 3 2 0 1 1 8 1 1 0 1 35 11 26 5 38 0 6 32 8 2 2 3 1 3 14 9 9 3 5 5 0 0 0 3 13 0 4 3 1 0 0 0 3 2 0 1 165 .0417.0 MIDP-2.0 MIDP-2.0) SymbianOS 7650 7710 8800 8800SI 8801 8910i 9300 9500 E50-1 E50-2 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.Mobile Programming (Private Version) Trịnh Công Duy Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia 6270 6275 6275i 6280 6282 6288 6300 6310i 6500c 6500s-1 6555 6555b 6600 6610 6610I 6620 6630 6650 6670 6680 6681 6682 6800 6820 6822 7200 7210 7250 7250I 7260 7270 7360 7370 7373 7390 7600 7610 7610 (3.0 MIDP-2.0 MIDP-2.1 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 MIDP-2.0 MIDP-2.1 CLDC-1.1 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.0 MIDP-1.0 CLDC-1.1 MIDP-2.0 MIDP-2.0 CLDC-1.

0 MIDP-2.0 CLDC-1.1 CLDC-1.Browser N71-1 N72 N73-1 N73-2 N75-3 N80-1 N90-1 N91 N91-1 N93-1 N93i-1 Xda 2mini Xda II SAMSUNG SGHX620 SGH-A412 SGH-A436 SGH-A437 SGH-A517 SGH-A701 SGH-A706 SGH-A707 SGH-A717 SGH-A727 SGH-A737 SGH-A801 SGH-B500 SGH-C100 SGH-C110 SGH-C130 SGH-C200 SGH-C207L SGH-C210 SGH-C218 SGH-C225 SGH-C230 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 0 1 0 1 0 0 1 3 4 1 70 0 0 1 1 0 0 0 8 0 0 0 0 0 0 0 2 1 0 0 2 1 2 0 0 1 0 0 3 0 0 0 0 0 0 0 0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia Nokia O2 O2 Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung E60-1 E61-1 E61i-1 E62-1 E65-1 E65-1 Orange E70-1 N-Gage N-GageQD N70 N70-1 N70-1 UP.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 166 .0 MIDP-2.

0 SGH-E217 MIDP-2.0 SGH-D820 MIDP-1.0 SGH-E300 SGH-E310 CLDC-1.1 CLDC-1.0 SGH-D807 MIDP-2.1 CLDC-1.0 SGH-D600 MIDP-1.0 MIDP-2.1 0 0 0 1 0 4 2 2 2 0 16 0 5 0 4 19 17 0 0 0 4 3 5 6 0 1 12 0 3 0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 SGH-D900 MIDP-1.0 SGH-D840 MIDP-2.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-C240 SGH-C300 SGH-C417 SGH-C417 Profile SGH-D100 SGH-D307 SGH-D347 SGH-D357 SGH-D407 SGH-D410 SGH-D500 MIDP-2.1 3 0 0 26 5 0 0 0 167 .0 SGH-E250-ORANGE MIDP-1.0 SGH-D500C MIDP-2.0 SGH-D510 MIDP-2.1 CLDC-1.0 SGH-D730 MIDP-2.1 CLDC-1.0 SGH-E210 MIDP-2.0 SGH-D900i MIDP-1.0 SGH-E250 MIDP-1.0 MIDP-2.0 MIDP-1.0 SGH-D780 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 SGH-D900-ORANGE MIDP-2.0 MIDP-2.1 CLDC-1.0 SGH-D520 MIDP-2.0 MIDP-1.1 CLDC-1.0 SGH-D600E MIDP-1.0 SGH-D800 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 SGH-E250-VODA MIDP-2.1 CLDC-1.0 SGH-D836 MIDP-2.0 MIDP-2.0 SGH-E100 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 SGH-E200 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 SGH-D720 MIDP-2.0 MIDP-2.0 CLDC-1.0 SGH-D830 MIDP-2.0 SGH-D500E MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.

0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-E315 SGH-E316 SGH-E317 SGH-E330 SGH-E330N SGH-E335 SGH-E338 SGH-E340 SGH-E350 SGH-E350E SGH-E360 SGH-E370 SGH-E380 SGH-E390 SGH-E420 SGH-E480 SGH-E490 SGH-E500 SGH-E530 SGH-E570 SGH-E590 SGH-E600 SGH-E610 SGH-E610C SGH-E620 SGH-E630 SGH-E635 SGH-E700 SGH-E700A SGH-E710 SGH-E715 SGH-E720 SGH-E720C SGH-E728 SGH-E730 SGH-E736 SGH-E738 SGH-E758 SGH-E760 SGH-E770 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 CLDC-1.1 0 0 1 1 0 0 0 0 5 20 17 4 2 2 0 9 1 0 4 1 1 0 0 0 2 0 0 0 0 0 0 4 0 0 1 0 0 0 0 3 MIDP-2.1 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 168 .1 CLDC-1.1 CLDC-1.

0 MIDP-2.0 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 SGH-F700 SGH-F700-Vodafone SGH-G600 SGH-G800 SGH-I300 SGH-I320 SGH-i600 SGH-I607 SGH-i640V SGH-I700 SGH-i710 SGH-i780 SGH-i780ORANGE SGH-i900 SGH-J200 SGH-J600 SGH-J600E SGH-J620 SGH-J700 SGH-J700-ORANGE SGH-J750 SGH-J750BNGI4 SGH-L600 MIDP-2.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 169 .0 MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-E780 SGH-E790 SGH-E800 SGH-E800N SGH-E810 SGH-E820 SGH-E840 SGH-E850 SGH-E860V SGH-E870 SGH-E880 SGH-E900 SGH-E950 SGH-F110 SGH-F330-TEN SGH-F400 SGH-F480 SGH-F480-Vodafone SGH-F490 SGH-F490-ORANGE SGH-F490-Vodafone SGH-F510/1.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 MIDP-2.1 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 1 1 0 0 0 1 2 0 0 0 3 24 1 0 0 1 0 1 1 0 0 0 0 4 2 3 0 0 0 0 0 0 0 2 1 0 3 7 1 0 2 1 1 0 0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.

0 MIDP-2.1 CLDC-1.0 SGH-T429 MIDP-2.1 CLDC-1.0 MIDP-2.0 SGH-T309 SGH-T319 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 SGH-P730 SGH-P730C SGH-P735 SGH-P777 SGH-P910 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 SGH-T629 MIDP-2.1 CLDC-1.0 SGH-T719 SGH-T729 MIDP-2.1 CLDC-1.0 CLDC-1.0 SGH-T219 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-L760 SGH-L760-Vodafone SGH-L770-Vodafone SGH-M110 SGH-M300 SGH-M600 SGH-P180-ORANGE SGH-P200-ORANGE SGH-P207 SGH-P220-ORANGE MIDP-2.0 CLDC-1.0 MIDP-2.0 SGH-T639 MIDP-2.1 CLDC-1.1 1 2 1 0 0 2 0 0 2 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 CLDC-1.0 SGH-P920 MIDP-2.1 CLDC-1.0 SGH-T609 SGH-T619 MIDP-2.1 CLDC-1.0 SGH-T629R MIDP-2.0 SGH-P300 MIDP-2.0 SGH-P400 SGH-P510 SGH-P520 MIDP-2.0 MIDP-2.1 CLDC-1.0 SGH-T439 MIDP-2.0 SGH-T809 MIDP-1.1 CLDC-1.0 SGH-T409 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 SGH-S105 SGH-S200 SGH-S300 SGH-S300M SGH-T209 MIDP-2.1 170 .0 SGH-P260-ORANGE MIDP-1.0 SGH-T329 MIDP-2.0 SGH-T509 SGH-T509S SGH-T519 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 SGH-P310 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 SGH-T819 MIDP-2.0 MIDP-2.0 CLDC-1.0 SGH-T539 MIDP-2.

0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 1 0 14 4 1 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 3 1 13 MIDP-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 MIDP-2.0 171 .1 CLDC-1.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 CLDC-1.1 MIDP-2.1 CLDC-1.0 MIDP-2.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-T999 SGH-U100 SGH-U600 SGH-U700 SGH-U700-Vodafone SGH-U900 SGH-U900-Vodafone SGH-X100 SGH-X105 SGH-X120 SGH-X140 SGH-X160 SGH-X166 SGH-X200 SGH-X210 SGH-X300 SGH-X400 SGH-X426 SGH-X426M SGH-X427M SGH-X430 SGH-X450 SGH-X460 SGH-X466 SGH-X475 SGH-X480 SGH-X480C SGH-X486 SGH-X488 SGH-X495 SGH-X497 SGH-X500 SGH-X507 SGH-X510 SGH-X520X SGH-X526 SGH-X530 SGH-X600 SGH-X620 SGH-X628 SGH-X630 SGH-X636 SGH-X640 SGH-X650 SGH-X660 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.

Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung SGH-X670 SGH-X680 SGH-X700 SGH-X800 SGH-X808 SGH-X810 SGH-X820 SGH-Z107 SGH-Z110 SGH-Z130/1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 SGH-Z400 SGH-Z400-Vodafone SGH-Z500 SGH-Z500/1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 172 .1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 SGH-Z540 SGH-Z548 SGH-Z560 SGH-Z560-Vodafone SGH-Z630/1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 SGH-Z308 SGH-Z310 SGH-Z360 SGH-Z370/1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 SGH-Z720 SGH-Z720-Vodafone SGH-ZV10 SGH-ZV40-Vodafone SGH-ZV50-Vodafone SGH-ZV60-Vodafone SGH-ZX10 SGH-ZX20 SPH-A640 SPH-A660 SPH-A680 SPH-A700 SPH-A740 SPH-A800 SPH-A840 SPH-A860 SPH-A880 MIDP-2.0 MIDP-2.0 SGH-Z140 SGH-Z150 SGH-Z170 SGH-Z230 SGH-Z240 SGH-Z300 SGH-Z300/1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 SPRINTPCS-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 0 0 11 0 0 1 2 1 0 0 17 0 3 4 3 0 16 2 0 0 0 2 0 21 16 1 11 1 1 2 0 0 1 3 0 1 0 0 1 2 2 0 4 9 1 0 0 2 MIDP-2.0 SGH-Z510/1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 SPRINTPCS-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.1 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-2.

0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.1 null CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 SPRINTPCS-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.0 null MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 CLDC-1.0 MIDP-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Samsung Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens SPH-A900 SPH-A900P SPH-A920 SPH-A940 SPH-M300 SPH-M500 SPH-M510 SPH-M610 SPH-M620 Z105 A31 A65 AF51 AL21 AX72 AX75 C110 C55 C60 C65 C66 C6V C72 C75 C81 CF62 CF75 CFX65 CL75 CX65 CX65i CX6V CX70 CX70I CX75 CX7A CX7I CX7V E71 EF81 EL71 M50 M55 M55C M65 M6V M75 M81 MC60 MIDP-2.0 26 5 7 5 1 4 0 3 1 0 1 1 0 0 0 0 0 2 0 42 1 0 5 20 0 0 4 0 0 48 0 0 58 0 36 0 1 0 0 14 1 0 2 0 14 0 16 0 3 173 .1 CLDC-1.1 CLDC-1.1 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.

0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 2 0 3 0 0 0 0 2 1 0 1 0 0 12 1 1 1 40 1 4 78 3 5 0 174 .0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.1 MIDP-2.0 MIDP-2.Mobile Programming (Private Version) Trịnh Công Duy Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens Siemens SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son MC6C ME75 S55 S65 S66 S68 S6C S6V S75 SF65 ObigoInternetBrowser SK65 SK6R SL65 SL6C SL75 SP65 ST60 SX1 SXG75 SXG75 UP.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.Browser C702 C902 D750i F500i G700 G700c J300a J300i K123i K300a K300i K310i K320i K500c MIDP-1.0 MIDP-1.0 MIDP-2.0 MIDP-1.1 0 7 3 43 0 0 0 2 3 0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 MIDP-1.1 CLDC-1.1 CLDC-1.

0 6 175 .0 MIDP-2.0 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics K500i K508i K510c K510i K530i K550i K600 K600i K608i K610i K660i K700i K750i K770i K790a K790i K800i K800iv K810i K850i M600i MODEL P1i P200 P800 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-1.0 MIDP-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.1 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-1.0 MIDP-1.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 MIDP-2.1 MIDP-1.1 CLDC-1.1 CLDC-1.1 38 3 1 11 2 15 0 9 2 66 1 157 10 5 6 8 119 1 21 7 3 0 2 0 MIDP-1.0 MIDP-1.

0 MIDP-1.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-2.0 MIDP-1.1 0 0 9 28 15 0 0 0 9 176 .0 MIDP-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 MIDP-1.0 MIDP-1.0 MIDP-1.Mobile Programming (Private Version) Trịnh Công Duy son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son P900 P910 P910a P910i P990i S500i S700c S700i S710a T610 T616 T630 T637 T650i T68 V600i V660i V800 W200i W300i W380a W380i W550i W580i MIDP-1.0 MIDP-1.0 CLDC-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.1 CLDC-1.0 CLDC-1.1 CLDC-1.1 24 1 3 28 3 4 2 25 5 12 1 25 4 1 0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-1.1 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.1 CLDC-1.0 CLDC-1.0 MIDP-1.0 MIDP-1.0 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.0 CLDC-1.0 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.

1 CLDC-1.1 MIDP-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.1 CLDC-1.0 MIDP-1.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.0 CLDC-1.0 MIDP-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.Mobile Programming (Private Version) Trịnh Công Duy SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics W610i W660i W700i W710i W760i W800i W810i W830i W850i W880i W890i W910i W950i W960i Z1010 Z310a Z310i Z310iv Z320i Z500a Z500i Z520a Z520i Z530i Z550i MIDP-1.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.0 MIDP-2.1 CLDC-1.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-1.1 CLDC-1.0 MIDP-1.0 MIDP-2.1 MIDP-1.0 MIDP-1.1 6 8 3 0 0 6 41 0 16 15 4 3 7 1 13 0 2 0 0 11 0 0 2 7 5 177 .0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-1.0 MIDP-2.0 MIDP-2.0 MIDP-2.1 CLDC-1.1 CLDC-1.1 MIDP-2.0 MIDP-1.1 CLDC-1.

Tham khảo: http://java.jsp MIDP Specification (JSR-37) http://www.0 MIDP-2.3.jcp.org/jsr/detail/66.0 MIDP-2.org/jsr/detail/36.1 CLDC-1.pdf http://www.org/jsr/detail/135.jcp.edu/burge/pdf/huc/01_J2ME_Overview.1 CLDC-1.sun.0 MIDP-2.1 CLDC-1.jsp PDA Profile Specification (JSR-75) http://www.jcp.jsp RMI Profile Specification (JSR-66) http://www.jsp Foundation Profile Specification (JSR-46) http://www.0 MIDP-1.com/techtopics/mobility/midp/articles/wtoolkit/ CLDC Specification (JSR-30) http://www.jsp CDC Specification (JSR-36) http://www.org/jsr/detail/75.jcp.0 CLDC-1.org/jsr/detail/132.jsp Java Game Profile (JSR-134) http://www.com/pub/a/onjava/2001/03/08/J2ME.0 MIDP-1.jcp.jcp.jsp J2ME Multimedia API Specification (JSR-135) http://www.1 CLDC-1.org/jsr/detail/139.jsp Mobile Information Device Next Generation (JSR-118) http://www.1 CLDC-1.jcp.cs.jsp J2ME White Papers KVM http://java.Mobile Programming (Private Version) Trịnh Công Duy son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son SonyErics son Z555a Z600 Z610i Z710i Z750i Z770i Z800 MIDP-2.html http://developers.sun.jsp CLDC Next Generation (JSR-139) http://www.1 MIDP-2.0 CLDC-1.jcp.armstrong.0 MIDP-2.1 MIDP-1.org/jsr/detail/62.1 1 3 2 3 1 0 1 VIII.com/j2me/ http://www.jcp.onjava.jcp.org/jsr/detail/46.jcp.sun.org/jsr/detail/134.0 MIDP-1.org/jsr/detail/37.0 MIDP-2.org/jsr/detail/30.jsp Personal Profile Specification (JSR-62) http://www.0 MIDP-1.com/products/cldc/wp Applications for Mobile Devices 178 .

html RIM BlackBerry Java IDE http://developers.sun.com/products/j2mewtoolkit Metrowerks CodeWarrior for J2ME http://www.zucotto.su n.com/kvm-interest.com/products/cldc MIDP http://java.com/developer Java Wireless Developer Initiative http://java.metrowerks.com KVM-Interest Mailing List Archive http://archives.com/products/midp MIDP for Palm OS http://java.com/products/cdc J2ME Wireless Toolkit http://java.com/ CDC and CVM http://java.sun.wirelessdevnet.html XML Parsers for J2ME TinyXML Parser http://gibaradunn.sun.javamobiles.microjava.rim.com/whiteboard/index.net/tools/jde/index.java.java.org Java Enabled Phones and PDAs http://www.sun.sun.sun.Mobile Programming (Private Version) Trịnh Công Duy http://java.com/desktop/java Zucotto WHITEboard SDK http://www.sourceforge.com Java Mobile Community http://www.com Micro Java Network http://www.com/j2me/docs/pdf/midpwp.s rac.shtml J2ME Developer Resources Java Developer Connection http://developer.org/tiny NanoXML Parser http://nanoxml.javamobile.com/j2me CLDC and KVM http://java.sun.sun.kvmworld.pdf J2ME Technologi es J2ME http://java.com KVM World http://www.net 179 .com/wireless Wireless Developer Network http://www.sun.

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->