Tải bản đầy đủ (.pdf) (59 trang)

Nghiên cứu JM và xây dựng ứng dụng minh họa (Đặng Nguyễn Kim Anh vs Đào Anh Tuấn) - 3 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (988.71 KB, 59 trang )


Java Mobile
Thay thế cho hệ thống file, thiết bị MIDP lưu trữ thông tin qua các record stores.
Chúng ta có thể xem các record store này như một bảng dữ liệu gồm nhiều dòng
record. Mỗi dòng record có một số ID và gồm một mảng các bytes. Người ta
thường xem record store như một cấu trúc file phẳng (flat-file).

Record ID Data
1 Mảng các bytes
2 Mảng các bytes
3 Mảng các bytes
……………. ………………….
Bảng 6.1 Mô hình Record Store
Mỗi record có một trường Record ID có kiểu dữ liệu integer, đóng vai trò như
khóa chính trong bảng dữ liệu và một trường “Data” là một mảng các bytes để chứa
dữ liệu. Một tập hợp các record này được gọi là một record store (tương ứng với
một bảng dữ liệu). Một ứng dụng MIDlet có thể có bao nhiêu record store tùy ý (kể
cả không có), mỗi record store được phân biệt bởi tên định danh duy nhất. Mở rộng
ra, các Record Stores của các ứng dụng MIDlet cùng chung một bộ (suite) thì cũng
phải có tên phân biệt.
Tên của record store có thể chứa đến 32 ký tự Unicode (16 bits) và tên này phân
biệt hoa thường.
Các ứng dụng MIDlet không những truy xuất được các record stores do nó tạo ra
mà còn truy xuất được các record stores do các ứng dụng khác trong cùng một bộ
(MIDlet Suite) tạo ra. Đây là một lý do khiến các nhà phát triển đặt ra khái niệm
MIDlet Suite, tuy nhiên các MIDlet không cùng một suite không thể truy xuất
Record Suite của nhau. Đây là một hạn chế của Record Store khi so sánh với hệ
thống file trên các thiết bị khác.
Một record store quản lý hai thông số để quản lý việc truy cập dữ liệu, hai thông
số này tự động được cập nhật khi có record được thêm, xóa hay sửa:
107



Java Mobile
• Số Version : đây là một giá trị integer. Tuy nhiên, có một điều không may là
giá trị khởi điểm của version không được định nghĩa bởi API. Nếu chúng ta
cần biết giá trị ban đầu này, ngay sau khi tạo record mới ta có thể gọi hàm
getVersion().
• Ngày tháng: là một số long, thể hiện số millisecond tính từ nửa đêm ngày
1/1/1970. Chúng ta có thể lấy ra thông số này bằng hàm getLastModified().
Các yêu cầu xử lý record store được thiết bị đồng bộ hóa tự động: khi có hai hay
nhiều yêu cầu xử lý thì yêu cầu đến trước được xử lý trước và các yêu cầu xử lý
khác sẽ được đặt vào hàng đợi.
Các hàm xử lý Record Store thông dụng:
Lớp javax.microedition.rms.RecordStore
Phương thức Mô tả
Constructor: Không có phương thức khởi tạo
static RecordStore
openRecordStore(String
recordStoreName, boolean
createIfNeccessary)
Mở một record store, tùy chọn tạo mới
nếu record store chưa tồn tại.
void closeRecordStore() Đóng record store
static void deleteRecordStore(String
rcdStoreName)
Xóa record store
static String[] listRecordStore() Liệt kê các record stores trong MIDlet
Suite
int addRecord(byte[] data, int offset, int
numBytes)
Thêm một record

void setRecord(int recordID,byte[]
newData,int offset, int numBytes)
Thay đổi nội dung một record.
void deleteRecord(int RecordID) Xóa một record khỏi record store
byte[] getRecord(int RecordID) Lấy mảng byte chứa nội dung record
108

Java Mobile
int getRecord(int RecordID, byte[]
buffer, int offset)
Lấy nội dung record từ vị trí offset đưa
vào mảng byte.
int getRecordSize(int RecordID) Lấy kích thước của record
int getNextRecordID() Lấy ID của record tiếp theo
int getNumRecords() Lấy số lượng Record trong Record
Store
long getLastModified() Thời điểm thay đổi cuối cùng của
Record Store
int getVersion() Lấy version của Record Store
String getName() Lấy tên của Record Store
int getSize() Tổng số bytes được sử dụng bởi Record
Store
int getSizeAvailable() Lấy dung lượng còn lại (bytes) được
phép dùng bởi Record Store.
RecordEnumeration enumerateRecords
(RecordFilter filter, RecordComparator
comparator, boolean keepUpdate)
Bổ sung chức năng duyệt các record
dưới dạng một tập hợp. (sẽ được đề cập
sau)

