android tutorial
Post on 07-Feb-2016
593 Views
Preview:
DESCRIPTION
TRANSCRIPT
MỤC LỤC
MỤC LỤC.................................................................................................................................4
MỞ ĐẦU...................................................................................................................................7
Tại sao cần quan tâm tới nền tảng Android..........................................................................7
Đối tượng và phạm vi nghiên cứu của đề tài.........................................................................8
PHẦN 1: NGHIÊN CỨU NỀN TẢNG ANDROID...................................................................9
CHƯƠNG 1. GIỚI THIỆU TỔNG QUAN VỀ MẠNG 3G VÀ XU THẾ THỊ TRƯỜNG....9
1.1. Giới thiệu...................................................................................................................9
1.2. Xu thế của thị trường di động..................................................................................13
CHƯƠNG 2. TỔNG QUAN VỀ ANDROID.......................................................................15
2.1. Android là gì ?..........................................................................................................15
2.2. Các chức năng chính của Android...........................................................................15
2.3. Kiến trúc của Android.............................................................................................16
2.4. Cấu trúc một dự án Android....................................................................................19
CHƯƠNG 3. CÁC THÀNH PHẦN TRONG ANDROID VÀ VIỆC TRUYỀN DỮ LIỆU. 23
3.1. Các kiến thức cơ bản về ứng dụng Android.............................................................23
3.2. Cách sử dụng các thành phần Android....................................................................24
3.3. File AndroidManifest.xml........................................................................................25
3.4. Truyền dữ liệu giữa các Activity..............................................................................26
CHƯƠNG 4. CẤU TRÚC GIAO DIỆN VÀ CÁC SỰ KIỆN TRONG ANDROID.............29
4.1. Giao diện người dùng trong Android.......................................................................29
4.2. Cách bố trí giao diện................................................................................................30
4.3. Cách xử lý các sự kiện trên giao diện.......................................................................30
4.4. Đối tượng Toast........................................................................................................32
CHƯƠNG 5. LISTVIEW VÀ MENU.................................................................................34
5.1. Giới thiệu ListView..................................................................................................34
5.2. ListAdapter..............................................................................................................35
5.3. Menu........................................................................................................................38
CHƯƠNG 6. GỌI DỊCH VỤ WEB.....................................................................................41
6.1. Dịch vụ web..............................................................................................................41
6.1.1. Dịch vụ web là gì...............................................................................................41
1
6.1.2. Đặc điểm...........................................................................................................43
6.2. Gọi dịch vụ web trong lập trình Android.................................................................44
6.2.1. Không sử dụng thư viện bên thứ ba..................................................................44
6.2.2. Sử dụng thư viện bên thứ ba:...........................................................................46
CHƯƠNG 7. SQLITE.........................................................................................................49
7.1. Tổng quan về SQLite................................................................................................49
7.1.1. Giới thiệu..........................................................................................................49
7.1.2. Những điều cần chú ý khi làm việc với SQLite :...............................................49
7.1.3. Kiểu dữ liệu:.....................................................................................................50
7.1.4. Câu lệnh SQL (sql statement)...........................................................................51
7.2. SQLite trong Android:.............................................................................................52
7.2.1. Tạo cơ sở dữ liệu :.............................................................................................53
7.2.2. Truy vấn và thao tác trong cơ sở dữ liệu..........................................................55
7.2.3. Transaction.......................................................................................................61
7.2.4. Lấy dữ liệu từ Cursor.......................................................................................62
7.3. Tạo cơ sỡ dữ liệu riêng và đưa vào ứng dụng Android............................................64
7.3.1. Tạo file cơ sở dữ liệu.........................................................................................64
7.3.2. Sao chép, sử dụng cơ sở dữ liệu có sẵn trong ứng dụng Android.....................65
CHƯƠNG 8. THREAD TRONG ANDROID.....................................................................69
8.1. UI thread..................................................................................................................69
8.2. Handler....................................................................................................................71
8.2.1. Sử dụng Messages.............................................................................................71
8.2.2. Sử dụng Runable..............................................................................................75
8.3. AsyncTask................................................................................................................77
8.3.1. Bốn bước được thực hiện trong AsyncTask......................................................78
8.3.2. Cách sử dụng....................................................................................................79
8.4. Một số chú ý khi làm việc với background thread...................................................83
Chương 9 LƯU TRỬ DỮ LIỆU........................................................................................84
9.1. Dùng Shared Preferences.........................................................................................84
9.2. Sử dụng file hệ thống ...............................................................................................87
9.2.1. Sử dụng bộ nhớ trong.......................................................................................87
9.2.2. Lưu trử vào file cache.......................................................................................90
2
9.2.3. Đọc file từ resources..........................................................................................91
9.2.4. Sử dụng bộ nhớ ngoài.......................................................................................92
Chương 10 LOCATION VÀ MAP......................................................................................96
10.1. Map API...............................................................................................................96
10.1.1. Cách lấy một map-api key từ Google................................................................96
10.1.2. MapView và MapActivity.................................................................................99
10.1.3. Sử dụng Overlays............................................................................................102
10.2. Location base service..........................................................................................107
10.2.1. Dịch vụ LocationManager..............................................................................107
10.2.2. Geocoding với Android...................................................................................112
DANH MỤC TÀI LIỆU THAM KHẢO...............................................................................142
3
MỞ ĐẦU
Tại sao cần quan tâm tới nền tảng Android
Hiện nay trên thế giới có rất nhiều các nền tảng lập trình di động khác nhau, bao
gồm Symbian, iPhone, Windows Mobile, BlackBerry, Java Mobile Edition, … Vậy
tại sao chúng ta lại cần quan tâm tới Android ?
Tuy sinh sau đẻ muộn nhưng Android là nền tảng đầu tiên đem lại những điều sau:
Một nền tảng hoàn toàn mở, tự do phát triển, dựa trên nhân Linux và mã
nguồn mở. Các nhà chế tạo thiết bị cầm tay và các nhà phát triển phần mềm
rất thích điều này vì họ có thể tự do tùy chỉnh lại nó và sử dụng nó mà không
phải trả thêm chi phí bản quyền.
Kiến trúc hệ thống dựa trên các thành phần rời rạc. Các lập trình viên có thể
thay thế hoàn toàn một thành phần trong nền tảng bằng một phiên bản cải
tiến khác của họ, điều này đem lại một xu hướng sáng tạo mới trong thế giới
lập trình di động.
Các dịch vụ mạnh mẽ được thiết kế bên trong: có thể kể đến các dịch vụ dựa
trên vị trí hiện tại của người dùng sử dụng GPS cho phép ta cung cấp cho
người dùng những trải nghiệm mới tùy vào nơi mà họ đang đứng, hoặc một
hệ thống cơ sở dữ liệu sử dụng SQL cho phép ta lưu trữ dữ liệu và đồng bộ
hóa chúng khi kết nối vào Internet.
Vòng đời của ứng dụng được kiểm soát tự động. Các ứng dụng được cô lập
lẫn nhau hoàn toàn bởi cơ chế bảo mật của Android. Điều này mang lại tính
ổn định mà chưa một nền tảng nào hiện nay làm được. Người dùng không
cần phải lo lắng xem ứng dụng nào đang chạy và ứng dụng nào cần phải tắt
để các ứng dụng khác có thể hoạt động. Android được tối ưu cho các thiết bị
có bộ nhớ thấp và ít năng lượng ở mức tốt nhất hiện nay.
Hình ảnh và âm thanh chất lượng cao. Nhanh, mượt, hình ảnh được khử
răng cưa cộng với khả năng tăng tốc cho đồ họa 3D bằng thư viện OpenGL
4
cho phép nhiều loại ứng dụng doanh nghiệp và Game chạy được trên nền
tảng này.
Khả năng tương thích với nhiều loại phần cứng hiện nay và trong tương lai.
Tất cả các chương trình được viết bằng Java và được thực thi bởi máy ảo
Dalvik của Android, do đó mã nguồn của lập trình viên sẽ tương thích hoàn
toàn với các kiến trúc chip như ARM, x86, … Hỗ trợ một loạt các phương
pháp nhập liệu như dùng bàn phím, cảm ứng, trackball. Giao diện người
dùng có thể được tùy chỉnh theo độ phân giải và phương hướng của thiết bị.
Đối tượng và phạm vi nghiên cứu của đề tài
Nội dung của bài báo cáo gồm 2 phần:
Phần một, chúng ta đi vào tìm hiểu nền tảng Android và các kỹ thuật lập trình ứng
dụng trên Android. Nội dung xoay quanh các thành phần cơ bản giúp chúng ta có
thể tạo ra một phần mềm ứng dụng trên nền tảng Android.
Phần hai, báo cáo sẽ giới thiệu về ứng dụng minh họa đã được xây dựng trong quá
trình nghiên cứu và tìm hiểu nền tảng Android. Đây là ứng dụng mang tính thực
tiễn cao thông qua việc sử dụng các dịch vụ dựa vào vị trí người dùng.
5
PHẦN 1: NGHIÊN CỨU NỀN TẢNG ANDROID
CHƯƠNG 1. GIỚI THIỆU TỔNG QUAN VỀ MẠNG 3G VÀ
XU THẾ THỊ TRƯỜNG.
1.1. Giới thiệu
3G, hay 3-G, (viết tắt của third-generation technology) là công nghệ truyền thông
thế hệ thứ ba, cho phép truyền cả dữ liệu thoại và dữ liệu ngoài thoại (tải dữ liệu,
gửi email, tin nhắn nhanh, hình ảnh...).
3G cung cấp cả hai hệ thống là chuyển mạch gói và chuyển mạch kênh. Hệ thống
3G yêu cầu một mạng truy cập radio hoàn toàn khác so với hệ thống 2G hiện nay.
Điểm mạnh của công nghệ này so với công nghệ 2G và 2.5G là cho phép truyền,
nhận các dữ liệu, âm thanh, hình ảnh chất lượng cao cho cả thuê bao cố định và thuê
bao đang di chuyển ở các tốc độ khác nhau. Với công nghệ 3G, các nhà cung cấp có
thể mang đến cho khách hàng các dịch vụ đa phương tiện, như âm nhạc chất lượng
cao; hình ảnh video chất lượng và truyền hình số; Các dịch vụ định vị toàn cầu
(GPS); E-mail;video streaming; High-ends games;...
6
CDMA
GSM
TDMA
PHS (IP-Based)
64 Kbps
GPRS
115 Kbps
CDMA 1xRTT
144 Kbps
EDGE
384 Kbps
cdma20001X-EV-DV
Over 2.4 Mbps
W-CDMA (UMTS)
Up to 2 Mbps
2G2.5G
2.75G 3G
1992 - 2000+2001+
2003+
1G
1984 - 1996+
2003 - 2004+
TACS
NMT
AMPS
GSM/GPRS
(Overlay) 115 Kbps
9.6 Kbps
9.6 Kbps
14.4 Kbps/ 64 Kbps
9.6 Kbps
PDC
Analog Voice
Digital Voice
Packet Data
IntermediateMultimedia
Multimedia
PHS
TD-SCDMA
2 Mbps?
9.6 Kbps
iDEN
(Overlay)
iDEN
Source: U.S. Bancorp Piper Jaffray
Hình 1-1 Lịch sử phát triển của công nghệ truyền thông
2G Network Layout
Mobile Switching Center
NetworkManagement
(HLR)
Out to another MSC or Fixed Network (PSTN/ISDN)
2.5G/2.75G Network Layout
Mobile Switching Center
NetworkManagement
(HLR)
Out to another MSC or Fixed Network (PSTN/ISDN)
IP GatewayInternet(TCP/IP)
3G Network Layout
Mobile Switching Center
IP Gateway
Internet(TCP/IP)
IP Gateway
Internet(TCP/IP)
NetworkManagement
(HLR)
- Base Station - Radio Network Controller
Mobile Switching Center
NetworkManagement
(HLR)
Out to another MSC or Fixed Network (PSTN/ISDN)
Hình 1-2 Sơ đồ mạng 3G
7
Các dịch vụ mạng 3G có thể mang lại:
Mobile Internet: duyệt Web, truyền tải file, email, truyền âm thanh, video,
dịch vụ thanh toán trên mạng.
Mobile Intranet / Extranet: cung cấp chức năng truy cập LAN, VPN.
Multimedia Message Service: gửi tin nhắn dạng MMS
Location based Service: cung cấp các thông tin, dịch vụ dựa vào vị trí của
người dùng.
Voice services: tăng cường chất lượng cuộc gọi với khả năng trình chiếu
hình ảnh.
Tại Việt Nam, lượng người sử dụng các dịch vụ 3G ngày càng đông, dưới đây là
thông số về lưu lượng sử dụng dữ liệu theo tháng.
Hình 1-3 Lưu lượng sử dụng dịch vụ 3G / tháng
8
Hình 1-4 Các website / dịch vụ trên Internet được sử dụng trên thiết bị di động
Hình 1-5 Thống kê mục đích sử dụng dịch vụ 3G
9
Theo khảo sát, các điện thoại thông minh (smart phone) đã tăng trưởng 142% ở Việt
Nam vào năm 2009. Nhu cầu dịch vụ 3G và các thiết bị di động hỗ trợ Internet ở
Việt Nam tăng theo từng ngày. Việt Nam đang trở thành một thị trường tiềm năng
cho các nhà cung cấp dịch vụ mạng, các thiết bị cầm tay và các dịch vụ giá trị gia
tăng dựa trên công nghệ 3G.
Theo ước tính của phân tích viên tạp chí ICT News, thị trường di động tại Việt Nam
sẽ vượt trên 620 triệu $ vào năm 2010.
Hình 1-6 Một số thiết bị di động hỗ trợ 2G / 3G
1.2. Xu thế của thị trường di động
Dưới đây là các xu hướng tương lai của ngành công nghiệp di động và dịch vụ
mạng theo khảo sát của CNET.
Trong cuộc chạy đua trên thị trường di động thông minh, iPhone của hãng Apple và
Android của Google sẽ chiếm giữ vị trị thống lĩnh. Đứng thứ 3 sẽ là BlackBerry,
sau đó sẽ tới Symbian và Windows Mobile.
10
Tốc độ băng thông trên di động sẽ nhanh hơn, cao hơn để đáp ứng được nhiều dịch
vụ hơn. Điện thoại Internet và dịch vụ ngân hàng qua di động là 2 lĩnh vực sẽ hưởng
lợi nhiều nhất.
Các dịch vụ dựa trên vị trí sẽ được cá nhân hóa hơn. Các dịch vụ dựa vào hệ thống
định vị toàn cầu sẽ được đưa vào trong các ứng dụng, giúp chúng trở nên thông
minh hơn khi đưa ra các kết quả dựa vào vị trí hiện tại của người dùng.
Các dịch vụ mạng xã hội sẽ ngày càng đa dạng và chuyên biệt hóa. Twitter,
Facebook, Friendster, MySpace, v.v… sẽ trở nên thân thuộc với nhiều người. Người
dùng có thể chia sẻ nội dung của họ và nắm quyền quản lý tốt hơn về tính riêng tư,
cá nhân. Các yếu tố giải trí sẽ là chìa khóa giữ chân người dùng trong lĩnh vực này.
11
CHƯƠNG 2. TỔNG QUAN VỀ ANDROID.
2.1. Android là gì ?
Android là một hệ điều hành dành cho thiết bị di động như điện thoại, máy tính
bảng và netbooks. Android được phát triển bởi Google, dựa trên nền tảng Linux
kernel và các phần mềm nguồn mở. Ban đầu nó được phát triển bởi Android Inc
(sau đó được Google mua lại) và gần đây nó trở thành một trong những phần mềm
đứng đầu của liên minh OHA (Open Handset Alliance - với khoảng 78 thành viên
bao gồm cả nhà sản xuất, nhà phát triển ứng dụng... cho thiết bị di dộng mà dẫn đầu
là Google). Andorid được phát triển nhằm cạnh tranh với các hệ điều hành di động
khác như iOS (Apple), BlackBerry OS, Windows Mobile (Microsoft), Symbian
(Nokia), Samsung (Bada), WebOS (Palm)... Theo thống kê trong quý II năm 2010
tại Mỹ, hệ điều hành Android chiếm thị phần 33% (cao nhất) trong tổng số các hệ
điều hành di động được bán ra, thứ 2 là BlackBerry OS 28% và iOS (Apple) xếp
thứ 3 với 22%.
Android có một cộng đồng phát triển ứng dụng rất lớn, hiện có khoảng hơn 70.000
ứng dụng có sẵn cho Android và đang liên tục được cập nhật. Ứng dụng được phát
triển bằng ngôn ngữ Java kết hợp với thư viện Java có sẵn của Google. Các nhà phát
triển ứng dụng có thể sử dụng máy tính chạy hệ điều hành Windows hoặc MacOS
hoặc Linux kết hợp với Android SDK để phát triển ứng dụng cho Android. Hệ điều
hành Android bao gồm 12.000.000 dòng mã trong đó có 3.000.000 dòng XML,
2.800.000 dòng C, 2.100.000 dòng Java, và 1.750.000 dòng C++.
Để phát triển phần mềm trên Android, các lập trình viên có thể tải về bộ công cụ
phát triển (Android SDK). Bộ công cụ bao gồm các công cụ và các API cần thiết, sử
dụng ngôn ngữ Java để lập trình.
2.2. Các chức năng chính của Android.
12
Các chức năng mà Android cung cấp:
Android cung cấp framework ứng dụng cho phép việc tái sử dụng và thay thế
mã nguồn ở dạng component một cách dễ dàng.
Cung cấp máy ảo Dalvik được tối ưu cho các thiết bị di động.
Trình duyệt Web dựa trên engine mã nguồn mở Webkit.
Các tính năng đồ họa được tối ưu bởi một thư viện đồ họa 2D bên dưới; đối
với đồ họa 3D, Android sử dụng thư viện OpenGL ES 1.0 nếu thiết bị có hỗ
trợ.
Sử dụng SQLite để lưu trữ dữ liệu có cấu trúc.
Hỗ trợ các định dạng hình ảnh, âm thanh, video phổ biến như MPEG4,
H.264, MP3, AAC, AMR, JPG, PNG, GIF.
Hỗ trợ băng tầng GSM (tùy vào phần cứng thiết bị).
Hỗ trợ Bluetooth, EDGE, 3G, WiFi (tùy vào phần cứng thiết bị).
Ngoài ra còn có khả năng của các thiết bị như máy chụp ảnh, thiết bị định vị
toàn cầu, la bàn, và bộ cảm biến gia tốc.
Cung cấp môi trường phát triển phần mềm đầy đủ, bao gồm phần mềm giả
lập thiết bị, các công cụ gỡ rối (debugging), theo dõi bộ nhớ và năng suất
hoạt động, plugin cho Eclipse IDE.
2.3. Kiến trúc của Android
13
Hình 2-7 Kiến trúc tổng quát của Android
Tầng ứng dụng – Applications:
Tầng ứng dụng của Android bao gồm các ứng dụng cốt lõi như:
Email client
Chương trình SMS
Lịch
Bản đồ
Trình duyệt
Danh bạ
Các ứng dụng khác.
Tầng Application Framework:
14
Ở tầng này, các nhà phát triển ứng dụng có thể truy cập vào phần cứng thiết bị,
thông tin định vị vị trí, chạy các dịch vụ nền, đặt các cảnh báo, thông báo vào thanh
trạng thái, v.v…. và quan trọng nhất, đó là các API của framework.
Phía dưới tất cả các ứng dụng là một tập các dịch vụ hệ thống bao gồm:
Một tập các đối tượng View có thể được mở rộng để xây dựng một ứng
dụng, gồm có List, Grid, TextBox, Button, và WebBrowser.
Các đối tượng ContentProvider cho phép các ứng dụng truy cập vào dữ liệu
của các ứng dụng khác (chẳng hạn như truy cập danh bạ), hoặc để chia sẽ dữ
liệu với nhau.
Một trình quản lý tài nguyên, cho phép truy xuất các tài nguyên không phải
mã nguồn như các chuỗi đã được bản địa hóa, các tập tin đồ họa và giao
diện.
Trình quản lý thông báo cho phép tất cả các ứng dụng hiển thị các cảnh báo
lên thanh trạng thái.
Trình quản lý các đối tượng Activity dùng để quản lý vòng đời của các ứng
dụng cung cấp các chức năng điều hướng.
Tầng Libraries - Runtime
Tầng này cung cấp các thư viện media dựa trên thư viện PacketVideo’s
OpenCORE; các thư viện này hỗ trợ khả năng playback và thu lại nhiều định dạng
âm thanh, hình ảnh thông dụng như MPEG4, H.264, MP3, AAC, AMR, JPG, PNG.
Kèm theo đó là thư viện SQLite, một hệ quản trị cơ sở dữ liệu nhỏ nhẹ và mạnh mẽ
được cung cấp cho tất cả các ứng dụng.
Ở Runtime, Android cung cấp máy ảo Dalvik dùng để thực thi các file định dạng
Dalvik Executable (.dex) đã được tối ưu hóa cho các thiết bị có bộ nhớ nhỏ. Máy ảo
Dalvik chỉ chạy các class đã được đăng ký và được biên dịch bởi một trình biên
dịch Java đi kèm theo bộ SDK (dx tool). Ngoài ra Dalvik còn sử dụng nhân Linux
để quản lý các tính năng ở mức thấp và các tác vụ chạy theo luồng. Mọi ứng dụng
Android chạy trên một tiến trình riêng cùng với một instance riêng của máy ảo
15
Dalvik. Dalvik đã được tối ưu sao cho một thiết bị có thể chạy nhiều mày ảo cùng
lúc một cách hiệu quả.
Tầng Linux kernel
Android được phát triển dựa trên các dịch vụ hệ thống cốt lõi của Linux phiên bản
2.6, bao gồm các module:
Security
Memory management
Process management
Network stack
Driver model
Tầng kernel hoạt động như một lớp trừu tượng giữa lớp phần cứng và phần mềm.
2.4. Cấu trúc một dự án Android
Khi tạo mới một dự án Android, bạn cần lưu ý tới các mục sau:
AndroidManifest.xml, là một file XML miêu tả ứng dụng được xây dựng và
các thành phần kèm theo như các Activities, các Services được cung cấp bởi
ứng dụng đó.
Thư mục libs/ chứa các thư viện Java ở dạng JAR của các hãng thứ 3 mà ứng
dụng cần để chạy.
Thư mục src/ chứa mã nguồn Java cho ứng dụng.
Thư mục res/ chứa các tài nguyên chẳng hạn như các biểu tượng, giao diện,
… được đóng gói kèm theo khi biên dịch ứng dụng.
Thư mục assets/ chứa các file tĩnh mà bạn muốn cài đặt lên hệ thống.
Các bước để tạo mới một dự án Android:
File > New > Android project, điền các thông số cần thiết.
16
Hình 2-8 Cửa sổ tạo mọt dự án Android mới
Sửa đổi File giao diện được tạo sẵn: main.xml
17
Hình 2-9 file main.xml có sẵn
Sửa file mã nguồn tương ứng:
Hình 2-10 File HelloAndroid.java
Chạy ứng dụng ở dạng Android Application
18
Hình 2-11 Kết quả chạy chương trình HelloAndroid
Và như vậy, bạn đã viết được ứng dụng Android đầu tiên.
19
CHƯƠNG 3. CÁC THÀNH PHẦN TRONG ANDROID VÀ
VIỆC TRUYỀN DỮ LIỆU.
3.1. Các kiến thức cơ bản về ứng dụng Android.
Các ứng dụng Android được viết bằng ngôn ngữ Java. Tất cả các đoạn code được
đóng gói lại thành một file .apk và được xem là một ứng dụng Android. Một file
apk được tạo ra bởi công cụ aapt đi kèm theo bộ SDK bao gồm:
Mã nguồn Java đã được biên dịch.
Dữ liệu của ứng dụng.
Các file tài nguyên.
Mỗi ứng dụng Android có một không gian hoạt động riêng. Mặc định, mọi ứng
dụng chạy trên một tiến trình Linux riêng. Android quản lý việc bắt đầu và kết thúc
một tiến trình khi nó không còn được dùng đến. Mỗi tiến trình có một máy ảo riêng,
do đó mã của ứng dụng chạy hoàn toàn cách biệt với tất cả các ứng dụng khác. Mặc
định, mỗi ứng dụng được gán cho một ID của người dùng duy nhất.
Điểm đặc biệt của một ứng dụng Android: không có một đầu vào duy nhất cho mọi
thứ trong ứng dụng ( không có hàm main() ) nhưng các thành phần cơ bản có thể
được hệ thống tạo ra và chạy khi cần thiết. Bốn thành phần chính của ứng dụng
Android là:
Activity.
Service.
Broadcast Receiver.
Content provider.
Activity là một cá thể, tập trung vào những việc mà người dùng có thể làm, chẳng
hạn như: liệt kê tất cả các mục cho người dùng chọn, hiển thị một nội dung cụ thể
20
cho người dùng xem, Activity1 hiển thị danh sách các email, Activity2 hiện chi tiết
nội dung của email.
Mỗi activity được cài đặt thông qua việc kế thừa từ lớp cơ sở Activity. Activity đảm
nhiệm việc tạo ra cửa sổ để thiết lập giao diện người dùng thông qua phương thức
setContentView(View).
Service là thành phần không chứa giao diện người dùng, chạy ngầm trong hệ thống
trong một khoảng thời gian không xác định. Ví dụ: một Service chơi file nhạc ở
background, hoặc một service lấy dữ liệu qua mạng Internet hoặc thực hiện các tính
toán và cung cấp kết quả về cho các Activity cần nó. Mỗi service kế thừa từ lớp cơ
sở Service.
Content Provider là thành phần giúp chia sẻ một tập dữ liệu của ứng dụng tới các
ứng dụng khác. Dữ liệu có thể được lưu trên file hệ thống, trong cơ sở dữ liệu
SQLite, hoặc các các lưu trữ hợp lý khác. Để sử dụng, ta cần gọi các phương thức
từ đối tượng ContentResolver.
3.2. Cách sử dụng các thành phần Android.
ContentProvider được kích hoạt khi chúng nhận được yêu cầu từ ContentResolver.
Ba thành phần còn lại, bao gồm Activity, Service, BroadcastReceiver được kích
hoạt thông qua đối tượng Intent. Intent là đối tượng chứa nội dung của thông điệp
cần gửi tới các thành phần trong Android.
Một Activity được kích hoạt bằng cách truyền một đối tượng Intent vào các phương
thức sau:
Context.startActivity()
Activity.startActivityForResult()
21
Activity được gọi có thể lấy được các dữ liệu kèm theo lời gọi bằng cách lấy đối
tượng Intent truyền vào thông qua method getIntent().
Một Activity thường bắt đầu một Activity khác. Nếu nó mong đợi kết quả trả về từ
Activity mà nó gọi, nó sẽ sử dụng phương thức startActivityForResult() thay vì
startActivity().
Lấy ví dụ: nếu một Activity gọi một Activity khác cho phép người dùng chọn một
tấm hình và chờ kết quả trả về là tấm hình được chọn. Kết quả trả về là một đối
tượng Intent kèm theo các dữ liệu cần thiết để lấy được tấm ảnh đó. Đối tượng
Intent này được lấy thông qua phương thức onActivityResult().
Một đối tượng Service được bắt đầu bằng cách truyền một đối tượng Intent vào
phương thức Context.startService(). Android gọi phương thức onStart() của Service
và truyền đối tượng Intent vào.
Các thành phần nên được tắt khi chúng không còn cần dùng đến nữa hoặc phải giải
phóng bộ nhớ cho các thành phần khác đang hoạt động. Một Activity có thể kết
thúc một Activity mà nó gọi thông qua phương thức startActivityForResult() bằng
phương thức finishActivity(). Một Service có thể được tắt bằng cách gọi chính
phương thức stopSelf() của nó hoặc bằng phương thức Context.stopService().
3.3. File AndroidManifest.xml.
Trước khi Android kích hoạt một thành phần ứng dụng nào đó, nó phải biết được sự
tồn tại của thành phần đó. Do đó các ứng dụng phải khai báo các thành phần mà
chúng có trong file manifest.
File Manifest là một file XML có cấu trúc và được đặt tên chung là
AndroidManifest.xml cho tất cả các ứng dụng.
Ví dụ:
22
Hình 3-12 File Manifest của ứng dụng mShopping
3.4. Truyền dữ liệu giữa các Activity.
Để truyền dữ liệu giữa các Activity với nhau rất đơn giản, ta chỉ cần sử dụng đối
tượng Bundle và gán vào Intent để truyền vào hoặc trả về Intent đó.
Ví dụ:
23
Hình 3-13 Ứng dụng minh họa việc truyền dữ liệu.
Truyền dữ liệu:
private View.OnClickListener mClickViewContactListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent iGetContactInfo = new Intent(getApplicationContext(), ViewContactInfoActivity.class);
Bundle bundle = new Bundle();
bundle.putString("nameKey", txtName.getText().toString());
bundle.putString("emailKey", txtEmail.getText().toString());
bundle.putString("projectKey", txtProject.getText().toString());
iGetContactInfo.putExtras(bundle);
startActivity(iGetContactInfo);
}
};
Nhận dữ liệu:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contactinfo);
txtNameValue = (TextView)findViewById(R.id.txtNameInfoValue);
txtEmailValue = (TextView)findViewById(R.id.txtEmailInfoValue);
txtProjectValue = (TextView)findViewById(R.id.txtProjectInfoValue);
24
finishBtn = (Button)findViewById(R.id.btnFinish);
finishBtn.setOnClickListener(mClickFinishListener);
Bundle bundle = getIntent().getExtras();
String name = bundle.getString("nameKey");
String email = bundle.getString("emailKey");
String project = bundle.getString("projectKey");
txtNameValue.setText(name);
txtEmailValue.setText(email);
txtProjectValue.setText(project);
}
25
CHƯƠNG 4. CẤU TRÚC GIAO DIỆN VÀ CÁC SỰ KIỆN
TRONG ANDROID.
4.1. Giao diện người dùng trong Android.
Trong ứng dụng Android, giao diện người dùng được xây dựng bằng các đối tượng
View và ViewGroup. View là đối tượng cơ sở của giao diện người dùng trên nền
tảng Android, lấy ví dụ như TextView hoặc Button. Đối tượng ViewGroup là lớp cơ
sở cho các cách bố cục giao diện khác nhau như LinearLayout, GridLayout,
TableLayout, …
Giao diện người dùng trong Android sử dụng hệ thống phân cấp gồm các View và
ViewGroup.
Hình 4-14 Hệ thống phân cấp View
Để gắn cây phân cấp View lên màn hình hiển thị, đối tượng Activity trước tiên phải
gọi phương thức setContentView() và truyền vào tham chiếu tới đối tượng đại diện
cho nút gốc của cây phân cấp.
26
4.2. Cách bố trí giao diện.
Để bố trí giao diện trong Android, ta có thể sử dụng file XML để định nghĩa và thể
hiện các View.
Tất cả các tập tin định nghĩa giao diện nằm trong thư mục <Tên dự án>/res/layout
Ta có thể tạo giao diện và bố cục chúng bằng tập các tham số như android:id,
android:layout_width, android:layout_height, android:text, …
Ví dụ mã XML tạo 1 View bao gồm một TextView và 1 Button
Hình 4-15 Tạo giao diện bằng XML
4.3. Cách xử lý các sự kiện trên giao diện.
Một khi đã thêm các đối tượng View vào giao diện người dùng, người dùng sẽ
tương tác được với chúng. Để xử lý các sự kiện trên giao diện người dùng, chúng ta
cần phải làm một trong hai việc sau:
27
Định nghĩa một đối tượng lắng nghe sự kiện và đăng ký nó với lớp View tương ứng.
Lớp View chứa một danh sách các lớp giao tiếp được đặt tên theo cấu trúc On<Sự
kiện>Listener, ví dụ: OnClickListener(). Các lớp giao tiếp này được gọi là event
listeners.
Viết mã ghi đè lên các phương thức có sẵn của đối tượng View, chẳng hạn như
onTouchEvent(). Phương pháp này được sử dụng khi chúng ta cần tự tạo ra lớp
View. Các đối tượng như vậy được gọi là event handlers.
Một số event listener thường sử dụng:
View.OnClickListerner, cài đặt phương thức onClick() để xử lý khi người
dùng nhấn vào các View.
View.OnKeyListener, cài đặt phương thức onKey() để xử lý các sự kiện khi
người dùng nhấn, thả một phím trên thiết bị.
View.OnTouchListener, cài đặt phương thức onTouch() để xử lý khi người
dùng thực hiện một hành động gây ra sự kiệm chạm.
Ví dụ minh họa:
28
Hình 4-16 Ví dụ xử lý sự kiện onClick()
4.4. Đối tượng Toast.
Toast là một đối tượng được sử dụng để thể hiện những thông điệp thoáng qua trong
chốc lát. điều này có nghĩa là thông điệp này sẽ tự hiển thị và biến mất bất chấp tới
thao tác của người dùng.
Toast là cách tốt để tương tác với người dùng và sử dụng trong quá trình gỡ rối.
Để sử dụng, ta sử dụng các phương thức tĩnh của lớp Toast, ví dụ:
Toast.makeText(this, “successfully”, Toast.LENGTH_SHORT).show();
29
Hình 4-17 Toast xuất hiện trên màn hình và biến mất sau 1 khoảng thời gian cho trước
30
CHƯƠNG 5. LISTVIEW VÀ MENU.
5.1. Giới thiệu ListView.
ListView là một trong những widget quan trọng nhất trong Android vì nó được sử
dụng rất thường xuyên.
Hình 5-18 Một dạng ListView
ListView là một lớp kế thừa từ View, dùng để hiển thị các item trong một danh sách
cuộn dọc màn hình. Các item này được lấy ra từ một ListAdapter kết nối với
ListView.
31
ListView thường đi kèm trong ListActivity vì nó có thể gắn kết các nguồn dữ liệu
vào ListView. Để sử dụng ListView, file layout phải chứa một đối tượng ListView
với thuộc tính id là “@android:id/list”
Một tập các hàng ngang sẽ tạo thành ListView. Mỗi hàng có thể được tạo ra bằng
cách chỉ định cụ thể file layout dành cho nó thông qua ListAdapter.
Hình 5-19 Tạo giao diện với ListView bằng XML
5.2. ListAdapter.
ListAdapter là cầu nối giữa ListView và các nguuồn dữ liệu. ListAdapter là một lớp
giao tiếp cài đặt từ Adapter. Để gắn kết dữ liệu với ListView, ta cần có đối tượng
cài đặt lớp giao tiếp ListAdapter này. Android cung cấp 2 ListAdapter chuẩn sau:
SimpleAdapter, sử dụng cho các loại dữ liệu tĩnh (chẳng hạn như Map).
SimpleCursorAdapter, sử dụng cho các đối tượng Cursor, kết quả của các
truy vấn.
32
Class SimpleCursorAdapter là cách đơn giản để ánh xạ các cột dữ liệu từ đối tượng
Cursor sang các TextView hoặc ImageView được định nghĩa trong một file XML
qua các bước:
Đặc tả các cột dữ liệu.
Đặc tả các View dùng để hiển thị các cột dữ liệu.
Đặc tả file XML dùng để định nghĩa sự hiện diện của các đối tượng View.
Sử dụng hàm tạo: SimpleCursorAdapter(Context context, int layout, Cursor
c, String[] from, int[] to)
Để gắn kết dữ liệu danh bạ điện thoại từ ListAdapter vào ListView như hình dưới,
ta thực hiện các bước sau:
Hình 5-20 Ví dụ minh họa ListView
Tạo layout chính cho màn hình:
33
Hình 5-21 main.xml
Tạo layout cho các hàng (các item trong ListView):
Hình 5-22 row.xml
Thực hiện gắn kết dữ liệu:
34
Hình 5-23 Gắn kết dữ liệu vào ListView
5.3. Menu.
Menu là một thành phần quan trọng của một ứng dụng dùng để cung cấp một
phương pháp giao tiếp thân thiện cho người dùng truy cập vào các chức năng của
chương trình và các thiết lập.
Android cung cấp 3 loại menu cho ứng dụng:
Options Menu: loại menu chính của class Activity, xuất hiện khi người dùng bấm
vào nút menu trên điện thoại.
Context Menu: một danh sách các menu items hiện ra khi người dùng thực hiện
hành động nhấn giữ (long press) lên View.
Submenu: một danh sách các menu items hiện ra khi người dùng nhấn vào một
menu item trong Options Menu hoặc Context Menu.
Để cài đặt menu ta cần thực hiện 2 bước:
35
Tạo ra menu thông qua Menu Resource bằng cách override phương thức
onCreateOptionsMenu(Menu menu). Phương thức này được gọi lần đầu tiên
khi Options Menu được mở.
Xử lý việc menu được chọn bằng cách override phương thức
onOptionsItemSelected(MenuItem item)
Hình 5-24 Options Menu
36
Hình 5-25 menu.xml
Hình 5-26 Minh họa cài đặt menu
37
CHƯƠNG 1.GỌI DỊCH VỤ WEB
Trong chương này chúng em không hướng dẫn chi tiết cách cài đặt một dịch vụ web
như thế nào sẻ giới thiệu về dịch vụ web, như là dịch vụ web là gì, đặc điểm của nó
như thế nào , …Và tiếp đó là cách gọi dịch vụ web trong lập trình với hệ điều hành
Android như thế nào như là : cách tạo yêu cầu (request) đến dịch vụ web, cách xữ lý
những đáp trả (response) của dịch vụ web có định dạng là XML hoặc là JSON.
6.1. Dịch vụ web
6.1.1. Dịch vụ web là gì
Hãy bắt đầu bằng cách xem xét toàn cảnh về các dịch vụ web thực sự là gì và tại sao
chúng lại quan trọng cho sự phát triển phần mềm.
Các ứng dụng truyền thống
Lúc bắt đầu, đã có các máy tính. Và nó đã hoạt động rất tốt. Các máy tính có vẻ đã
thực hiện các nhiệm vụ phi thường, tự động hoá rất nhiều thứ mà mọi người đã phải
làm bằng tay, bắt đầu với các tính toán phức tạp và chuyển sang tài chính và nhiều
nhiệm vụ khác.
Nhưng các ứng dụng truyền thống là "các tháp cao". Ứng dụng nguồn nhân lực có
thể không thực sự nói về ứng dụng tài chính, mà ứng dụng tài chính có thể không
thực sự nói về ứng dụng phân phối. Tất cả các ứng dụng này đã có nhà riêng của
mình, trên máy tính của riêng mình và trong khi chúng đã rất có ích, nhưng vẫn
chưa có một cách hay để chia sẻ dữ liệu giữa chúng. Bạn đã có tùy chọn để viết các
quy trình bó (batch) để chuyển dữ liệu từ một hệ thống này đến một hệ thống khác,
nhưng điều đó không thay thế cho việc tích hợp thời gian thực.
38
Tính toán phân tán
Bước tiếp theo trong chuỗi tiến hóa của chúng ta là tính toán phân tán. Tính toán
phân tán đã cho phép các ứng dụng khác nhau nói chuyện với nhau, ngay cả khi
chúng không ở trên cùng một máy tính. Các công nghệ như CORBA, MTS và EJB
(Enterprise Java Beans), đã cung cấp một hệ thống bao gồm một đăng ký về các loại
để cho các ứng dụng có thể tìm thấy các thành phần mà chúng muốn tương tác và
sau đó gọi các thành phần này như thể chúng đang nằm trên máy cục bộ.
Các hệ thống này đã được phần mềm trung gian hỗ trợ, hoặc cụ thể hơn, phần mềm
trung gian hướng-thông báo, cung cấp cả hai yêu cầu này. Các ứng dụng bây giờ có
thể được xây dựng theo cách mà chúng có thể truy cập tài nguyên trên các hệ thống
khác, ngay cả khi chúng đang ở các vị trí địa lý khác nhau.
Nhưng vẫn còn có một vấn đề. Trong khi các ứng dụng tự do truyền thông bất cứ
nơi nào trong hệ thống, thì hệ thống vẫn còn là hệ thống khép kín. Ít nhất, ứng dụng
khách của bạn đã phải sử dụng cùng một công nghệ như là ứng dụng máy chủ.
Ngoài ra, các hệ thống không được thiết kế, theo một quy tắc, để truy cập từ bên
ngoài tổ chức riêng lẻ đã tạo ra chúng
Các dịch vụ web
Tiếp theo, liên kết hầu như không tránh khỏi trong chuỗi tiến hóa này là các dịch vụ
web. dịch vụ web có thể dựa trên các thông báo SOAP hoặc dựa trên REST .Sử
dụng XML hoặc JSON, trong nhiều trường hợp, trên HTTP, "các dịch vụ web" vẫn
còn có nghĩa là nhiều thứ cho nhiều người. XML và JSON, là những tiêu chuẩn mã
nguồn mở dựa trên văn bản, bất cứ ai có thể truy cập được từ bất kỳ ứng dụng nào
(ứng dụng bất kỳ là ứng dụng được thiết kế để chấp nhận nó). Điều này mở rộng thế
giới cho ứng dụng của bạn để bao gồm bất cứ ai có thể tiếp cận nó trên mạng của
bạn.
Một loại dịch vụ web khác liên quan đến việc sử dụng một chuẩn như XML-RPC.
Trong trường hợp này, các lệnh được gửi đến một hệ thống thông qua XML.
39
Hình 6-27 Ví dụ về ứng dụng gọi dịch vụ web dựa trên SOAP
6.1.2. Đặc điểm
Dịch vụ web cho phép client và server tương tác được với nhau ngay cả
trong những môi trường khác nhau.
Phần lớn kĩ thuật của dịch vụ web được xây dựng dựa trên mã nguồn mở
và được phát triển từ các chuẩn đã được công nhận, ví dụ như XML,
JSON…
Một dịch vụ web bao gồm có nhiều mô-đun và có thể công bố lên mạng
Internet.
Các client và dịch vụ web đều không cần biết cài đặt của nhau. Một ứng
dụng khi được triển khai sẻ hoạt động theo mô hình client-server. Nó có
thể được triển khai bởi một phần mềm ứng dụng phía server ví dụ như
Microsoft.Net, java , PHP…
40
Ưu điểm :
- Dịch vụ web cung cấp khả năng hoạt động rộng lớn với các ứng dụng
phần mềm khác nhau chạy trên những nền tảng khác nhau.
- Sử dụng các giao thức và chuẩn mở. Giao thức và định dạng dữ liệu
dựa trên văn bản (text), giúp các lập trình viên dễ dàng hiểu được.
- Nâng cao khả năng tái sử dụng.
Thúc đẩy đầu tư các hệ thống phần mềm đã tồn tại bằng cách cho
phép các tiến trình/chức năng nghiệp vụ đóng gói trong giao diện
dịch vụ web .
- dễ dàng cho việc phát triển các ứng dụng phân tán.
- Thúc đẩy hệ thống tích hợp, giảm sự phức tạp của hệ thống, hạ giá
thành hoạt động, phát triển hệ thống nhanh và tương tác hiệu quả với
hệ thống của các doanh nghiệp khác.
Nhược điểm :
- Những thiệt hại lớn sẻ xảy ra vào khoảng thời gian chết của dịch vụ
web , giao diện không thay đổi, có thể lỗi nếu một máy khách không
được nâng cấp, thiếu các giao thức cho việc vận hành.
- Có quá nhiều chuẩn cho dịch vụ web khiến người dùng khó nắm bắt.
- Phải quan tâm nhiều hơn đến vấn đề an toàn và bảo mật.
Dịch vụ web sẻ đáp ứng (response ) cho chúng ta cái gì ?
Khi nhận được một lời gọi, dịch vụ web sẻ thực hiện yêu cầu mà nó nhận được, tùy
cài đặt, có thể là update, delete, … Nhưng khi được yêu cầu phải đáp trả lại thông
tin thì nó sẻ trả về một tài liệu XML hoặc là một tài liệu JSON (có thể là một SOAP
message, RSS, Atom , …)
6.2. Gọi dịch vụ web trong lập trình Android
6.2.1. Không sử dụng thư viện bên thứ ba
41
Trong Android không có thư viện về SOAP để hổ trợ cho dịch vụ web. Tuy nhiên,
nó có sẵn các thư viện cho việc kết nối qua giao thức HTTP, củng như hổ trợ việc
phân tích (parse) tài liệu XML , và tài liệu JSON. Để phân tích tài liệu XML chúng
ta có thể sử dụng SAX, DOM, với JSON thì ta có thể dùng JSONObject.
Vì các thiết bị di động có tài nguyên hạn chế , nên việc chúng ta tự phân tích tài liệu
XML hoặc JSON bằng giải thuật riêng, thì có thể hổ trợ việc tối ưu bộ nhớ củng
như các yêu cầu khác. Tuy nhiên hiện nay các thư viện third party củng đã được xây
dựng rất nhiều hổ trợ cho việc phân tích , gọi dịch vụ web , ví dụ như : KSoap2,
Axis2, GSON, … Để gọi một dịch vụ web ta cần làm theo từng bước sau :
Với dịch vụ web sử dụng SOAP :
Tạo một kết nối HTTP tới dịch vụ web, thường sử dụng lớp
java.net.HttpURLConnection.
Tạo một yêu cầu SOAP (SOAP request).
Mở một ouput stream từ kết nối HTTP ban đầu. Ghi SOAP request vào stream đó.
Bước tiếp theo là mở một input stream từ kết nối HTTP ban đầu và nhận chuổi đáp
trả của dịch vụ web. Chuổi đó chính là một SOAP response.
Bước tiếp theo là chúng ta sử dụng SAX hoặc DOM để phân tích dữ liệu XML nhận
được từ SOAP response.
Với dịch vụ web sử dụng REST :
Tạo một HTTP request có thể sử dụng một trong các phương thức GET, POST,
PUT, DELETE của HTTP. Thường dùng lớp con của interface :
HttpUriRequest(HttpGet, HttpPost, ..) trong gói
org.apache.http.client.methods.HttpUriRequest.
Tạo kết nối tới nhà cung cấp dịch vụ web. Mở một input stream từ kết nối để lấy
đáp trả từ dịch vụ web.
42
Phân tích tài liệu XML hoặc tài liệu JSON từ đáp trả của dịch vụ web.
Để thao tác với XML chúng ta có thể sử dụng các package sau :
org.w3c.dom : phân tích bằng DOM
org.xml.sax : phân tích bằng SAX
org.xmlpull : dùng XMLPULL V1 API
Để thao tác với JSON ta dùng lớp căn bản như org.json.JSONArray,
org.json.JSONObject.
6.2.2. Sử dụng thư viện bên thứ ba:
Ksoap2 :
Với những dịch vụ web sử dụng SOAP chúng ta có thể sử dụng thư viện KSoap2.
Đây là một thư viện được xem là khá nhẹ và hiệu quả, đang được sử dụng rất nhiều
hiện nay.
Để sử dụng KSoap2 trước hết phải download thư viện Ksoap2 từ liên kết :
http://code.google.com/p/ksoap2-android/
Sau khi add thư viện vào project. Để sử dụng ta phải sử dụng các thư viện :
org.ksoap2.SoapEnvelope;
org.ksoap2.serialization.SoapObject;
org.ksoap2.serialization.SoapSerializationEnvelope;
org.ksoap2.transport.HttpTransportSE;
Tạo một yêu cầu SoapObject (SoapObject requet).
43
Tạo một đôi tượng SoapSerializationEnvelope từ SoapRequest đó.
Tạo một đối tượng của lớp HttpTransportSE từ đối tượng
SoapSerializationEnvelope ở trên.
Từ đối tượng của lớp HttpTransportSE, gọi method “call() “ để gọi dịch vụ web.
Sau khi thực hiện lời gọi dịch vụ web. Chúng ta sẻ nhận về một đối tượng
SoapObject là đáp trả từ dịch vụ web. Từ đối tượng này ta sẻ lấy được những dữ
liệu cần thiết mà dịch vụ web đáp trả.
Ví dụ sử dụng Ksoap2
Dưới đây là một ví dụ sử dụng thư viện KSoap2 để thực hiện lời gọi dịch vụ web
được triển khai bằng .Net.
private static final String SOAP_ACTION =
"http://tempuri.org/IXDTService/GetTop100ThiSinhDetailsByMaTruong";
private static final String METHOD_NAME =
"GetTop100ThiSinhDetailsByMaTruong";
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "http://10.0.2.2:3744/XemDiemThiService.svc";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("maTruong",mt);
SoapSerializationEnvelope envelope = new
SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
44
SoapObject result=(SoapObject)envelope.getResponse();
SoapObject thisinh = (SoapObject) result.getProperty(i);
String name = thisinh.getProperty("HoTen").toString();
String sbd = thisinh.getProperty("SoBaoDanh").toString();
45
CHƯƠNG 2.SQLITE
6.1. Tổng quan về SQLite
6.1.1. Giới thiệu
SQLite là phần mềm quản lý cơ sở dữ liệu (DBMS) với đặc điểm gọn, nhẹ. Được
nhúng sẵn trong hệ điều hành Android, không cần cài đặt, không cần cấu hình hay
khởi động mà có thể sử dụng ngay. SQLite hổ trợ những chức năng của cơ sỡ dữ
liệu quan hệ giống như cú pháp của SQL. Dữ liệu database cũng được lưu ở một file
duy nhất. Không có khái niệm user, password hay quyền hạn trong SQLite
database. SQLite được phát triển bởi Richard Hipp và được tài trợ bởi Adobe,
Oracle, Bloomberg, Mozilla với website chính thức http://www.sqlite.org. Chính vì
sự ra đời sau các đàn anh lớn như Microsoft SQL server, Oracle, MySQL . . . .
SQlite có cấu trúc câu lệnh tương tự như các hệ quản trị trước nó và website của
SQlite có một cách hướng dẫn sữ dụng khá sơ sài. Cũng chính vì thế sẻ có một khó
khăn cho người sữ dụng nếu như họ chưa hề sữ dụng các hệ quản trị đó(hệ quản trị
lớn).
SQLite rất gọn nhẹ, toàn bộ hệ quản trị chưa đến 400KB, và sữ dụng rất ít bộ nhớ
trong. Nên nó là một lựa chọn hoàn hỏa cho việc quản lý cơ sỡ dữ liệu trên điện
thoại di động. Như là Android, IphoneOS.
6.1.2. Những điều cần chú ý khi làm việc với SQLite :
Trong SQLite không có ràng buộc kiểu dữ liệu. Bạn có thể đặt một giá trị
của kiểu dữ liệu này vào một cột (column) có kiểu dữ liệu khác. Ví dụ như
bỏ một chuổi vào một cột số nguyên.
46
Nếu ban xây dựng ứng dụng với Android 2.2 trở lên thì có thể sử dụng khóa
ngoại trong SQLite. Vì lúc này Android sử dụng SQLite 3.6.22. Các phiên
bản SQLite trước 3.6.19 không hổ trợ khóa ngoại trong SQLite.
SQLite không hổ trợ Unicode mặc định.
6.1.3. Kiểu dữ liệu:
Các nhóm kiểu dữ liệu trong SQlite:
TEXT: dữ liệu dạng chuỗi, bao gồm:
- CHARACTER(20)
- VARCHAR(255)
- VARYING CHARACTER(255)
- NCHAR(55)
- NATIVE CHARACTER(70)
- NVARCHAR(100)
- TEXT
- CLOB
INTEGER: dữ liệu số nguyên, bao gồm:
- INT
- INTEGER
- TINYINT
- SMALLINT
- MEDIUMINT
- BIGINT
- UNSIGNED BIG INT
- INT2
- INT8
47
NUMERIC: dữ liệu số nói chung, bao gồm:
- NUMERIC
- DECIMAL(10,5)
- BOOLEAN
- DATE
- DATETIME
REAL: kiếu số thực, bao gồm:
- REAL
- DOUBLE
- DOUBLE PRECISION
- FLOAT
NONE: không xác đinh kiểu, bao gồm: BLOB
6.1.4. Câu lệnh SQL (sql statement).
Câu lệnh của SQLite hoàn toàn tương tự với ngôn ngữ sql.
Tạo bảng :
create table <tên bảng>(
cột 1 <kiểu dữ liệu>,
cột 2 <kiểu dữ liệu>,
cột 3 <kiểu dữ liệu>,
. . . . . . .
);
Thêm một dòng mới
48
insert into <tên bảng> [(các cột)] values(<các gia trị>);
Xóa dữ liệu
Delete from <tên bảng> [where điều liện];
Cập nhật
Update <tên bảng> set <tên cột>=<giá trị>[,<tên cột>=<giá trị>, . . ]
[where diều kiện];
6.2. SQLite trong Android:
Android cung cấp một thư viện được xây dựng bằng ngôn ngữ C để truy cập đến cơ
sở dữ liệu SQLite nhằm đảm bảo tốc độ và giảm tối đa việc sử dụng tài nguyên.
Nhưng thư viện này đã được bao bằng những lớp java (API) ở mức cao hơn. Để
hiểu rõ hơn ta có thể xem hình 7- 1 sau :
Hình 7-28 Android SDK software stack
49
Ta có thể thấy SQLite nằm trong Native libraries. Cần nói thêm, Native libraries là
những thư viện được xây dựng bằng những ngôn ngữ khác (phần lớn là C/ C++),
không phải là java. Và những thư viện này thường được bao bởi các lớp java(API).
Trong phần lớn trường hợp chúng ta chỉ cần thao tác với các lớp java và không cần
quan tâm đến các mức sâu hơn.
Trong hệ điều hành Android, SQLite chạy như là một dịch vụ của hệ điều hành, chứ
không giống với một hệ quản trị cơ sở dữ liệu như SQL server hoặc MySQL. Một
cơ sở dữ liệu của ứng dụng nào thì chỉ có ứng dụng đó truy cập. Nếu muốn chia sẻ
dữ liệu với những ứng dụng khác, thì chúng ta có thể sử dụng Content Provider.
Nếu ứng dụng của bạn tạo cơ sở dữ liệu, thì nó sẻ được lưu theo đường dẫn :
"DATA/data/APP_NAME/databases/FILENAME". "DATA".
APP_NAME : là tên ứng dụng.
FILENAME : là tên cơ sở dữ liệu của ứng dụng.
Khi bạn muốn copy cơ sở dữ liệu từ máy này sang máy khác, bạn chỉ cần copy file
trên, và đặt vào đúng đường dẫn.
6.1.1. Tạo cơ sở dữ liệu :
Để tạo hoặc upgrade một cơ sở dữ liệu trong ứng dụng Android, chúng ta thường
thừa kế lớp SQLiteOpenHelper, lớp này nằm trong gói android.database. Sau đó
chúng ta override các phương thức onCreate() và onUpgrade() :
onCreate() : Được gọi khi database được tao ra .Trong phương thức này
chúng ta tạo table, trigger, view.
onUpgrade() để upgrade cơ sở dữ liệu trong trường hợp chúng ta cần thay
đổi mô hình cơ sở dữ liệu. Như là alter table, drop table, tạo mới table.
50
Cả hai phương thức này đều có đối số là một đối tượng của lớp “SQLiteDatabase”.
SQLiteOpenHelper cung cấp phương thức “getReadableDatabase()” và
“getWriteableDatabase()” để truy cập đến một đối tượng của lớp “SQLiteDatabase”
, đây là đối trượng cho phép truy cập vào cơ sở dữ liệu với chế độ đọc hoặc ghi.
Trong cơ sở dữ liệu, bạn cần đặt tên khóa chính luôn là “_id”.
Dưới đây là một ví dụ với lớp DBHelper thừa kế từ lớp SQLiteOpenHelper.
package com.example.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBHelper extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "applicationdata";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CREATE = "create table todo (_id
integer primary key autoincrement, "
+ "category text not null, summary text not null, description text not
null);";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
51
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w(DBHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to
"
+ newVersion + ", which will destroy all
old data");
db.execSQL("DROP TABLE IF EXISTS todo");
onCreate(db);
}
}
Cần lưu ý, phương thức onCreate() chỉ được một lần, khi database chưa được tạo ra.
Những lần sau chạy ứng dụng thì phương thức này không được gọi lại nữa.
6.1.2. Truy vấn và thao tác trong cơ sở dữ liệu.
52
Để truy vấn và thao tác trong cơ sở dữ liệu, chúng ta sử dụng lớp SQLiteDatabase
và giao diện Cursor. Cả hai đều nằm trong gói android.database.
“SQLiteDatabase” là class cung cấp các phương thức quan trọng trong việc quản lý
cơ sở dữ liệu như create, delete, thực thi các câu lệnh SQL, .. . Một vài phương thức
cụ thể như :
insert() : để thêm một hàng vào table.
update() : thay đổi giá trị của một hàng.
delete() : xóa một hoặc nhiều hàng trong table
execSQL() : thực thi câu lệnh sql không trả về dữ liệu (không phải là
SELECT).
query() , rawQuery() : để thực thi truy vấn cơ sở dữ liệu.
Phương thức “rawQuery()” sử dụng những câu truy vấn SQL còn “query()” cung
cấp một giao diện trong việc truy vấn động, ví dụ như là chúng ta chưa biết cần phải
truy vấn những cột nào. Cả hai phương thức này đều trả về một đối tượng Cursor.
Đây là một ví dụ về sử dụng rawQuery():
Cursor getAllDepts()
{
SQLiteDatabase db=this.getReadableDatabase();
Cursor cur=db.rawQuery("SELECT _id, colDeptName from
deptTable” ,new String [] {});
return cur;
}
Phương thức “query()” có những các đối số quan trọng sau :
String table : Tên của table được dùng để truy vấn.
53
int[] columnNames : Mảng tên của những cột mà chúng ta muốn truy vấn từ
bảng.
String whereClause : có thể đặt null nếu không sử dụng where trong câu truy
vấn. Có thể đặt các đối số trong where là “?”. Ví dụ như “_id = ?, HoTen
= ?”. Và các đối số cần đặt trong mảng valuesForWhereClause.
String[] valuesForWhereClause : Mảng chứa các giá trị thay thế cho “?”
trong whereClause. Có thể đặt null.
String groupBy : chuổi string , GROUP BY những cột nào. Có thể đặt null.
String having : tương tự với whereClause, having theo điều kiện nào. Có thể
đặt null.
String orderBy : sắp xếp theo kiểu nào. Có thể đặt null.
Cursor là một giao diện. Đại diện cho kết quả của một truy vấn vào cơ sở dữ liệu.
Nó cung cấp các phương thức cho chúng ta đọc hoặc viết ngẩu nhiên từ kết quả của
truy vấn dữ liệu. Khi sử dụng Cursor cần chú ý là nó không có đồng bộ hóa, nên khi
xữ lý với nhiều thread chúng ta cần chú ý thực thi đồng bộ hóa khi sử dụng đối
tượng của Cursor. Ngoài ra trong khi truy vấn , nếu tên của khóa chính của bạn
không phải là _id thì chúng ta phải đặt alias cho nó là _id, vì Cursor luôn yêu cầu
khóa chính là _id. Ví dụ như :
SELECT [Column Name] as _id.
Ngoài ra, để thêm vào hoặc thay đổi một hàng trong table, chúng ta sử dụng lớp
android.content.ContentValues. Ví dụ sử dụng ContentValues:
ContentValues values = new ContentValues();
values.put(KEY_CATEGORY, category);
values.put(KEY_SUMMARY, summary);
54
values.put(KEY_DESCRIPTION, description);
Lớp này cho phép chúng ta chứa một tập những khóa / giá trị. Chúng ta sử dụng tên
của cột trong table để làm khóa. Sau khi tao ra , thêm vào những giá trị cần thiết cho
một hàng, sử dụng phương thức insert(), hoặc update của một đối tượng
SQLiteDatabase để thêm vào hoặc update trong cơ sở dữ liệu.
Dưới đây là một ví dụ truy vấn cơ sở dữ liệu. Đây là một adapter của lớp DBHelper
trong ví dụ trên :
package com.example.sqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class SQLAdapter {
// Database fields
public static final String KEY_ROWID = "_id";
public static final String KEY_CATEGORY = "category";
public static final String KEY_SUMMARY = "summary";
public static final String KEY_DESCRIPTION = "description";
private static final String DATABASE_TABLE = "todo";
private Context context;
private DBHelper dbHelper;
private SQLiteDatabase db;
55
public SQLAdapter(Context context) {
this.context = context;
}
public synchronized SQLAdapter open() {
if (dbHelper == null) {
dbHelper = new DBHelper(context);
Log.v("Minh", "call DBHelper constructer");
}
db = dbHelper.getWritableDatabase();
return this;
}
public void close() {
dbHelper.close();
}
public long insertTodo(String category, String summary, String description)
{
ContentValues value = createContentValues(category, summary,
description);
return db.insert(DATABASE_TABLE, null, value);
}
public boolean updateTodo(long rowId, String category, String summary,
String description) {
ContentValues updateValues = createContentValues(category,
summary,
56
description);
return db.update(DATABASE_TABLE, updateValues,
KEY_ROWID + "=" + rowId,
null) > 0;
}
public Cursor fetchAllTodos() {
return db.query(DATABASE_TABLE, new String[]
{ KEY_CATEGORY,
KEY_SUMMARY, KEY_DESCRIPTION }, null, null,
null, null, null);
}
public Cursor fetchTodo(long rowID) {
Cursor cursor = db.query(true, DATABASE_TABLE, new String[]
{ KEY_CATEGORY, KEY_SUMMARY, KEY_DESCRIPTION }, KEY_ROWID
+ " = " + rowID, null, null, null, null, null);
if(cursor != null){
cursor.moveToFirst();
}
return cursor;
}
public boolean deleteTodo(long rowId) {
return db.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId,
null) > 0;
}
57
private ContentValues createContentValues(String category, String
summary,
String description) {
ContentValues values = new ContentValues();
values.put(KEY_CATEGORY, category);
values.put(KEY_SUMMARY, summary);
values.put(KEY_DESCRIPTION, description);
return values;
}
}
6.1.3. Transaction
Thêm một điều đặc biệt nữa là lớp “SQLiteDatabase” hổ trợ cho chúng ta thực hiện
transaction , nghĩa là khi chúng ta cần thực hiện một tập những truy vấn lên cơ sở
dữ liệu và chỉ khi tất cả những truy vấn này cùng thành công thì chúng mới có hiệu
lực, ngược lại chỉ một truy vấn thất bại thì tất cả sẻ không được thực thi. Bằng các
phương thức :
db.beginTransaction(): đánh dấu bắt đầu một transaction.
db.setTransactionSuccessful() : đánh dấu rằng transaction đã thành công. Và
không thực hiện thêm bất kì một hành động nào lên cơ sỡ dữ liệu cho đến khi
gặp phương thức endTransaction. Nếu trong khoảng
setTransactionSuccessful() đến endTransaction() có exeption nào sinh ra thì
transaction vẩn được commit.
db.endTransaction();
Đây là một ví dụ về transaction :
58
db.beginTransaction();
Cursor cur = null;
try {
cur = db.query("tbl_countries",
null, null, null, null, null, null);
cur.moveToPosition(0);
ContentValues values = new ContentValues();
values.put("state_name", "Georgia");
values.put("country_id", cur.getString(0));
long stateId = db.insert("tbl_states", null, values);
db.setTransactionSuccessful();
view.append("n" + Long.toString(stateId));
} catch (Exception e) {
Log.e("Error in transaction", e.toString());
} finally {
db.endTransaction();
cur.close();
}
6.1.4. Lấy dữ liệu từ Cursor
Điều chúng ta cần quan tâm bây giờ là khi đã có được dữ liệu từ cơ sở dữ liệu trong
một đối tượng của Cursor thì chúng ta sẻ thao tác với chúng như thế nào.
Như đã nói , kết quả của truy vấn cơ sỡ dữ liệu được đặt trong một đối tượng của
giao diện Cursor. Với giao diện này , chúng ta thường sử dụng các phương thức như
là :
59
boolean moveToNext() : di chuyển đến dòng tiếp theo trong tập kết quả trả về. Nếu
không còn dòng nào nữa sẻ trả về false.
boolean moveToFist() : di chuyển đến dòng đầu tiên của tập kết quả trả về. Nếu tập
kết quả là rổng thì phương thức này sẻ trả về false.
boolean moveToPosition(int position) : di chuyển đến dòng có chỉ số position. Trả
lại false nếu không có dòng ở vị trí position.
boolean moveToPrevious() : di chuyển đến dòng phía trước của dòng hiện tại. Nếu
đang ở dòng đầu tiên thì phương thức này sẻ trả về false.
Boolean moveToLast() : di chuyển đến dòng cuối cùng trong tập kết quả. Nếu tập
kết quả là rổng thì phương thức này sẻ trả về false.
Ngoài ra, còn có các phương thức kiểm tra vị trí hiện thời mà đối tượng của Cursor
đang trỏ đến: isAfterLast(), isBeforeFirst(), isFirst(), isLast() và
isNull(columnIndex).
Dưới đây là một ví dụ về truy xuất dữ liệu từ Cursor :
if (cursor.moveToFirst()){
do{
String data = cursor.getString(cursor.getColumnIndex("data");
// do what ever you want here
}while(moveToNext());
}
cursor.close();
Hoặc một ví dụ khác :
mCursor.moveToFirst();
int index = mCursor.getColumnIndex(Browser.BookmarkColumns.TITLE);
60
while (mCursor.isAfterLast() == false) {
view.append("n" + mCursor.getString(index));
mCursor.moveToNext();
}
Như ta thấy trong cả hai ví dụ trên đều sử dụng phuong thức
Cursor.getColumnIndex(String ColumnName) để lấy chỉ số của cột trong đối tượng
của Cursor. Từ chỉ số này mà chúng ta sẻ lấy được giá trị của các cột này bằng các
phương thức getShort(), getString(), getDouble(), getInt(), getBlob() sẻ trả về các
kiểu tương ứng là short, String, double, int và getBlob sẻ trả về một byte stream để
truy xuất đến kiểu dữ liệu không xác định.
6.3. Tạo cơ sỡ dữ liệu riêng và đưa vào ứng dụng Android
Giả sử bạn không muốn tạo cơ sở dữ liệu bằng ứng dụng Android. Mà muốn tự tạo
một cơ sở dữ liệu, hoặc trong trường hợp cơ sỡ dữ liệu đã có sẵn và bạn muốn đưa
vào ứng dụng Android sử dụng. Dưới đây là một hướng để giải quyết những yêu
cầu này.
6.1.1. Tạo file cơ sở dữ liệu
Trước hết là tạo một cơ sở dữ liệu. Hoặc nếu đã có sẵn một cơ sỡ dữ liệu thì bạn
củng phải thay đổi một vài điều.
Bạn nên có một giao diện đồ họa để thao tác với SQLite. Có thể sử dụng SQLite
Database Browser , là một phần mềm mả nguồn mở. Có thể download tại :
http://sourceforge.net/projects/sqlitebrowser/
61
Bạn có thể tạo một cơ sở dữ liệu mới hoặc mở cơ sỡ dữ liệu đã có sẵn lên. Và tạo
thêm table “android_metadata” , bằng cách chạy câu lệnh SQL sau :
CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US')
Sau đó thêm vào một dòng có giá trị là “en_US”, bằng câu lệnh SQL sau :
INSERT INTO "android_metadata" VALUES ('en_US')
Bước tiếp theo , khá cần thiết đó là nếu là cơ sỡ dữ liệu có sẵn bạn nên chuyển tên
tất cả các khóa chính của các table mà bạn có thành “_id”. Hoặc, nếu bạn tạo mới
các table khác, thì các khóa chính nên đặt là “_id”. Hình 7-2 là một ví dụ :
Hình 7-29. Cơ sỡ dữ liệu ví dụ
6.1.2. Sao chép, sử dụng cơ sở dữ liệu có sẵn trong ứng dụng
Android
Sau khi đã có được file cơ sở dữ liệu. Bạn copy file đó vào thư mục “assets” trong
project của ứng dụng. Sau đó, trong project bạn thêm một class DatabaseHelper
thừa kế từ lớp SQLiteOpenHelper trong gói “android.database.sqlite”. Lớp này
củng có các phương thức tương tự như phần trên. Nhưng thay vì tạo cơ sở dữ liệu
trong hàm onCreate(), thì chúng ta thêm phương thức createDataBase() để copy file
cơ sở dữ liệu từ “assert” vào thư mục chứa cơ sở dữ liệu của ứng dụng
public class DataBaseHelper extends SQLiteOpenHelper{
62
//đường dẫn đến cơ sở dữ liệu trong android
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/";
//tên file cơ sở dữ liệu
private static String DB_NAME = "myDBName";
private SQLiteDatabase myDataBase;
private final Context myContext;
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
public void createDataBase() throws IOException{
boolean dbExist = checkDataBase();
if(dbExist){
return;
}else{
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
63
private boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
private void copyDataBase() throws IOException{
InputStream myInput = myContext.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream myOutput = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
myOutput.flush();
myOutput.close();
myInput.close();
64
}
public void openDataBase() throws SQLException{
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READONLY);
}
@Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
}
}
65
CHƯƠNG 3.THREAD TRONG ANDROID
Khi sử dụng ứng dụng, người dùng luôn mong muốn không phải chờ, không cảm
thấy ứng dụng của mình đang dừng lại. Theo một số ý kiến thì tối đa nên
delay(dừng lại) 200 mili giây.
Nhưng ứng dụng thường có những tác vụ tốn nhiều chi phí và thời gian. Và giải
pháp được đưa ra là thực hiện những tác vụ này trong một thread khác (background
thread) với thread chính của ứng dụng.
6.2. UI thread
Khi ứng dụng được khởi chạy, hệ thống khởi tạo một thread, đây được gọi là “main
thread” của ứng dụng. Main thread còn được gọi là UI thread. Đây là thread có vai
trò rất quan trọng, nó có trách nhiệm gửi những sự kiện đến những widgets thích
hợp.
Ví dụ như , nếu bạn chạm vào một button trên màn hình, UI thread sẻ gửi sự kiện
này (touch event) đến button (widget) đó, do đó, button đó được đặt vào trạng thái
là pressed và một yêu cầu được gửi đến hàng đợi sự kiện. Tiếp đó, UI thread sẻ lấy
yêu cầu từ hàng đợi và thông báo tới button để vẽ nó tự vẽ lại.
Với mô hình một thread này nếu không triển khai đúng cách thì nó có thể mang lại
hiệu suất kém. Đặc biệt, nếu mọi thứ đểu được thực thi trong một thread, khi chúng
ta thực thi một công việc tốn thời gian ví dụ như truy cập dữ liệu từ internet, hoặc
truy vấn từ cơ sở dữ liệu trong UI thread thì giao diện của ứng dụng sẻ bị đứng.
Không có sự kiện nào được gửi đi, bao gồm cả việc vẽ giao diện. Khi đó, người
dùng sẻ nghĩ là ứng dụng bị treo. Và nếu UI thread bị khóa trong một khoảng thời
gian nào đó ( thường là 5 giây) thì một dialog sẻ hiện ra và thông báo là ứng dụng
không hồi đáp, bạn có muốn đóng nó lại không.
66
Tóm lại, điều quan trọng là luôn để UI thread không bị khóa. Nếu có những tác vụ
cần nhiều tài nguyên, tốn thời gian để thực hiện, bạn nên đưa nó vào một thread
khác.
Dưới đây là một ví dụ về ứng dụng download hình ảnh từ internet và hiển thị vào
một ImageView.
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
}).start();
}
Trước hết, đoạn code này có vẽ là một giải pháp tốt, và nó không làm cho UI thread
bị đứng vì nhiệm vụ download hình ảnh đã được thực hiện trong một thread khác.
Nhưng việc thực hiện như đoạn code vừa rồi đã vi phạm tới mô hình đơn thread của
giao diện người dùng. UI thread không phải là thread-safe, và việc cập nhật, hay
thao tác với giao diện phải luôn được thực hiện trong UI thread. Trong đoạn code
trên , mImageView đã được thao tác từ một thread khác, điều này có thể làm cho
chương trình crash.
Android đã đưa ra một số cách để truy cập đến UI thread từ những thread khác. Bao
gồm một số cách phổ biến sau :
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
Từ Android 1.5 trở lên, hổ trợ lớp AsynTask.
67
Trong bài báo cáo này sẻ giới thiệu về hai cách được sử dụng nhiều nhất là Handler
và AsynTask.
6.3. Handler
Handler nằm trong gói “android.os” .Handler có thể xem như là một người liên lạc
giữa UI thread và những thread khác(background thread). Những thread khác có thể
gưởi message đến một đối tượng Handler. Và đối tượng Handle này sẻ xử lý từng
message đó, và update giao diện người dùng. Mổi Activity chỉ cần một đối tượng
Handler để xử lý những message được gửi đến, có thể là từ nhiều thread khác.
Chúng ta không cần phải đăng ký đối tượng Handler với UI thread, chỉ cần tạo ra
một đối tượng Handler , nó sẻ tự động ràng buộc với hàng đợi message của Activity
đã tạo ra nó.
Chúng ta có hai cách để giao tiếp với Handler :
Qua messages
Qua một đối tượng Runnable
6.3.1. Sử dụng Messages
Message là lớp nằm trong gói “android.os”.
Để dùng Messages và Handler chúng ta phải thực hiện hai bước chính sau :
Tạo một đối tượng Handler đã overide phương thức callback
“handleMessage(Message msg)”. Để điều khiển việc nhận messages từ những
thread khác. Ngĩa là khi nhận được messages thì làm gì. Mỗi khi có message gửi
đến thì phương thức này sẻ được gọi. Đây sẻ là phương thức thực hiện thay đổi trên
giao diện người dùng (giao tiếp với UI thread). Vì vậy , công việc thực hiện trong
phương thức này không nên tốn quá nhiều thời gian, dẫn đến việc UI thread bị khóa.
68
Bước tiếp theo là từ những thread khác, chúng ta phải thực hiện gửi message cho
Handler khi cần thiết. Để send message cho Handler, trước hết phải gọi phương
thức obtainMessage() để nhận một đối tượng Message. Bạn có thể gửi một message
trống tới Handler, hoặc message đó chứa định danh và các đối số. Handler xử lý
càng phức tạp, càng nhiều loại message thì bạn càng phải đưa nhiều thông tin cho
Handler biết là đây là message xử lý sự kiện gì.
Bạn gửi message tới Handler bằng một trong các phương thức sau :
sendMessage() : đưa message vào hàng đợi message.
sendMessageAtFrontOfQueue() : đưa message vào hàng đợi, và đặt nó ngay đầu
hàng đợi. Nghĩa là nếu có nhiều message trong hàng đợi , thì message vừa gửi đến
sẻ có độ ưu tiên cao nhất, và xử lý đầu tiên.
sendMessageAtTime() : đặt message vào hàng đợi khi đến một thời điểm nào đó.
Được tính bằng mili giây.
sendMessageDelayed() : đặt message vào hàng đợi sau một khoảng thời gian delay
nào đó, tính bằng mili giây.
Dưới đây là một ví dụ sử dụng Handler :
Tạo mới một project. Sửa lại layout của activity như sau.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ProgressBar android:id="@+id/ progressBar1"
style="?android:attr/progressBarStyleHorizontal"
69
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
ở đây chúng ta sử dụng một ProgressBar để minh họa trên UI cho biết background
thread chạy được tới đâu.
Tiếp theo là xử lý trong Activity :
package com.gtvt.example
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
public class HandlerDemo extends Activity {
private ProgressBar m_ProgressBar;
private boolean isRunning=false;
private Handler handler=new Handler() {
@Override
public void handleMessage(Message msg) {
m_ProgressBar.incrementProgressBy(5);
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
70
m_ProgressBar=(ProgressBar)findViewById(R.id.progressBar1);
}
public void onStart() {
super.onStart();
m_ProgressBar.setProgress(0);
Thread background=new Thread(new Runnable() {
public void run() {
try {
for (int i=0;i<20 && isRunning;i++) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
}
catch (Throwable t) {
}
}});
isRunning=true;
background.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
isRunning=false;
}
}
71
Trong phần khởi tạo của Activity, chúng ta tạo một đối tượng của lớp Handler. Ở
đây chúng ta đã overide lại phương thức handleMessage(). Trong hàm này , cứ mổi
lần nhận được một message từ thread khác, nó sẻ tăng giá trị của progressbar thêm
5.
Trong hàm onStart(), chúng ta tạo một background thread. Thread này đại diện cho
những thread thực hiện những tác vụ tốn nhiều thời gian. Vì vậy trong hàm run()
chúng ta cho thread này sleep một giây, cứ sau mổi giây, thread này sẽ gửi cho
Handler một message. Mặc định của progress bar thì giá trị max là 100. Nhưng
chúng ta có thể đặt lại giá trị này bằng phương thức “setMax()”, như đặt là số dòng
trong một truy vấn cơ sở dữ liệu, …
6.3.2. Sử dụng Runable
Runnable là giao diện nằm trong gói “java.lang”.
Để gưởi một message đến Handler qua Runable cần thực hiện hai bước sau :
Tạo một đối tượng Runnable.
Gửi message đến Handler qua phương thức “post(Runnable r)”.
Để thực hiện việc thay đổi trên giao diện, triển khai những thay đổi đó trong
phương thức run() của giao diện Runable.
Dưới đây là một ví dụ về sử dụng Runnable :
package de.vogella.android.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
72
import android.widget.ProgressBar;
public class ProgressTestActivity extends Activity {
private Handler handler;
private ProgressBar m_ProgressBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1);
handler = new Handler();
startProgress();
}
public void startProgress() {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
final int value = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
73
@Override
public void run() {
m_ProgressBar.setProgress(value);
}
});
}
}
};
new Thread(runnable).start();
}
}
Khi gọi phương thức handler.post(Runable r). Thì nó sẽ add đối tượng Runnable
vào hàng dợi message, và sẽ được thực thi bởi đối tượng handler.
6.4. AsyncTask
AsyncTask là một lớp ảo trong gói “android.os” của hệ điều hành Android. Lớp này
cung cấp cho chúng ta những phương thức để quản lý việc tương tác giữa UI thread
và background thread. Nó được triển khai bằng cách thừa kế lớp AsyncTask và triển
khai những phương thức đã được định nghĩa trước của nó. Lớp AsyncTask sử dụng
ba kiểu generic : Params, Progress, Result. Và bốn phương thức để chúng ta tương
tác giữa UI thread, và background thread : onPreExecute, doInBackground,
onProgessUpdate và onPostExecute.
Những kiểu generic mà AsyncTask sử dụng :
Params : kiểu của đối số được truyền cho background thread. Ví dụ như khi chúng
ta có một lớp con của AsyncTask có nhiệm vụ download dữ liệu. Thì chúng ta cần
74
truyền cho đối tượng này một chuổi String là đường dẫn đến nguồn download. Khi
đó Params là kiểu String.
Progress : là kiểu được trả về trong quá trình đang thực hiện tính toán của
background thread. Ví dụ như trong quá trình download , bạn muốn thông báo cho
người dùng biết đã download được bao nhiêu % chẳng hạn. Thì khi đó Progress có
thể là kiểu int.
Result : kiểu của kết quả trả về từ background thread. Ví dụ bạn cần background
thread download dữ liệu thì kế quả có thể là một mảng byte. Thì khi đó Result là
kiểu byte[].
Mổi khi chúng ta muốn sử dụng AsyncTask , phải chỉ ra cả ba kiểu trên. Nếu chúng
ta không sử dụng một kiểu nào thì có thể dùng kiểu Void.
Ví dụ như :
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
6.4.1. Bốn bước được thực hiện trong AsyncTask
Khi một đối tượng AsyncTask được thực thi, nó sẽ lần lượt thực hiện các phương
thức :
onPreExecute() : được gọi trong UI thread, ngay sau khi đối tượng
AsyncTask được gọi thực thi. Vì phương thức này được thực hiện trong UI
thread, nên nó thường được dùng để thông báo cho người dùng biết là đang
bắt đầu thực thi background thread , ví dụ như hiển thị một Progess bar trong
giao diện người dùng.
doInBackground(Params…) : Được gọi trong background thread ngay sau
khi phương thức onPreExecute() được thực thi xong. Phương thức này được
dùng để thực thi những công việc tốn nhiều thời gian. Đây là phương thức
thực hiện những công việc chính của background thread. Đối số của hàm này
75
chính là đối số được truyền lúc gọi thực thi AsyncTask. Trong hàm này , bạn
có thể gọi phương thức publishProgress(Progress…) để truyền những kết
quả đã làm được hoặc là những thông báo cho người dùng, … Giá trị truyền
cho hàm publishProgress(Progress …) sẽ được xử lý trong UI thread trong
hàm onProgressUpdate(Progress…).
onProgressUpdate(Progress…): hàm này được gọi trong UI thread sau khi
phương thức publishProgress(Progress…) được gọi. Phương thức này
thường được dùng để hiển thị lên giao diện ứng dụng những thông tin mà
background thread muốn thông báo khi đang thực hiện tính toán. Ví dụ như
hiển thị % đã được hoàn thành trên Progressbar, hoặc hiển thị log trong một
text field.
onPostExecute(Result) : được gọi trong UI thread sau khi hàm
doInBackground() thực hiện xong. Kết quả của việc tính toán trong
background thread được truyền vào phương thức này. Vì nó được thực hiện
trong UI thread nên phương thức này thường được sử dụng để hiển thị kết
quả mà background thread đã thực hiện lên giao diện.
6.4.2. Cách sử dụng
Dưới đây là một ví dụ sử dụng AsyncTask :
package com.gtvt.example
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
public class ProgressTestActivity extends Activity {
private ProgressBar m_ProgressBar;
76
private ProgressTask m_ ProgressTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1);
m_ ProgressTask =new ProgressTask();
m_ ProgressTask.execute(10);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(task.getStatus() == AsyncTask.Status.RUNNING)
m_ ProgressTask.cancel(true);
}
class ProgressTask extends AsyncTask<Integer, Integer, Void>{
@Override
protected void onPreExecute() {
m_ProgressBar.setMax(100);
}
@Override
protected void onCancelled() {
m_ProgressBar.setMax(0);
}
77
@Override
protected Void doInBackground(Integer... params) {
// lay gia tri dau tien cua progress bar
int start=params[0];
// tang gia tri cua progress bar
for(int i=start;i<=100;i+=5){
try {
boolean cancelled=isCancelled();
if(!cancelled){
publishProgress(i);
SystemClock.sleep(1000);
}else{
break;
}
} catch (Exception e) {
Log.e("Error", e.toString());
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
m_ProgressBar.setProgress(values[0]);
}
78
@Override
protected void onPostExecute(Void result) {
Log.v("Progress", "Finished");
}
}
}
Trong ví dụ này thực hiện việc tăng dần giá trị cho progress bar. Và để giả sử là
AsyncTask đang thực hiện một tác vụ tốn thời gian nào đó, chúng ta dùng hàm
SystemClock.sleep() để cho thread tạm dừng trong vòng 1 giây.
Để sử dụng AsyncTask trong ví dụ này đã gọi như sau :
ProgressTask task=new ProgressTask();
task.execute(10);
Để dừng một AsyncTask đang chạy chúng ta có thể gọi phương thức
cancel(boolean). Và thường thì trước khi dừng một AsyncTask chúng ta phải kiểm
tra xem nó đã được dừng chưa bằng phương thức : isCancelled() và kiểm tra xem
nó có đang chạy không bằng cách so sánh : task.getStatus() ==
AsyncTask.Status.RUNNING.
6.5. Một số chú ý khi làm việc với background thread
Có nhiều trường hợp ứng dụng của bạn muốn thay đổi công việc của
background thread, hay muốn tạm dừng background thread khi người dùng
thao tác với giao diện. Vì vậy trong trường hợp này bạn phải giao tiếp được
với background thread. Android đã hổ trợ nhiều lớp trong gói
java.util.concurrent để bạn có thể giao tiếp an toàn với background thread.
79
Có trường hợp khi ứng dụng của bạn bị tắt đi trong khi background thread
vẩn tiếp tục thực thi. Ví dụ như sau khi bạn chạy ứng dụng, thì có một cuộc
gọi gọi tới, tiếp sau đó là thêm một tin nhắn tơi, rồi bạn tìm kiếm trong danh
bạ, … Những ứng dụng khác được mở ra liên tiếp như vậy có thể làm cho hệ
điều hành tắt đi ứng dụng ban đầu của bạn vì thiếu bộ nhớ, trong khi
background thread của bạn vẩn chạy. Để xử lý những trường hợp như thế
này bạn cần overive những phương thức khi mà ứng dụng của bạn tắt đi sẽ
được gọi để có thể tắt đi background thread của bạn. Những phương thức ví
dụ như : onStop() của activity…
Một chú ý nữa đó là bạn nên hiển thị cho người dùng biết là background
thread của bạn đang chạy. Ví dụ như dùng một ProgressBar chẳng hạn. Và
bạn nên chú ý khả năng gặp phải lổi trong background thread. Ví dụ như khi
bạn đang lấy thông tin từ Internet bằng một background thread. Và vì một lý
do gì đó, Internet bị ngắt chẳng hạn.
80
Chương 7 LƯU TRỬ DỮ LIỆU
Hệ điều hành Android cung cấp cho chúng ta một vài cách để có thể lưu trử được
dữ liệu của ứng dụng. Tùy từng trường hợp mà bạn chọn cho mình cách nào, ví dụ
như dữ liệu đó là private hay là có thể cho ứng dụng khác truy cập ,hay bạn muốn
lưu trử nhiều hay ít, …
Gồm có các lựa chọn sau :
Cơ sở dữ liệu SQLite : như đã giới thiệu ở chương 7.
Dùng Shared Preferences :chứa dữ liệu đơn giản, và là private với các ứng
dụng khác. Dữ liệu được chứa theo từng cặp : key –values.
Bộ nhớ trong của máy : chứa dữ liệu trong bộ nhớ trong của điện thoại.
Bộ nhớ ngoài : chứa dữ liệu ở bộ nhớ ngoài, như là thẻ nhớ.
Chứa dữ liệu trên mạng : chứa dữ liệu trên mạng , ở một server nào đó.
6.6. Dùng Shared Preferences
Lớp SharedPreferences trong gói “android.content” cung cấp cho chúng ta cách
thức để lưu và lấy lại dữ liệu bằng những cặp khóa – giá trị. Có thể sử dụng lớp
SharedPreferences để lưu những kiểu cơ bản bao gồm : boolean, float, int, long và
String.
Để lấy đối tượng SharedPreferences của ứng dụng, chúng ta có thể sử dụng hai
phương thức:
getSharedPreferences() : sử dụng khi cần tạo nhiều file chứa dữ liệu bằng
SharedPreferences có thể gọi phương thức này từ context của ứng dụng.
Phương thức này gồm hai đối số :
o name : Tên file preference. Nếu file preference chưa tồn tại nó sẽ
được tạo ra khi bạn tạo một editor (sẽ được nói ở dưới) bằng phương
81
thức SharedPreferences.edit() và commit bằng phương thức
Editor.commit().
o Mode : là đối số thứ hai. Chỉ ra quyền thao tác: MODE_PRIVATE, là
giá trị default. Nghĩa là chỉ có ứng dụng tạo ra có thể thao tác được.
MODE_WORLD_READABLE: cho phép tất cả các ứng dụng có thể
đọc được. MODE_WORLD_WRITEABLE : cho phép tất cả ứng
dụng có thể đọc ghi.
getPreferences(): dùng phương thức này khi bạn chỉ cần một file preference
để chứa dữ liệu. Trong trường hợp này bạn không cần truyền tên file.
Phương thức này có một đối số là mode , tương tự với đối số thứ 2 của hàm
getSharedPreferences() đã nói ở trên.
Cả hai phương thức này đều trả về một đối tượng SharedPreferences để chúng ta
đọc và ghi giá trị lên file preference.
Các giá trị preference khi được lưu lại sẽ được ghi vào một file xml có đường dẫn là
:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
Hoặc tên mặc định là(khi sử dụng phương thức : getPreferences()) :
/data/data/YOUR_PACKAGE_NAME/shared_prefs/
YOUR_PACKAGE_NAME_preferences.xml
Ở đây :
YOUR_PACKAGE_NAME : là tên gói của ứng dụng.
YOUR_PREFS_NAME : tên của file preference được truyền vào qua hàm
getSharedPreferences().
Để ghi dữ liệu chúng ta cần tạo một đối tượng của giao diện
SharedPreferences.Editor. Đây là một giao diện được sử dụng để ghi giá trị vào đối
tượng SharedPreferences. Tất cả những giá trị bạn ghi vào editor đều được giữ lại
82
cho đến khi bạn gọi phương thức commit(), hoặc apply(). Thì lúc đó dữ liệu mới
được ghi xuống vào SharedPreferences. Nếu commit() thất bại thì tất cả dữ liệu
trong Editor sẽ không được lưu lại.
Để thêm giá trị vào một đối tượng Editor dùng các phương thức như là :
putBoolean(), putString(), …
Để đọc dữ liệu , chúng ta sử dụng các phương thức getBoolean() , getString() , … từ
một đối tượng SharedPreferences. Dựa vào khóa, để chúng ta có thể lấy được giá trị
của mình.
Dưới đây là một ví dụ khá dễ hiểu từ website : http://developer.android.com của
google.
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
@Override
protected void onCreate(Bundle state){
super.onCreate(state);
. . .
// doc gia tri
SharedPreferences settings = getSharedPreferences(PREFS_NAME,
Context.MODE_PRIVATE);
boolean silent = settings.getBoolean("silentMode", false);
setSilent(silent);
}
@Override
protected void onStop(){
83
super.onStop();
SharedPreferences settings = getSharedPreferences(PREFS_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", mSilentMode);
// Commit the edits!
editor.commit();
}
}
6.7. Sử dụng file hệ thống .
Hệ điều hành Android sử dụng file hệ thống dựa trên hệ điều hành Linux và hổ trợ
chia quyền. Có một vài cách để bạn có thể truy cập vào file hệ thống. Bạn có thể tạo
và đọc file của ứng dụng, củng có thể đọc file có sẵn trong resource, …
6.7.1. Sử dụng bộ nhớ trong.
Chúng ta có thể ghi dữ liệu vào file ở bộ nhớ trong của điện thoại. Mặc định thì file
lưu trong bộ nhớ trong là private với ứng dụng của bạn, và các ứng dụng khác
không thể truy cập được. Khi người dùng gở bỏ ứng dụng , những file đó sẽ bị xóa
cùng ứng dụng.
Khi bạn lưu file vào bộ nhớ trong, nó sẽ được lưu theo đường dẫn :
data/data/[PACKAGE_NAME]/files/[FILE_NAME]
84
Để có thể ghi file chúng ta cần mở một Outputstream tới file cần ghi. Để làm được
điều đó chúng ta gọi phương thức openFileOutput() từ context của ứng dụng.
Phương thức này trả về cho chúng ta một FileOutputStream . Cần truyền hai đối số :
Name: tên file
Mode: chỉ ra quyền truy cập vào file . Bao gồm các quyền :
o MODE_PRIVATE : chỉ ứng dụng tạo ra file mới được truy cập.
o MODE_WORLD_READABLE : cho phép tất cả các ứng dụng có thể
đọc file.
o MODE_WORLD_WRITEABLE : cho phép những ứng dụng khác có
thể truy cập vào file.
o MODE_APPEND : nếu file đã có sẵn thì ghi tiếp dữ liệu vào cuối file
thay vì xóa file củ, ghi lại file mới.
Dưới đây là một ví dụ đơn giản để ghi file:
String FILENAME = "hello_file";
String string = "hello world!";
Context context = getApplicationContext();
FileOutputStream fos = null;
try {
fos = context.openFileOutput(FILENAME,
Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
} catch (IOException e) {
Log.v(TAG, e.toString());
} final{
if(fos != null){
try{
85
fos.close();
}catch(IOException e) {
Log.v(TAG, e.toString());
}
}
Khi đã có được Outputstream, chúng ta thao tác hoàn toàn giống như lập trình trong
java.
Để đọc dữ liệu chúng ta phải mở một InputStream bằng phương thức :
openFileInput(). Phương thức này sẽ trả về một đối tượng FileInputStream.
Ví dụ về đọc file hệ thống.
String FILENAME = "hello_file";
String string = null;
Context context = getApplicationContext();
FileInputStream fis = null;
try {
fis = context.openFileInput(FILENAME);
byte[] reader = new byte[fis.available()];
while (fis.read(reader) != -1) {}
string = new String(reader);
} catch (IOException e) {
Log.v(TAG, e.toString());
} final{
if(fis != null){
try{
fis.close();
}catch(IOException e) {
Log.v(TAG, e.toString());
}
86
}
}
6.7.2. Lưu trử vào file cache
Nếu bạn muốn cache dữ liệu, thay vì tạo một file rồi ghi dữ liệu vào đó, bạn có thể
sử dụng phương thức getCacheDir() của Context của ứng dụng để lấy đường dẫn
của thư mục chứa file tạm của Android. Sau đó bạn tạo file tạm, và lưu dữ liệu vào
đó.
Khi thiết bị thiếu bộ nhớ trong, hệ điều hành có thể xóa những file tạm này. Tuy
nhiên chúng ta không nên để hệ điều hành làm việc này , mà nên đặt một giới hạn
nào đó cho cache file ví dụ như là 1MB. Khi cache file quá giới hạn này thì nên
thực hiện xóa , hay lược bớt.
Khi bạn gở bỏ chương trình thì những file tạm này củng bị xóa.
Dưới đây là một ví dụ :
File file = new File(context.getCacheDir(), "temp.txt");
try {
file.createNewFile();
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("Hello World");
bw.newLine();
bw.close();
} catch (IOException e) {
Log.v(TAG, e.toString());
} final{
87
try{
if(fw != null)
fw.close();
if(bw!=null)
bw.close();
}catch(IOException e) {
Log.v(TAG, e.toString());
}
}
6.7.3. Đọc file từ resources
Trong trường hợp chúng ta đã có những file có sẵn , và muốn đưa vào ứng dụng sử
dụng. Chúng ta có thể đưa những file đó vào thư mục “res/raw”. Khi compile thành
file apk, file này cũng sẻ được nén cùng. Và để đọc những file này chúng ta có thể
thực hiện như ví dụ sau :
Resources resources = context.getResources();
InputStream is = null;
String string = null;
try {
is = resources.openRawResource(R.raw.filename);
byte[] reader = new byte[is.available()];
while (is.read(reader) != -1) {}
string = new String(reader);
} catch (IOException e) {
Log.e("ReadRawResourceFile", e.getMessage(), e);
} finally {
if (is != null) {
try {
88
is.close();
} catch (IOException e) {
}
}
}
Có một chú ý là chúng ta không thể ghi lên file trong resources . Chúng ta chỉ có thể
đọc.
6.7.4. Sử dụng bộ nhớ ngoài
Các thiết bị Android đều hổ trợ bộ nhớ ngoài được dùng để lưu trử dữ liệu. Đó có
thể là một thiết bị nhớ có thể di chuyển được ví dụ như là sd card hay có bộ nhớ
không thể tháo ra được. Dử liệu lưu trử trong bộ nhớ ngoài có thể được truy cập từ
bất cứ ứng dụng nào. Và củng có thể truy cập được bằng cách kết nối chế độ USB
với máy tính.
Trước khi sử dụng bộ nhớ ngoài, chúng ta phải luôn kiểm tra xem nó có tồn tại hay
có thể sử dụng được không bằng phương thức :
Environment.getExternalStorageState(), Environment là lớp nằm trong gói :
android.os . Bộ nhớ ngoài có thể đang được kết nối với máy tính, không tồn tại, chỉ
được quyền đọc, …Dưới đây là một ví dụ để kiểm tra bộ nhớ ngoài :
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// co the doc va ghi
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
89
// chi co the doc
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// khong the doc hoac ghi
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
Ví dụ vừa rồi kiểm tra xem bộ nhớ ngoài có thể đọc và viết hay không. Phương thức
getExternalStorageState() trả về trạng thái của bộ nhớ ngoài. Dựa vào đó bạn có thể
thông báo với người dùng những thông tin cần thiết khi truy cập bộ nhớ ngoài.
Sau khi kiểm tra và bạn có thể truy cập đến bộ nhớ ngoài. Bước tiếp theo là chúng
ta sẽ truy cập nó như thế nào. Nếu bạn sử dụng api level 8 trở lên, thì sử dụng
phương thức getExternalFilesDir() từ context của ứng dụng . Phương thức này có
một đối số “type” để chỉ ra thư mục con của bộ nhớ ngoài mà bạn muốn , ví dụ như
DIRECTORY_MUSIC ,DIRECTORY_RINGTONES, … Để lấy về thư mục gốc
của bộ nhớ ngoài bạn có thể truyền null. Ngoài ra, nếu bạn chỉ ra đối số type mà
trong bộ nhớ ngoài chưa có, thì sau khi gọi phương thức này nó sẽ tự tạo thu mục
đó cho bạn. Nếu người dùng gở bỏ ứng dụng của bạn thì thư mục đó và toàn bộ dữ
liệu sẽ bị xóa.
Nếu bạn sử dụng API level 7 trở xuống, có thể sử dụng phương thức
Environment.getExternalStorageDirectory() để lấy thư mục gốc của bộ nhớ ngoài.
Dưới đây là một ví dụ về tạo file trong bộ nhớ ngoài :
void createExternalStorageFile(Context context) {
//tao file moi
File file = new File(context.getExternalFilesDir(null), "DemoFile.txt ");
try {
90
file.createNewFile();
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("Hello World");
bw.newLine();
bw.close();
} catch (IOException e) {
Log.w("ExternalStorage", "Error writing " + file, e);
}
}
91
Chương 8LOCATION VÀ MAP
Một chức năng phổ biến mà hiện tại các thiết bị di động đang sử dụng đó là GPS.
Hiện nay , GPS không phải là cách duy nhất để một thiết bị di động có thể biết được
vị trí của bạn. Ngoài GPS còn có một số cách sau :
Dựa vào cột thu- phát sóng điện thoại gần nhất nơi bạn đang đứng.
Dựa vào sóng Wifi gần nhất mà bạn đang đứng.
Nhờ chức năng này mà thiết bị có thể nói cho bạn biết bạn đang ở đâu, và được sử
dụng nhiều nhất với các ứng dụng bản đồ và chỉ đường. Và android cung cấp cho
chúng ta hai API rất tốt để có thể phục vụ mục tiêu này. Đó là : Map API và
Location API.
6.2. Map API
Map API trong Android cung cấp cho chúng ta những công cụ cần thiết để hiển thị
bản đồ và có thể thao tác với nó. Ví dụ như có thể Zoom , pan, ..
Để làm quen với Map API trước hết cần tìm hiểu cách sử dụng lớp MapView. Để
làm việc với lớp MapView chúng ta cần phải có một map-api key từ Google vì
MapView lấy dữ liệu từ dịch vụ của Google.
6.2.1. Cách lấy một map-api key từ Google
Chúng ta cần phải hiểu như sau :
Để hiển thị bản đồ trên MapView, chúng ta phải có một map api key.
Mổi MapView phải có một map api key, và map api key phải được đăng ký
với chứng nhận được sử dụng cho ứng dụng. Ở đây cần giải thích thêm là ,
92
một ứng dụng Android cần có một chứng nhận, được gọi là digital signature
vertificate. Và map api key phải được đăng ký với chứng nhận này.
Tất cả các MapView trong một ứng dụng có thể sử dụng chung một map api
key.
Chúng ta cần sử dụng hai map-api key. Một cho việc sử dụng trên emulator,
và một cho sản phẩm lúc đưa ra sử dụng trên thiết bị. Lý do của việc này là
vì , khi chúng ta phát triên ứng dụng, sử dụng ADT plug-in để build file apk
và cài đặt file apk đó lên emulator. Lúc này file apk phải được đăng ký một
chứng nhận như đã nói ở trên. ADT plug-in đã sử dụng chứng nhận debug
trong quá trình phát triển ứng dụng. Để có thể đưa ứng dụng ra thị trường
bạn cần phải có một chứng nhận khác cho apk file. Nên lúc này bạn cần một
map api key khác.
Để lấy được một map api key, chúng ta cần phải có được chứng nhận (digital
signature vertificate) mà bạn sẻ dùng cho ứng dụng. Chúng ta sẻ sử dụng chứng
nhận debug của ADT plug-in. Google yêu cầu chúng ta nhập vào mả hóa MD5
fingerprint của chứng nhận này sau đó Google sẻ sinh ra map api key cho chúng ta.
Chứng nhận của ADT plug in thường nằm ở : C:\Documents and Settings\
<username>\Local Settings\Application Data\Android\debug.keystore
Để chắc chắn , bạn có thể tìm chứng nhận của ADT plug in như sau : mở Eclipse
lên, vào menu :
Window->preferences->Android->Build. Vị trí của chứng nhận sẻ nằm ở : “Default
debug key-store”.
Như hình 10-1 :
93
Hình 10-30 Cách lấy chứng nhận debug.
Để có được mả hóa MD5 fingerprint của chứng nhận bạn phải chạy keytool trong
jre. Thường nằm mặc định ở : C:\Program Files\Java\jdk\jre\bin. Mở commandline
lên, chuyển đến thư mục hiện hành là thư mục bin của jre. Gỏ lệnh như sau :
keytool -list –keystore “your debug.keystore file patch”
Sau đó bạn sẽ được yêu cầu nhập mật khẩu. Mặc định mật khẩu của chứng chỉ ADT
plug-in là : android.
Dưới đây là ví dụ :
94
Hình 10-31 Ví dụ lấy MD5 fingerprint
Sau khi có được mả của MD5 fingerprint. Chúng ta nhập vào trang web của google
theo link sau :
http://code.google.com/android/maps-api-signup.html
Click vào nút : Generate API key.
Như vậy là bạn đã có được map api key. Bây giờ chúng ta sẻ bắt đầu sử dụng
MapView.
6.2.2. MapView và MapActivity
Có nhiều thành phần hổ trợ MapView trong Android. Và một class mở rộng của
Activity hổ trợ MapView là MapActivity. MapView và MapActivity là hai lớp hổ
trợ nhau khá tốt trong việc hiển thị và thao tác với bản đồ. Để hiển thị một
MapView chúng ta phải đặt nó trong một MapActivity. Thêm nữa, như đã nói ở
trên, khi sử dụng MapView cần phải có map api key. Nếu khởi tạo MapView trong
file xml, thì phải đặt map api key bằng thuộc tính : android:apiKey. Nếu khởi tạo
bằng Constructor thì bạn phải truyền map api key vào cho constructor của
MapView. Vì mapview phải lấy dữ liệu từ google nên bạn phải cấp quyền truy cập
internet cho ứng dụng của mình trong file AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET" />
95
Dưới đây là hình của một ví dụ sử dụng MapView và MapActivity :
Hình 10-32 Ví dụ sử dụng MapView
Trước hết chúng ta cần tạo file layout : mapview.xml nội dung như sau :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.google.android.maps.MapView android:id="@+id/mapview"
android:layout_width="fill_parent"
96
android:layout_height="wrap_content"
android:apiKey="040-Yr0ZWi2scm-fazCcXKq2zZaglz-Ncv1IgQg"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/zoomCtrls"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
</LinearLayout>
</RelativeLayout>
Ở đây chúng ta sử dụng Relative layout, chứa một mapview và một LinearLayout.
Và bạn phải gán map api key như ví dụ vào thuộc tính : android:apiKey. Chúng ta
sử dụng linearlayout “zoomCtrls” để chứa những control của mapview.
Đây là phần code java sử dụng MapActivity:
package com.gtvt.hellomapview.activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class HelloMapView extends MapActivity {
private MapView m_MapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
97
setContentView(R.layout.mapview);
m_MapView = (MapView)findViewById(R.id.mapview);
LinearLayout layout = (LinearLayout)findViewById(R.id.zoomCtrls);
layout.addView(m_MapView.getZoomControls());
m_MapView.setClickable(true);
}
@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}
}
Điều đáng chú ý trong lớp này là chúng ta sử dụng linearlayout zoomCtls để chứa
ZoomControl của mapview. Điều này có nghĩa là mapview đã có săn các control
cho phép bạn phóng to và thu nhỏ bản đồ. Điều mà chúng ta cần làm đó là tham
chiếu đến những control này và hiển thị chúng trên layout. Dòng cuối cùng trong
hàm onCreate() : mapView.setClickable(true), cho phép chúng ta có thể pan trên
bản đồ.
Phần tiếp theo chúng ta sẽ tìm hiểu làm sao để có thể thêm những dữ liệu khác lên
mapview.
6.2.3. Sử dụng Overlays
Google cung cấp cho chúng ta khả năng có thể hiển thị những dữ liệu của chúng ta
lên một mapview. Để làm được điều này , chúng ta phải thêm những layer khác lên
98
trên cùng của bản đồ. Android cung cấp cho chúng ta một số lớp để chúng ta có thể
thêm những layer khác vào map. Các lớp này thường chứa từ “Overlay” trong tên
của mình. Dưới đây là một ví dụ mở rộng của ví dụ trên, có sử dụng lớp
ItemizedOverlay.
package com.gtvt.hellomapview.activity;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.LinearLayout;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
public class HelloMapView extends MapActivity {
private MapView m_MapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
99
setContentView(R.layout.mapview);
m_MapView = (MapView)findViewById(R.id.mapview);
LinearLayout layout = (LinearLayout)findViewById(R.id.zoomCtrls);
layout.addView(m_MapView.getZoomControls());
m_MapView.setClickable(true);
Drawable
marker=getResources().getDrawable(R.drawable.mapmarker);
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
m_MapView.getOverlays().add(new InterestingLocations(marker));
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
class InterestingLocations extends ItemizedOverlay {
private List<OverlayItem> locations = new
ArrayList<OverlayItem>();
private Drawable marker;
public InterestingLocations(Drawable marker) {
super(marker);
this.marker=marker;
GeoPoint hanoi = new
GeoPoint((int)(21.0333333*1000000),(int)(105.85*1000000));
100
GeoPoint hochiminh = new
GeoPoint((int)(10.8230989*1000000),(int)(106.6296638*1000000));
locations.add(new OverlayItem(hanoi ,
"Ha Noi, Vietnam's capital", "Ha Noi, Vietnam's
capital"));
locations.add(new OverlayItem(hochiminh ,
"Ho Chi Minh city", "Ho Chi Minh city"));
populate();
}
@Override
public void draw(Canvas canvas, MapView mapView, boolean
shadow) {
super.draw(canvas, mapView, shadow);
boundCenterBottom(marker);
}
@Override
protected OverlayItem createItem(int i) {
return locations.get(i);
}
@Override
public int size() {
return locations.size();
}
}
}
101
Hình 10-33 Ví dụ sử dụng Overlay
Như đã nói ở trên, để thêm những điểm đánh dấu trên bản đồ, chúng ta phải sử dụng
lớp com.google.android.maps.Overlay. Đây là một lớp ảo, vì vậy để sử dụng nó
chúng ta phải thừa kế, hoặc là sử dụng những lớp con của nó. Trong ví dụ trên,
chúng ta đã viết lớp InterestingLocations thừa kế từ lớp ItemizedOverlay, đây là lớp
con của lớp Overlay. ItemizedOverlay là lớp hổ trợ cho chúng ta trong việc tạo một
danh sách những điểm cần đánh dấu trong bản đồ. Mổi điểm đánh dấu trên bản đồ
là một đối tượng của lớp OverlayItem. Hàm size() trả về số lượng cần đánh dấu trên
bản đồ, hàm createItem(int i) trả về điểm đánh dấu thứ i.
Cách thường gặp nhất trong việc sử dụng lớp ItemizedOverlay là thừa kế lớp này,
và thêm những điểm cần đánh dấu lên bản đồ trong hàm tạo của lớp đó. Sau đó gọi
102
phương thức populate() của lớp ItemizedOverlay. Phương thức này sẽ gọi ngay đến
phương thứ size() để lấy được số lượng điểm đánh dấu. Sau đó mở một vòng lặp ,
gọi phương thức createItem(int i) mà bạn đã overide và lấy được điểm cần đánh dấu
trên bản đồ mà chúng ta đã tạo ra trong hàm tạo của lớp.
Một điều cần phải quan tâm ở đây nữa là trong hàm tạo của lớp OverlayItem, bạn
cần phải có một đối tượng GeoPoint. Lớp GeoPoint đại diện cho một địa điểm trên
trái đất, bao gồm kinh độ và vĩ độ. Chúng ta nhân với 1.000.000 là vì hàm tạo của
GeoPoint sử dụng vi độ, và là kiểu int.
6.3. Location base service
Gói android.location cung cấp cho chúng ta dịch vụ location(Location based
services). Chúng ta sẽ tìm hiểu hai thành phần quan trọng nhất của gói này đó là :
dịch vụ LocationManager và Geocoder.
6.1.1. Dịch vụ LocationManager
Location base service cung cấp cho chúng ta khả năng biết được vị trí địa lý hiện tại
của thiết bị. Và có thể đăng ký để biết được khi nào thiết bị đến một vị trí địa lý đặc
biệt nào đó. Thành phần chính của dịch vụ này là dịch vụ của hệ điều hành
LocationManager. Bây giờ chúng ta sẽ tìm hiểu cách sử dụng dịch vụ này.
Trước hết, muốn sử dụng dịch vụ, chúng ta phải có một tham chiếu đến dịch vụ đó.
Đây là một ví dụ :
package com.gtvt. hellomapview.activity;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.location.Location;
103
import android.location.LocationManager;
import android.os.Bundle;
public class LocationManagerDemoActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocationManager m_locMgr =
(LocationManager)this.getSystemService(Context.LOCATION_SERVICE);
Location loc =
m_locMgr.getLastKnownLocation(LocationManager.GPS_PROVIDER);
List<String> providerList = locMgr.getAllProviders();
}
}
Dịch vụ LocationManager là một dịch vụ hệ thống nên chúng ta không thể khởi tạo
nó. Bạn có thể truy cập đến dịch vụ này từ context và sử dụng tên của dịch vụ để
gọi nó. Bạn có thể sử dụng phương thức getSystemService() của Context của ứng
dụng để gọi dịch vụ này, bạn phải truyền đối số là tên của dịch vụ , trong ví dụ trên
là : Context.LOCATION_SERVICE.
Dịch vụ LocationManager cung cấp vị trí địa lý bằng cách sử dụng GPS hoặc là
Network (mạng internet). GPS sử dụng hệ thống định vị toàn cầu để lấy được thông
tin về vị trí của thiết bị. Còn Network sử dụng trạm thu phát sóng điện thoại hoặc vị
trí phát wifi gần nhất để lấy thông tin vị trí của thiết bị. Lớp LocationManager cung
cấp vị trí gần nhất mà thiết bị nhận biết được qua phương thức
getLastKnowLocation(). Đối số truyền cho hàm này thông báo cho dịch vụ biết sẽ
lấy thông tin vị trí từ GPS “LocationManager.GPS_PROVIDER” hay là Network
“LocationManager.Network”. Kết quả trả về là một đối tượng của lớp
104
android.location.Location. Đối tượng của lớp này cung cấp cho chúng ta những
thông tin về vị trí hiện tại của thiết bị như : kinh độ, vĩ độ, độ cao của thiết bị, tốc
độ, thời gian mà vị trí được tính toán..
Vì LocationManager tính toán vị trí dựa trên nhà cung cấp (GPS hay là Network),
lớp này cho phép chúng ta lấy được danh sách những nhà cung cấp bằng phương
thức : getAllProviders().
Như đã nói ở phần đầu, một lợi ích quan trọng của dịch vụ LocationManager là
nhận thông báo về những thay đổi của vị trí của thiết bị. Dưới đây là một ví dụ sử
dụng chức năng này của LocationManager, để làm được việc này chúng ta phải
đăng ký một listener để nhận những sự kiện về thay đổi vị trí từ LocationManager.
package com.gtvt.hellomapview.activity;
import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.Toast;
public class LocationUpdateDemoActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
LocationManager m_locMgr = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
LocationListener locListener = new LocationListener(){
public void onLocationChanged(Location location){
if (location != null){
105
Toast.makeText(getBaseContext(),"New location
latitude [" +
location.getLatitude() + "] longitude [" +
location.getLongitude()+"]",
Toast.LENGTH_SHORT).show();
}
}
public void onProviderDisabled(String provider){}
public void onProviderEnabled(String provider){}
public void onStatusChanged(String provider, int status,
Bundle extras){}
};
m_locMgr.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,
0, locListener);
}
}
Từ ví dụ trên, bạn có thể thấy cách đăng ký với dịch vụ LocationManager một
LocationListener, đó là gọi phương thức requestLocationUpdates() để đăng ký đối
tượng LocationListener. Khi vị trí của thiết bị thay đổi, dịch vụ LocationManager sẽ
gọi phương thức onLocationChanged() của đối tượng listener, và truyền vào là
Location mới của thiết bị. Trong ví dụ trên , khi vị trí của thiết bị thay đổi, chương
trình chỉ hiển thị lên một Toast thông báo về vị trí mới của thiết bị.
Chú ý, chúng ta sử dụng emulator , mà emulator thì lại không có GPS hoặc là các
trạm thu phát sóng điện thoại. Vì vậy, để có thể sử dụng dịch vụ LocationManager
với emulator , chúng ta có thể gưởi vị trí bất kì cho emulator bằng cách như sau.
106
Chạy emulator, sau đó mở cửa sổ Emulater Control trong eclipse lên ( cửa sổ này
nằm trong chế độ xem DDMS trong eclipse). Trong cửa sổ này , bạn có thể đặt kinh
độ và vĩ độ bất kỳ cho emulator ở phần Location Control. Như hình 10-5.
10-34. Đặt kinh độ và vĩ độ cho emulator.
Đây là kết quả sau khi chạy chương trình trên, và gưởi vị trí location mới cho
emulator.
107
6.1.2. Geocoding với Android
Làm việc với bản đồ, bạn có thể muốn đổi từ tên một địa điểm thành vị trí kinh độ,
vĩ độ. Việc đổi như trên gọi là geocoding. Và Android cung cấp lớp
android.location.Geocoder hổ trợ cho chúng ta khả năng đổi như trên và ngược lại,
từ kinh độ, vĩ độ sẽ đổi thành một danh sách địa chỉ. Lớp này cung cấp các phương
thức như :
List<Address> getFromLocation(double latitude, double logtitude, int
maxResults);
List<Address> getFromLocationName(String locationName, int maxResults,
double lowerLeftLatitude, double lowerLeftLongitude, double
upperRightLatitude, double upperRightLongitude);
List<Address> getFromLocationName(String locationName, int
maxResults);
Đây là các phương thức trả về một danh sách các Address, lớp
android.location.Address đại diện cho một địa chỉ thực tế, chứa các thông tin về địa
chỉ đó. Vì trả về cả một danh sách nên chúng ta có thể hiểu rằng việc tính toán địa
chỉ không phải là một phép tình hoàn toàn chính xác. Một địa chỉ có thể có nhiều
cách tìm. Ví dụ như : dựa vào tên của địa điểm, dựa vào vị trí địa lý kinh độ, vĩ độ,
…Vì kết quả của phương thức trả về một danh sách nên chúng luôn có một đối số
giới hạn danh sách các địa chỉ tìm được là maxResult( giá trị từ 1 –> 5). Dưới đây là
một ví dụ sử dụng Geocoder.
File goecode.xml chứa layout của ví dụ:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="fill_parent"
108
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:orientation="vertical" >
<EditText android:layout_width="fill_parent"
android:id="@+id/location"
android:layout_height="wrap_content"
/>
<Button android:id="@+id/geocodeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Find Location"/>
</LinearLayout>
<com.google.android.maps.MapView
android:id="@+id/geoMap" android:clickable="true"
android:layout_width="fill_parent"
android:layout_height="320px"
android:apiKey="@string/mapskey"
/>
</RelativeLayout>
Activity của ví dụ:
package com.gtvt.hellomapview.activity
import java.io.IOException;
import java.util.List;
import android.app.ProgressDialog;
import android.location.Address;
import android.location.Geocoder;
import android.os.AsyncTask;
109
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class GeocodingDemoActivity extends MapActivity{
public static final String TAG = "GeocodingDemoActivity";
private ProgressDialog m_ProgressBar = null;
private Geocoder m_Geocoder = null;
private MapView m_MapView = null;
@Override
protected boolean isRouteDisplayed() {
return false;
}
@Override
protected void onCreate(Bundle icicle){
super.onCreate(icicle);
setContentView(R.layout.geocode);
110
m_MapView = (MapView)findViewById(R.id.geoMap);
Button geoBtn =(Button)findViewById(R.id.geocodeBtn);
m_Geocoder = new Geocoder(this);
geoBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
EditText loc = (EditText)findViewById(R.id.location);
String locationName = loc.getText().toString();
new GettingAdresses().execute(locationName);
}
});
}
private class GettingAdresses extends AsyncTask<String, Void,
List<Address>>{
@Override
protected void onPostExecute(List<Address> addressList) {
m_ProgressBar.dismiss();
if(addressList!=null && addressList.size()>0){
int lat = (int)addressList.get(0).getLatitude()*1000000;
int lng =
(int)addressList.get(0).getLongitude()*1000000;
GeoPoint pt = new GeoPoint(lat,lng);
m_MapView.getController().setZoom(10);
m_MapView.getController().setCenter(pt);
m_MapView.getController().animateTo(pt);
}else{
111
Toast.makeText(GeocodingDemoActivity.this, "Can't
get the address", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onPreExecute() {
m_ProgressBar =
ProgressDialog.show(GeocodingDemoActivity.this,
"Searching..", "Wait for a moment !");
}
@Override
protected List<Address> doInBackground(String... locationNames) {
if(locationNames.length < 1)
return null;
try {
List<Address> addressList =
m_Geocoder.getFromLocationName(locationNames[0], 5);
return addressList;
} catch (IOException e) {
Log.v(TAG, e.toString());
return null;
}
}
}
}
112
Cần thêm những quyền sau vào file AndroidManifest.xml.
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
Có một chú ý quan trọng đó là hiện tại thì Emulator không hổ trợ geocoder. Vì vậy
nếu bạn chạy ví dụ trên với emulator thì luôn có một thông báo hiện lên là "Can't
get the address". Lúc này một exception sẻ được quăng ra :
GeocodingDemoActivity(905): java.io.IOException: Service not Available.
Nhưng nếu bạn sử dụng geocoder trên thiết bị thật và có kết nối Internet thì chương
trình sẽ chạy bình thường.
113
DANH MỤC TÀI LIỆU THAM KHẢO
Tiếng Anh
[1] Mark L.Murphy (2009), Beginning Android, APress, United States of America.
[2] Marko Gargenta (2011), Learning Android, O’Reilly Media, Inc.
114
top related