void addRecordListener(RecordListener
listener)
Thêm một bộ lắng nghe trên Record
Store (sẽ được đề cập sau)
void removeRecordListener
(RecordListener listener)
Gỡ bỏ bộ nghe (sẽ được đề cập sau)
Bảng 6.2 Lớp RecordStore
Ghi chú: Record ID được tính bắt đầu từ 1 chứ không phải từ 0 như chỉ số mảng.
Sau đây là một ví dụ về việc nhập xuất dữ liệu thông qua record store bằng kỹ thuật
stream. Ví dụ đã được tinh giản và chỉ trình bày những phân đoạn chính.

//Import các thư viện cần thiết
import java.io.*;
import javax.microedition.midlet.*;
109

Java Mobile
import javax.microedition.rms.*;

public class ReadWriteStreams extends MIDlet {
private RecordStore rs = null; // Record Store
static final String REC_STORE = "db_1"; // Tên của Record Store
public ReadWriteStreams() {
openRecStore(); // Hàm tạo Record Store
writeTestData(); // Hàm ghi dữ liệu
readStream();
// Hàm đọc dữ liệu
closeRecStore(); // Đóng Record Store
deleteRecStore(); // Xóa Record Store

}

public void startApp() {
// Không có giao diện, chương trình sẽ ghi, đọc dữ liệu và thoát
destroyApp(false);
notifyDestroyed();
}
public void openRecStore() {
try {
// Tạo record store mới nếu chưa tồn tại
rs = RecordStore.openRecordStore(REC_STORE, true );
}
catch (Exception e) {
//Xuất thông báo lỗi
}
}
public void closeRecStore() {
try {
rs.closeRecordStore();
110

Java Mobile
}
catch (Exception e) {
//Xuất thông báo lỗi
}
}
public void deleteRecStore(){
if (RecordStore.listRecordStores() != null){
try{

RecordStore.deleteRecordStore(REC_STORE);
}
catch (Exception e){
//Xuất thông báo lỗi
}
}
}
/*
* Tạo 3 mảng để mô phỏng việc ghi dữ liệu
* */
public void writeTestData() {
String[] strings = {"String 1", "String 2"};
boolean[] booleans = {false, true};
int[] integers = {1 , 2};
writeStream(strings, booleans, integers);
}
/*
* Viết vào record store dùng stream
* */
public void writeStream(String[] sData, boolean[] bData, int[] iData) {
try{
111

Java Mobile
/* Tạo stream để viết dữ liệu, ở đây ta tạo ra 2 streams, một
stream có tác dụng như buffer, stream còn lại để ghi dữ liệu
vào Record*/

//Buffer
ByteArrayOutputStream strmBytes =

new ByteArrayOutputStream();

DataOutputStream strmDataType =
new DataOutputStream(strmBytes);

byte[] record;
//Ba mảng có kích thước bằng nhau
for (int i = 0; i < sData.length; i++) {
// Ghi dữ liệu
strmDataType.writeUTF(sData[i]);
strmDataType.writeBoolean(bData[i]);
strmDataType.writeInt(iData[i]);
strmDataType.flush();
// Biến dữ liệu trong stream thành dạng mảng để ghi vào
//record, vì các record chỉ chấp nhận dữ liệu dạng mảng
record = strmBytes.toByteArray();
rs.addRecord(record, 0, record.length);
// Xóa hết dữ liệu trong buffer để tiếp tục ghi các phần
//tử tiếp theo của mảng
strmBytes.reset();
}
//Sau khi hoàn tất việc ghi các mảng, đóng các stream
strmBytes.close();
strmDataType.close();
}
112

Java Mobile
catch (Exception e){
//Xuất các thông báo lỗi

}
}
/*
* Đọc lại dữ liệu đã được ghi
* */
public void readStream(){
try{
// Tạo một mảng có kích thước đủ lớn
// Trong thực tế nên kiểm tra và cấp phát lại kích thước nếu cần
byte[] recData = new byte[50];
// Tạo một stream làm buffer lấy dữ liệu từ mảng recData
ByteArrayInputStream strmBytes =
new ByteArrayInputStream(recData);
// Tạo stream để xuất dữ liệu theo đúng khuôn dạng đã ghi
DataInputStream strmDataType =
new DataInputStream(strmBytes);
for (int i = 1; i <= rs.getNumRecords(); i++){
// Lấy dữ liệu đưa vào mảng
rs.getRecord(i, recData, 0);
// Đọc lại theo đúng thứ tự đã ghi
System.out.println("Record #" + i);
System.out.println("UTF:" + strmDataType.readUTF());
System.out.println("Boolean: " +
strmDataType.readBoolean());
System.out.println("Int: " + strmDataType.readInt());
System.out.println(" ");
// Xóa dữ liệu buffer để bắt đầu ghi các phần tử tiếp theo
strmBytes.reset();
113


Java Mobile
}
strmBytes.close();
strmDataType.close();
}
catch (Exception e){
//Xuất các thông báo lỗi
}
}

}

Ví dụ trên đã nêu một cách cơ bản nhất các bước cần thiết để ghi dữ liệu và truy
xuất dữ liệu bằng record store. Ví dụ đã được tinh giản và chỉ nêu các hàm thật cần
thiết. Việc mở, ghi record, đọc record, đóng và xóa record được thể hiện thành từng
hàm riêng biệt để tiện theo dõi. Trong ví dụ trên có một số điểm ta cần chú ý:
Chỉ số RecordID bắt đầu từ 1 chứ không phải từ 0 như chỉ số của các phần tử trong
mảng. Nếu ta cố gắng truy xuất phần tử số 0 sẽ phát sinh lỗi.
Tương tự như các hàm truy xuất I/O khác của Java, các hàm liên quan đến Record
Store cần phải được đưa vào trong khối try – catch vì các hàm này có thể phát sinh
các exceptions.
Trong ví dụ trên chúng ta dùng stream để ghi và xuất dữ liệu có khuôn dạng; do đó
khi chúng ghi theo trình tự nào thì xuất cũng phải theo trình tự đó.

114

Java Mobile
6.2. Duyệt danh sách Record với RecordEnumeration

Ngay trên ví dụ vừa rồi, chúng ta duyệt qua danh sách các records trong record

store bằng dòng lặp. Đây là các tiếp cận được những lập trình viên nghĩ đến ban đầu
vì chúng ta đã nhận định các record như các dòng trong một bảng của CSDL. Tuy
nhiên, MIDP cung cấp cho chúng ta một công cụ thuận tiện và chính xác hơn để
duyệt qua các record trong một record store. Chúng ta đề cập đến khái niệm “chính
xác hơn” ở đây vì lý do khi duyệt bằng vòng lặp thực chất có thể gây nên một
exception trong chương trình. Giả sử chúng ta có 3 record đánh số từ 1 đến 3, vì lý
do nào đó chúng ta xóa record số 2 bằng phương thức deleteRecord(int RecordID)
thì số 2 không bao giờ được sử dụng để gán làm RecordID cho một record khác.
Vậy khi ta duyệt bằng vòng lặp, giả sử i từ 1 đến 3, khi giá trị i=2 sẽ gây ra một lỗi
exception. Khi đó, chúng ta bắt buộc phải duyệt bằng công cụ enumeration. Ngoài
ra, khi chúng ta sử dụng các chức năng lọc (filter) và sắp xếp (sort) các record, giá
trị trả về sẽ là một RecordEnumeration vì các RecordID lúc này không tuân theo bất
kỳ một thứ tự nào cả.
Việc tạo ra một RecordEnumeration là một công việc đơn giản, chỉ tốn một vài
dòng lệnh. Lớp RecordEnumeration không có phương thức khởi tạo (constructor),
ta tạo ra RecordEnumeration bằng phương thức enumerateRecord(…) của lớp
RecordStore
Ví dụ:
RecordEnumeration re = rs.enumerateRecords(null,null,false);
while (re.hasNextElement()) {
// lấy thông tin record tiếp theo ra buffer
String str = new String(re.nextRecord());
//xử lý tùy theo yêu cầu
}


115

Java Mobile
Hàm enumerateRecord nhận vào ba tham số:

• Tham số đầu là bộ lọc (filter), nếu không muốn lọc record ta để null
• Tham số thứ nhì là bộ sắp xếp (sort), nếu không muốn sắp xếp ta cũng để
null. Nếu cả hai tham số trên đều là null thì các record được lấy ra và sắp xếp
theo thứ tự bất kỳ.
• Tham số cuối cùng là một biến boolean bUpdate, nếu bUpdate là true thì khi
danh sách các record có sự thay đổi (bị thêm, xóa hay sửa) biến
RecordEnumeration sẽ tự cập nhật, nếu là false chúng ta phải tự cập nhật
bằng hàm rebuild().
Sau đây là các hàm thông dụng nhất của lớp RecordEnumeration
Lớp javax.microedition.rms.RecordEnumeration
Phương thức Chức năng
Construtor Không có constructor, tạo từ lớp RecordStore
int numRecords() Số lượng records trong tập hợp
byte[] nextRecord() Lấy record kế tiếp, duyệt theo thứ tự tiến
(forward)
int nextRecordId() Lấy RecordID của record kế tiếp
byte[] previousRecord() Lấy record trước đó, duyệt theo thứ tự lùi
(backward)
int previousRecordId() Lấy RecordID của record trước đó
boolean hasNextElement() Kiểm tra còn record để tiếp tục duyệt tiến
(forward) hay không?
bolean hasPrevioussElement() Kiểm tra còn record để tiếp tục duyệt lùi
(backward) hay không?
void keepUpdate(boolean
keepUpdate)
Bật, tắt chế độ tự cập nhật của enum
boolean isKeptUpdate() Kiểm tra xem enum có chức năng tự cập nhật
hay không?
116


Java Mobile
void rebuild() Tự cập nhật lại enumeration
void reset() Đưa enumeration về trạng thái ban đầu, lúc vừa
khởi tạo
void destroy() Trả lại hết tài nguyên giữ bởi biến enum này.
Bảng 6.3 Lớp RecordEnumeration
6.3. Sắp xếp bằng RecordComparator
Yêu cầu sắp xếp dữ liệu là một yêu cầu thường được đặt ra cho các ứng dụng
tiện ích. Công việc này trên các máy desktop thường được thực hiện thông qua các
hệ quản trị cơ sở dữ liệu, trên môi trường J2ME chúng ta cũng có một công cụ khá
hiệu quả để sắp xếp dữ liệu trên Record Store, đó là RecordComparator.
RecordComparator là một Java interface, khi có nhu cầu sắp xếp dữ liệu thì ứng
dụng của ta phải cài đặt (implements) interface này.
Sau đây là một ví dụ của lớp có cài đặt interface RecordComparator:
public class Comparator implements RecordComparator {
public int compare(byte[] rec1, byte[] rec2) {
String str1 = new String(rec1), str2 = new String(rec2);
int result = str1.compareTo(str2);
if (result == 0)
return RecordComparator.EQUIVALENT;
else if (result < 0)
return RecordComparator.PRECEDES;
else
return RecordComparator.FOLLOWS;
}
}

Chúng ta cài đặt interface này bằng cách cung cấp hàm int compare(byte[],byte[]).
Hàm này trả về 3 giá trị đặc biệt đã được định nghĩa trước là:
117


Java Mobile
• RecordComparator.EQUIVALENT
• RecordComparator.PRECEDES
• RecordComparator.FOLLOWS
Khi đó, để bắt đầu quá trình sắp xếp và xử lý dữ liệu ta tạo ra một đối tượng
Comparator (lớp có cài đặt RecordComparator) và một RecordEnumeration:

// Tạo đối tượng Comparator để sắp xếp
Comparator comp = new Comparator();
// Tạo đối tượng enum, tham số thứ nhì là comparator
RecordEnumeration re = rs.enumerateRecords(null,comp,false);
// Duyệt danh sách thông qua enum
while (re.hasNextElement()) {
String str = new String(re.nextRecord());

}

Lúc này hàm compare trong lớp Comparator sẽ được dùng làm cơ sở để sắp xếp
các record. Hàm này nhận vào hai tham số là hai mảng byte (là 2 record dùng để so
sánh).
int compare (byte[] r1, byte[] r2)
Tùy theo giá trị trả về của hàm mà record r1 sẽ được xếp trước record r2, xếp sau r2
hay 2 records được xem là “bằng nhau”.

Giá trị Ý nghĩa
EQUIVALENT Record r1 và r2 được xem là bằng nhau, thực chất lúc này r1
được xếp trên r2 vì không có sự thay đổi thứ tự
FOLLOWS Record r1 được xem là “lớn hơn” r2 và sẽ xếp sau.
PRECEDES Record r1 được xem là “nhỏ hơn” r2 và sẽ xếp trước.

Bảng 6.4 Các giá trị hằng để sắp xếp record
118

Java Mobile
Lúc này, khi ta dùng hàm enumerateRecords(…) của lớp RecordStore, kết quả
trả về sẽ là một tập hợp (enum) đã được sắp thứ tự và ta có thể thao tác trên tập hợp
này một cách bình thường.
Nguyên lý sort thực chất cũng khá đơn giản: đối với những record gồm nhiều
trường (compound record), khi chúng ta có nhu cầu chỉ search trên một trường bất
kỳ thì trước nhất phải đọc trường đó của 2 record cần so sánh ra 2 biến trung gian.
Sau đó việc so sánh 2 records trên thực chất là việc so sánh 2 biến trung gian. Tùy
theo nhu cầu sort ta sẽ gán giá trị trả về cho hàm compare của lớp Comparator một
cách tương ứng.
6.4. Lọc record với RecordFilter
Ngoài chức năng sắp xếp các record, J2ME còn hỗ trợ chúng ta công cụ lọc các
record. Phần vừa rồi chúng ta đề cập đến việc sắp xếp các record và đưa vào tập
hợp, quá trình này ứng dụng sẽ sắp xếp tất cả các records có trong record store. Nếu
chúng ta chỉ có nhu cầu lấy ra và sắp xếp các record thỏa mãn một yêu cầu nhất
định nào đó (ví dụ trường năm sinh trong record phải nhỏ hơn 1990) thì ta phải
dùng thêm công cụ RecordFilter. Cũng giống như RecordComparator, RecordFilter
là một interface, khi ta có nhu cầu lọc record thì ta phải cài đặt (implements)
interface này qua hàm boolean matches(byte[] candidate).
Hàm matches(byte[] candidate) này nếu trả về giá trị true thì record candidate sẽ
có mặt trong RecordEnumeration, ngược lại nếu trả về giá trị false thì record sẽ bị
loại.
Giả sử ta có bài toán lọc theo năm sinh như sau: Trong một record sẽ có hai
trường, trường HoTen được lưu dưới dạng String UTF8 và trường năm sinh lưu
dưới dạng integer. Ta cần lấy ra danh sách những người sinh trước năm 1990. Bài
toán được giải quyết như sau:


public class YearFilter implements RecordFilter{
private int Year=1990;
119

Java Mobile
public boolean matches(byte[] candidate) {
String HoTen;
Int NamSinh;
ByteArrayInputStream strm=new ByteArrayInputStream(candidate);
DataInputStream dataStrm=new DataInputStream(strm);
Hoten=dataStrm.readUTF();
NamSinh=dataStrm.readInt();
return (NamSinh<Year) ? true:false;
}
}

public class MIDletApp extends MIDlet {

MIDletApp{
YearFilter filter=new YearFilter;
RecordEnumeration enum=rs.enumerateRecords(filter,null,true);
while (enum.hasNextElement()) {

//Xuất danh sách các cá nhân thỏa yêu cầu
}
}

120

Java Mobile

6.5. Nhận thông điệp khi Record Store thay đổi
Đôi khi chúng ta có nhu cầu cần được thông báo mỗi khi các records trong
record store bị thay đổi, xóa hay được thêm vào. Để phục vụ cho nhu cầu này,
J2ME cung cấp cho chúng ta interface RecordListener. Interface này hoạt động
tương tự các interface chúng ta đã đề cập đến trong phần RMS này. Khi lớp ứng
dụng của ta cài đặt interface này, nếu có sự biến đổi nào trên các records của bộ
record store chúng ta sẽ nhận được thông điệp thông qua các hàm recordAdded(),
recordChanged() và recordDeleted().
Phương thức Ý nghĩa
void recordAdded(RecordStore recordStore, int
recordId)
Được gọi khi có một record
được thêm vào record store
void recordChanged(RecordStore recordStore,int
recordId)
Được gọi khi có một record bị
thay đổi nội dung.
void recordDeleted(RecordStore recordStore, int
recordId)
Được gọi khi một record bị
xóa khỏi record store.
Bảng 6.5 Lớp RecordListener

Các hàm sự kiện trên đều có hai tham số:
• RecordStore recordStore: cho biết record store nào bị thay đổi.
• int recordID: cho biết ID của record bị thay đổi

121

Java Mobile

6.6. Xử lý lỗi khi thao tác với Record Store

Hãng Sun đã đặt ra tổng cộng 5 exceptions dành riêng cho việc xử lý RMS. Tất cả
các exception này đều kế thừa từ lớp java.lang.Throwable, sau đây là danh sách các
exception trên và ý nghĩa của chúng:

InvalidRecordIDException:
Được dùng để thông báo người dùng truy xuất đến RecordID không hợp lệ. Ví dụ
như khi hàm RecordStore.getRecord(int ID) được gọi và tham số ID bằng 0 thì
exception này sẽ phát sinh.
Constructor:
public InvalidRecordIDException(String message)
public InvalidRecordIDException()
RecordStoreException:
Một exception dạng “chung chung”, được phát sinh khi có lỗi xảy ra do truy xuất
record store.
Constructor:
public RecordStoreException()
public RecordStoreException(String message)
RecordStoreFullException
Thông báo record store đã đầy: ví dụ khi gọi hàm RecordStore.add
Record(byte[],int,int) để thêm một record, nếu record này đã đạt đến dung lượng tối
đa sẽ phát sinh exception dạng này.
Constructor
public RecordStoreFullException()
public RecordStoreFullException(String message)
122

Java Mobile
RecordStoreNotFoundException

Thông báo tên của record store không tồn tại. Ví dụ khi ta gọi hàm
RecordStore.deleteRecordStore (String) với một tên không tồn tại thì exception này
sẽ phát sinh.
Constuctor:
public RecordStoreNotFoundException()
public RecordStoreNotFoundException(String message)
RecordStoreNotOpenException
Được phát sinh khi ta thực hiện một số công việc truy vấn trên record store như
thêm record, xóa record, đếm số record mà record store chưa được mở trước đó
Consturctor
public RecordStoreNotOpenException()
public RecordStoreNotFoundOpen(String message)


123

Java Mobile
Chương 7: Kết nối mạng với Generic
Connection Framework (GCF)

7.1. Giới thiệu GFC
Trong bộ J2SE và J2EE chúng ta thực hiện các công việc liên quan đến truy xuất
tài nguyên mạng qua hai packages chính là: java.io và java.net. Với kích thước hơn
200 kbytes và bao gồm hơn 100 lớp và interfaces, cách thức truy xuất thông qua hai
gói io và net này vượt quá khả năng của thiết bị J2ME. Ngoài ra, trong bộ J2EE và
J2SE này, nhà phát triển còn chú trọng nhiều đến các phương thức mạng và hệ
thống file system trong khi thiết bị J2ME lại không quan tâm nhiều đến các vấn đề
này. Vì các lý do trên, bộ thư viện Generic Connection Framework (GCF) đã được
phát triển và nhắm đến các thiết bị di động J2ME.
Các nhà phát triển không đưa ra mục tiêu phát triển một bộ thư viện cung cấp

các lớp, phương thức hoàn toàn mới mà họ muốn đưa ra một bộ thư viện con của
các thư viện đã được phát triển khá tốt trên môi trướng J2SE và J2EE. Bộ thư viện
con này sẽ có một số thay đổi nhỏ để thích ứng với các hạn chế trên thiết bị di động
cài đặt MIDP.
7.2. Lược đồ lớp
Mục tiêu chung đươc đề ra là chúng ta cần có một lớp chính: lớp Connector.
Lớp này sẽ có khả năng tạo các loại kết nối khác nhau: http, datagram, file…
Phương thức mở kết nối sẽ có dạng:
Connector.Open("protocol:address;parameters");
Ví dụ:
Connector.Open("e_web_address.com");
Connector.Open("socket://someaddress:1234");
Connector.Open("file://testdata.txt");
124

Java Mobile
Lúc này GCF cho thấy khả năng linh hoạt của mình. Tùy theo các protocol khác
nhau mà một kết nối loại tương ứng sẽ được mở và trả lại cho người dùng. Lớp
Connector sẽ tìm kiếm các lớp cài đặt cho loại protocol được yêu cầu, việc này
được thực hiện thông qua phương thức Class.forName(). Ví dụ khi có một yêu cầu
mở kết nối HTTP, lớp Connector sẽ thực thi hàm:
Class.forName(“com.sun.midp.io.j2me.http.Protocol”);
Sau khi kiểm tra, nếu lớp này tồn tại thì người dùng sẽ được trả lại một đối
tượng cài đặt (implements) interface Connection và các thao tác truy cập sẽ được
thực hiện thông qua đối tượng này. Lớp Connector và interface Connection được
định nghĩa trong CLDC.
Lược đồ các lớp trong thư viện:

Hình 7.1 Lược đồ các lớp trong thư viện GCF


Thực chất CLDC chỉ cung cấp các định nghĩa cho các interface. Quá trình cài
đặt (implement) các protocols được thực hiện trong Profiles. Ví dụ, trong MIDP 1.0
lớp HTTPConnection sẽ cài đặt (chỉ một phần) bộ giao thức HTTP 1.1. Lớp
125

Java Mobile
HTTPConnection cài đặt interface ContentConnection và có hơn 20 phương thức để
hỗ trợ người dùng tương tác với giao thức HTTP.
Mặc dù DatagramConnection được biểu diển trên lược đồ nhưng MIDP 1.0 chỉ
yêu cầu bắt buộc hỗ trợ giao thức HTTP. Các nhà phát triển có thể hỗ trợ thêm các
giao thức khác nhưng điều này không bắt buộc.
Trong MIDP 2.0, chúng ta được hỗ trợ các phương thức kết nối thông qua TCP
và UDP. Tuy nhiên, ở phần này chúng ta chú trọng đến việc kết nối thông qua
HTTP, đây là yêu cầu bắt buộc với các thiết bị MIDP.

Sau đây là danh sách các lớp,interfaces và hàm chính trong bộ GCF:

Connection (public abstract interface Connection)
public void close()
InputConnection (public abstract interface InputConnection extends Connection)
public InputStream openInputStream()
public DataInputStream openDataInputStream()
OutputConnection (public abstract interface OutputConnection extends Connection)
public OutputStream openOutputStream()
public DataOutputStream openDataOutputStream()
StreamConnection (public abstract interface StreamConnection extends
InputConnection, OutputConnection)
ContentConnection (public abstract interface ContentConnection extends
StreamConnection)
public long getLength()

public String getEncoding()
public String getType()
HttpConnection (public interface HttpConnection extends ContentConnection)
//Hơn 20 phương thức hỗ trợ truy cập HTTP
Connector (public class Connector)
126

Java Mobile
public static Connection open(String name)
public static Connection open(String name, int mode)
public static Connection open(String name, int mode, boolean
timeouts)
public static DataInputStream openDataInputStream(String name)
public static DataOutputStream openDataOutputStream(String name)
public static InputStream openInputStream(String name)
public static OutputStream openOutputStream(String name)
7.3. Kết nối HTTP:
Trong MIDP 1.0, phương thức duy nhất bảo đảm được cài đặt là HTTP. Thông
qua lớp HttpConnection chúng ta có thể liên lạc với web server hoặc một thiết bị có
hỗ trợ HTTP.
HTTP được xem như một giao thức dạng yêu cầu/phản hồi. Máy client sẽ gửi
một yêu cầu đến server (địa chỉ server được định nghĩa theo đặc tả Uniform
Resource Locator (URL)) và một phản hồi sẽ được phát sinh từ phía server gửi trả
cho client. Đây cũng chính là cách thức liên lạc giữa các trình duyệt web và web
server.
7.3.1.
Khởi tạo kết nối:
Lớp Connector có 7 phương thức để khởi tạo kết nối với server.
Lớp: javax.microedition.io.Connector
Phương thức Mô tả

static Connection open(String name) Tạo connection dạng READ_WRITE.
static Connection open(String name,
int mode)
Tạo connection với mode được chỉ định rõ
(được đề cập ở bảng sau).
static Connection open(String name,
int mode, boolean timeouts)
Tạo connection với mode được chỉ định,
xử lý exception timeouts (được đề cập ở
phần sau)
127

Java Mobile
static InputStream openInputStream
(String name)
Tạo một input stream cho connection
static OutputStream
openOutputStream (String name)
Tạo một output stream cho connection
static DataInputStream
openDataInputStream (String name)
Tạo một data input stream cho connection
static DataOutputStream (String
name)
Tạo một data output stream cho connection
Bảng 7.1 Lớp Connector

Có 3 modes được hỗ trợ khi ta tạo mới một connection:
javax.microedition.io.Connector
Mode Ý nghĩa

READ_WRITE Connection hỗ trợ cả đọc lẫn ghi dữ liệu
READ Connection chỉ cho phép đọc dữ liệu xuống
WRITE Connection chỉ hỗ trợ việc ghi dữ liệu.
Bảng 7.2 Mode Kết Nối

Trong 7 phương thức trên có 3 phương thức open() khác nhau: Phương thức đầu
tiên chỉ đòi hỏi địa chỉ của server, phương thức thứ hai cho phép người dùng chọn
kiểu connection có hỗ trợ reading/writing. Phương thức cuối cùng cho phép người
dùng tùy chọn có xử lý timeouts exception hay không. Các phương thức còn lại
dùng để mở các dòng dữ liệu đọc/ghi.

Sau đây là đoạn lệnh dùng để thiết lập một kết nối kiểu ContentConnetion (đây
là interface cài đặt interface Connection và chứa các định nghĩa cho lớp
HttpConnection, xin xem lại lược đồ bên trên):
String url = ""
ContentConnection connection = (ContentConnection) Connector.open(url);
128

Java Mobile
Chúng ta không thiết lập một đối tượng kiểu Connector mà dùng hàm static
open() để tạo một connection. Kết quả trả về là một đối tượng Connection, ta có thể
“ép kiểu” về dạng Connection chúng ta mong muốn (ở đây là ContentConnection).
Sau đó ta có thể tạo một InputStream từ connection này để phục vụ việc đọc dữ
liệu:

InputStream iStrm = connection.openInputStream();
// ContentConnection hỗ trợ phương thức getLength
int length = (int) connection.getLength();
if (length > 0) {
byte imageData[] = new byte[length];

// Đọc dữ liệu vào mảng
iStrm.read(imageData);
}

Sau khi tạo thành công connection, ta có tạo các dòng dữ liệu đọc/ghi dựa trên
connection này. Interface ContentConnection còn hỗ trợ cả phương thức
getLenght() cho biết chiều dài dữ liệu hiện hành. Cấu trúc và phong cách thiết kế
hướng đối tượng của Java hỗ trợ cho các nhà phát triển ứng dụng rất nhiều.
Thậm chí chúng ta còn có thể lấy ra dòng dữ liệu Input mà không cần tạo đối tượng
connection như sau:

// Tạo một InputStream connection
String url = "
InputStream iStrm = (InputStream) Connector.openInputStream(url);
try {
byte imageData[] = new byte[2500];
// Đọc dữ liệu vào mảng
int length = iStrm.read(imageData);
}
129

Java Mobile
Tuy nhiên, ở cách tiếp cận vừa nêu chúng ta không tận dụng được phương thức
getLength() của interface ContentConnection. Việc chọn lựa cách tiếp cận nào là
tùy thuộc vào hoàn cảnh và sở thích của người lập trình.

Đến thời điểm này chúng ta chỉ mới khởi tạo kết nối và đọc, ghi dữ liệu mà chưa
tận dụng các tính năng liên quan đến giao thức HTTP. HTTP là một giao thức dạng
yêu cầu / phản hồi: client gửi một yêu cầu (request), server sẽ gửi lại một thông điệp
phản hồi. Tiếp theo chúng ta sẽ nghiên cứu những chức năng hỗ trợ giao thức

HTTP.
7.3.2.
Các đặc điểm của kết nối HTTP bằng J2ME:
Các phương thức Yêu Cầu (Request):
Một yêu cầu từ phía client gồm 3 phần: phương thức request, header và phần thân
(body). HttpConnection hỗ trợ ba phương thức request: GET, POST, HEAD.
javax.microediton.io.HttpConnection
Phương thức Mô tả
GET Thông tin yêu cầu được gửi kèm URL
POST Thông tin yêu cầu được gửi theo một stream riêng biệt.
HEAD Gói tin yêu cầu truy vấn thông tin về một tài nguyên.
Bảng 7.3 Các Request Method chính
Cả ba phương thức trên thông báo cho server biết client cần truy vấn một thông
tin. Phương thức GET và POST khác nhau ở cách thông tin truy vấn được gửi lên
server.
Chúng ta có thể chỉ định rõ phương thức sử dụng trong HttpConnection bằng hàm
setRequestMethod():

HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.GET);

130

Java Mobile
javax.microediton.io.HttpConnection
Phương thức Mô tả
void setRequestMethod (String method) Đặt phương thức request là Get, Post
hay Head
void setRequestProperty (String key,

String value)
Đặt một thuộc tính request (thông tin ở
phần header)
String getRequestMethod () Lấy phương thức request đang sử dụng
String getRequestProperty (String key) Lấy giá trị hiện thời của một thuộc tính
request (thuộc phần header)
Bảng 7.4 Các phương thức set/get Request Method

Sử dụng GET, phần thân của yêu cầu được đưa vào chung trong URL. Điều này
được gọi là URL encoding. Ví dụ, chúng ta có một web form gồm 2 trường color và
font. Tên của hai trường này được đặt là userColor và userFont. Khi user chọn giá
trị cho hai trường này, giả sử lần lượt là userColor = blue và userFont = courier và
submit lên trang web
thì URL thực chất có
dạng như sau:

(dấu ? dùng để phân cách vùng địa chỉ trang và phần thông tin yêu cầu gửi lên
server). Sau đó, trang web trên server sẽ dùng các hàm riêng để tách vùng thông tin
yêu cầu và thực hiện xử lý, sau đó sẽ trả kết quả cho client.
Với phương thức POST, thông tin truy vấn không được ghép thêm vào vùng
URL mà được chuyển lên server trong vùng BODY của gói tin HTTP.
Phương thức POST có hai ưu điểm so với phương thức GET:
• Phương thức POST không giới hạn kích thước vùng dữ liệu request gửi lên
server, ngược lại phương thức GET do gửi dữ liệu dạng gắn kèm vào URL
nên không thể gửi kèm dữ liệu quá lớn.
• POST gửi dữ liệu theo một dòng riêng (trong phần BODY) nên không thể
hiện thông tin cho người dùng thấy trong URL.
131

×