Contents
Các thành phần cơ bản của một ứng dụng Android
1.Activity
Định nghĩa Activity
Là thành phần tối quan trọng của bất kỳ một ứng dụng Android nào. Thuật ngữ Activity chỉ một việc mà người dùng có thể thực hiện trong một ứng dụng Android. Do gần như mọi activity đều tương tác với người dùng, lớp Activity đảm nhận việc tạo ra một cửa sổ (window) để người lập trình đặt lên đó một giao diện UI với setContentView(View). Một activity có thể mang nhiều dạng khác nhau: Một cửa sổ toàn màn hình (full screen window), một cửa sổ floating (với windowsIsFloating) hay nằm lồng bên trong 1 activity khác (với ActivityGroup).
Có 2 phương thức mà gần như mọi lớp con của Activity đều phải hiện thực:
- onCreate(Bundle) – Nơi khởi tạo activity. Quan trọng hơn, đây chính người lập trình gọi setContentView(int) kèm theo layout để thể hiện UI của riêng mình. Đồng thời còn có findViewById(int) giúp gọi các widget (buttons, text boxes, labels,..) để dùng trong UI.
- onPause() – Nơi giải quyết sự kiện người dùng rời khỏi activity. Mọi dữ liệu được người dùng tạo ra tới thời điểm này cần phải được lưu vào ContentProvider.
Để có thể sử dụng Context.startActivity(), mọi lớp activity đều phải được khai báo với tag <activity> trong file AndroidManifest.xml.
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.diendanhocweb.helloworld”><application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.MainActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” /><category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application></manifest>
Vòng đời của một activity
Các activity được quản lí dưới dạng các activity stack – First-In-Last-Out: Khi một activity mới được khởi tạo, nó sẽ được đưa lên trên cùng stack, các activity khác muốn chạy trên nền (foreground) trở lại thì cần phải chờ tới khi Activity mới này kết thúc.
Một Activity có 4 trạng thái:
Active hay Running: Khi một activity đang chạy trên màn hình.
Paused: Khi một activity vẫn đang chạy trên màn hình nhưng đang bị một activity trong suốt (transparent) hay không chiếm toàn màn hình hiển thị phía trên. Tuy vẫn lưu trữ dữ liệu, nhưng các paused activity này sẽ bị hệ thống bắt chấm dứt khi đang thiếu bộ nhớ trầm trọng.
Stopped: Khi 1 activity bị che khuất hoàn toàn bởi 1 activity khác. Tuy vẫn lưu trữ dữ liệu, nhưng các stopped activity này sẽ thường xuyên bị hệ thống bắt chấm dứt để dành chỗ cho các tiến trình khác.
Killed hay Shut down: Khi 1 activity đang paused hay stopped, hệ thống sẽ xóa activity ấy ra khỏi bộ nhớ.
Hình 1 – Lược đồ vòng đời của 1 activity
Dựa vào lược đồ trên, thấy được có 3 vòng lặp quan trọng sau:
Vòng đời toàn diện (Entire Lifetime): Diễn ra từ lần gọi onCreate(Bundle) đầu tiên và kéo dài tới lần gọi onDestroy() cuối cùng.
Vòng đời thấy được (Visible Lifetime): Diễn ra từ khi gọi onStart() và kéo dài tới khi gọionStop(). Ở vòng đời này, activity được hiển thị trên màn hinh nhưng có thế không tương tác với người dùng ở trên nền. Các phương thức onStart(0 và onStop() có thể được gọi nhiều lần.
Vòng đời trên nền (Foreground Lifetime): Diễn ra từ khi gọi onResume(0 và kéo dài tới khi gọi onPause(). Ở vòng đời này, activity nằm trên mọi activity khác và tương tác được với người dùng. 1 activity có thể liên tục thay đổi giữa 2 trạng thái paused và resumed, chẳng hạn khi thiết bị sleep hay 1 intent mới được đưa tới.
Toàn bộ vòng đời của 1 activity được định nghĩa nhờ các phương thức sau:
public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState);
//Gọi khi mới tạo activity để setup các view, binding dữ liệu,…
//Kèm theo sau luôn là onStart().
protected void onRestart();
//Gọi sau khi activity bị stopped và trước khi được khởi động lại.
//Kèm theo sau luôn là onStart().
protected void onStart();
//Gọi khi activity hiện lên trước mắt người dùng.
//Kèm theo sau là onResume() nếu activity hiện lên nền hay onStop(0 nếu bị ẩn đi.
protected void onResume();
//Gọi khi activity bắt đầu tương tác với người dùng và đang trên cùng của activity stack.
//Kèm theo sau luôn là onPause().
protected void onPause();
//Gọi khi hệ thống sắp khởi động lại 1 activity khác trướcđó.
//kèotheo sau là onresume nếu activity trở lại trên cùng hay onStop() nếu bị ẩn đi.
protected void onStop();
//Gọi khi activity không còn hiển thị trước người dùng
//Kèm theo sau là onRestart() nếu activity hiện lên trở lại hay onDestroy nếu sắp xoá activity đi.
protected void onDestroy();
//Gọi ngay trước khi kết thúc activity, xảy ra khi hàm finish() được gọi hoặc khi hệ thống yêu cầu buộc phải kết thúc.
}
2.Intent
Định nghĩa Intent
Khi Tim Berners phát minh ra giao thức Hypertext Transfer Protocol (HTTP), ông cũng đã phát minh ra một định dạng URLs chuẩn. Định dạng này là một hệ thống các động từ đi kèm các địa chỉ. Địa chỉ sẽ xác định nguồn tài nguyên như Web page, hình ảnh hay các server-side program. Động từ sẽ xác định cần phải làm cái gì với nguồn tài nguyên đó: GET để nhận dữ liệu về, POST để đưa dữ liệu cho nó để thực thi một công việc nào đó. Khái niệm Intent cũng tương tự, Intent là một mô tả trừu tượng của một hành động được thực thi. Nó đại diện cho một hành động đi kèm với một ngữ cảnh xác định. Với Intent thì có nhiều hành động và nhiều component (Một thể hiện của một class java dùng để thực thi các hành động được đặc tả trong Intent) dành cho Intent của Android hơn là so với HTTP verbs (POST, GET) và nguồn tài nguyên (hình ảnh, web page) của giao thức HTTP, tuy nhiên khái niệm vẫn tương tự nhau.
Intent được sử dụng với phương thức startActivity() để mở một Activity, và dùng với broadcastIntent để gởi nó đến bất kì BroadcastReceiver liên quan nào, và dùng với startService(Intent), bindService(Intent, ServiceConnection, int) để giao tiếp với các Service chạy dưới nền.
Intent cung cấp một chức năng cho phép kết nối hai chương trình khác nhau trong quá trình thực thi (runtime) (Cung cấp khả năng cho phép hai chương trình khác nhau giao tiếp với nhau). Chức năng quan trọng và được sử dụng nhiều nhất của một Intent là mở một Activity, nơi mà nó có thểđược dùng như một vật kết nối các Activity lại với nhau (Truyền thông tin giữa hai Activity khác nhau).
Hình 2 – Sử dụng Intent để trao đổi thông tin giữa hai chương trình
Thành phần chính của Intent bao gồm:
Action: Xác định hành động sẽ được thực thi, các hành động này có thể là: ACTION_VIEW, ACTION_EDIT, ACTION_MAIN…
Data: Các dữ liệu được sử dụng để hành động (Action) thao tác trên nó, như bản ghi về một người nào đó trong cơ sở dữ liệu chứa các contact của thiết bị.
Ví dụ về thành phần action/data:
ACTION_VIEW content://contacts/people/1 – hiển thị thông tin liên lạc của người có mã là “1”.
ACTION_DIAL content://contacts/people/1 – gọi điện cho người có mã là “1”.
ACTION_DIAL tel:123 – gọi đến số điện thoại “123”
Ngoài ra, Intent còn có các thuộc tính phụ sau:
Category: thông tin chi tiết về hành động được thực thi, ví dụ như CATEGORY_LAUNCHER có nghĩa là nó sẽ xuất hiện trong Launcher như ứng dụng có mức level cao (top-level application), trong khi CATEGORY_ALTERNATIVE chứa thông tin danh sách các hành động thay thế mà người dùng có thể thực thi trên phần dữ liệu mà Intent cung cấp.
Type: Chỉ định 1 kiểudữ liệu chính xác (kiểu MIME) được mang bởi intent. Thường thì type được suy ra từ chính dữ liệu. Bằng cách thiết lập thuộc tính type, bạn có thể vô hiệu hóa sự phỏng đoán kiểu dữ liệu và chỉ định rỏ một kiểu chính xác.
Component: Chỉ định rõ tên của lớp thành phần (Một thể hiện của một class java dùng để thực thi các hành động được đặc tả trong Intent) sử dụng cho Intent . Thông thường thì nó được xác định bằng cách tìm kiếm thông tin trong Intent (Các thông tin như Action, data/type, và category) và nối chúngvới các component (Một thể hiện của một Class java dùng để thực thi các hành động được đặc tả trong Intent) có thể đáp ứng được các yêu cầu sử lý của Intent.
Extracts: là một đối tượng Bundle dùng để chứa các thông tin kèm theo được dùng để cung cấp thông tin cần thiết cho component. Ví dụ: Nếu có một Intent đặc tả hành động send email thì những thông tin cần chứa trong đối tượng Bundle là subject, body…
Intent Resolution
Intent có 2 dạng chính sau:
Explicit Intents: Xác định rỏ một component (Một thể hiện của một class java dùng để thực thi các hành động được đặc tả trong Intent) (thông qua phương thức setComponent(ComponentName) hoặc setClass(Context, Class)) cung cấp lớp sẽ thực thi các hành động được đặc tả trong Intent. Thông thường thì những Intent này không chứa bất kỳ thông tin nào khác (như category, type) mà đơn giản chỉ là cách để ứng dụng mở các Activity khác bên trong một Activity.
Implicit Intents: Không chỉ định một component nào cả, thay vào đó, chúng sẽ chứa đủ thông tin để hệ thống có thể xác định component có sẵn nào là tốt nhất để thực thi hiệu quả cho Intent đó.
Khi sử dụng Implicit intents, do tính chất chuyên quyền của loại Intent này,ta cần phải biết phải làm gì với nó. Công việc này được đảm nhiệm bởi tiến trình của Intent resolution, nó giúp chỉ định Intent đến một Actvity,
BroadcastReceiver, hoặc Service (hoặc thỉnh thoảng có thểlà 2 hay nhiều hơn một activity/receiver) để có thể xử lý các hành động được đặc tả trong Intent.
Bất cứ thành phần nào (Activity, BroadcastReceiver, Service) khi muốn sử dụng trong ứng dụng đều phải được đăng kí trong file AndroidManifest.xml. Trong đó cầnđịnh nghĩa một thẻ <intent-fillter> cung cấp các thông tin để hệ thống có thể xác định được cái mà các component này (Activity, BroadcastReceiver, Service) có thể xử lý được (những action mà component này có thể thực hiện được).
Intent Fillter là bản đặc tả có cấu trúc của các giá trị của Intent dùng để xác định component phù hợp để thực hiệncác hành động được đặc tả trong Intent. Một Intent Fillter nối các hành động, categories và dữ liệu (hoặc thông qua type hoặc sheme) trong Intent. Intent Fillter được khai báo trong AndroidManifest.xml và sử dụng thẻ intent-fillter.
Một Intent Fillter có các thành phần chính sau:
Action: Tên hành động mà component có thể thực thi.
Type:Kiểu hành động mà component có thể thực thi.
Category: Phân nhóm các hành động.
Đối với những dữ liệu không phải là nội dung cụ thể (VD: URI) thì việc xem xét lựa chọn Intent phù hợp sẽ dựa vào lược đồ(Scheme) của dữ liệu được cung cấp (VD: http:// mailto: …)
Luật xác định component phù hợp Intent
Để xác định một thành phần là phù hợp với một Intent hệ thống sẽ tiến hành xem xét từ trên xuống.
Trước tiên khi một Intent được gọi, Android sẽ tìm kiếm những component (Activity, BroadcastReceiver, Service) có action-name phù hợp với Intent.
Nếu có component phù hợp Android sẽmở component đó lên để thực thi các hành động theo yêu cầu.
Nếu có nhiều hơn 1 component có action-name phù hợp thì Android sẽ yêu cầu người dùng chọn component phù hợp.
Ngược lại nếu không có component nào phù hợp Android sẽ tiến hành xem xét kiểu dự liệu của Intent cung cấp xem có component nào có đủ năng lực để sử lý kiểu dữ liệu đó không. Nếu khôngđược Android sẽ tiến hành xem xét scheme của dữ liệu đó để tìm kiếm component phù hợp. Nếu vẫn không tìm được component phù hợp Android sẽ tiến hành xem xét các component có chung Category với Intent để xác định component.
3.Service
Định nghĩa Service
Một service là một thành phần của ứng dụng, thể hiện mong muốn ứng dụng thực hiện các hành động trong khi không tương tác với người dùng hoặc cung cấp chức năng cho các ứng dụng khác sử dụng. Nói một cách đơn giản, service là các tác vụ (task) chạy ngầm dưới hệ thống nhằm thực hiện một nhiệm vụ nào đó. Mỗi class Service phải chứa thẻ <service> được khai báo trong file AndroidManifext.xml. Services có thể được bắt đầu bởi Context.startService() và Context.bindservice()
Cũng như các đối tượng của ứng dụng khác, services chạy trên luồng (Thread) của tiến trình chính. Có nghĩa là nếu service của bạn dự định chạy các hành vi có cường độ lớn hoặc các hành vi có thể gây nghẽn mạch, nó sẽ tự sinh ra luồng (Thread) của chính nó để làm thực hiện các tác vụ được đặc tả trong Service.
Cần lưu ý:
Service không phải là một tiến trình độc lập. Đối tượng Service không chạy trên tiến trình (process) của riêng nó (trừ khi có một yêu cầu đặt biệt), Service chạy trên tiến trình của chương trình.
Service không phải là một luồng (thread).
Service có chứa 2 đặc trưng cơ bản sau:
Là một chức năng để ứng dụng thông báo với hệ thống về việc mà nó muốn thực hiện ở phía dưới nền (ngay cả khi người dùng không tương tác trực tiếp tới ứng dụng). Điều này tương ứng với việc gọi phương thức Context.startservice(), nó sẽ yêu cầu hệ thống lên lịch cho service để chạy cho đến khi bị chính service hay người nào đó dừng nó lại.
Là một chức năng cho phép ứng dụng để cung cấp các chức năng của nó cho các ứng dụng khác. Điều đó tương ứng với việc gọi Context.bindService(), cho phép một long-standing connection được tạo ra để service có thể tương tác với nó.
Khi một Service được tạo ra, việc hệ thống cần làm là tạo ra thành phần và gọi hàm onCreate() và bất kỳ lệnh callback thích hợp nào trên luồng chình. Nó phụ thuộc vào Service thi hành hành vi thích hợp nào, chẳng hạn tạo luồng thứ hai để thực hiện tiến trình.
Vòng đời của 1 Service
Có 2 lý do để một service được chạy bởi hệ thống. Bằng cách gọi phương thức Context.startService() và Context.bindService(). Nếu người dùng gọi phương thức Context.startService() thì hệ thống sẽ nhận về một đối tượng service (tạo mới nó và gọi hàm onCreate() nếu cần thiết) và sau đó lại gọi phương thức onStartCommand(Intent, int ,int) với các biến được cung cấp bởi client. Service mới được tạo ra này sẽ chạy cho tới khi có lệnh gọi phương thức Context.stopService() hoặc stopSelf(). Dù cho một service có được start bao nhiêu lần đi nữa thì nó sẽ bị tắt ngay khi phương thức Context.stopService() hoặc phương thức stopSelf() được gọi.Service có thể dùng phương thức stopSelf(int) để đảm bảo rằng service sẽ không được tắt cho đến khi Intent đã khởi tạo nó được xử lý.
Dựa theo hình trên, 1 service được bắt đầu bởi phương thức startService() là service dùng để thực hiện các tác vụ ngầm dưới nền, service được bắt đầu bởi phương thức bindService() là service được dùng để cung cấp các chức năng cho các chương trình khác.
BroadcastReceiver
Là lớp nhận về các intents được gửi bởi sendBroadcast(). Có thể tạo instance cho lớp này bằng 2 cách: hoặcvới Context.registerReceiver() hay thông qua tag <receiver> trong file AndroidManifest.xml.
Có hai lớp broadcast quan trọng cần xét đến:
Broadcasts bình thường (Normal broadcasts): Được gửi bởi context.sendBroadcast tới các receivers hoàn toàn không theo 1 trật tự nào, dẫn tới đôi khi tồn tại cùng 1 lúc nhiều receivers khác nhau. Tuy nhanh nhưng không thể trao đổi kết quả nhận về giữa cac1 receivers với nhau.
Broadcasts có thứ tự (Ordered broadcasts) – Được gửi bởi Context.sendOrderedBroadcast() tới từng receiver một. Như vậy, kết quả nhận về của 1 receiver có thể trở thành input của 1 receiver khác, hay có thể ngay lập tức dừng việc broadcast tới các receiver khác. Trật tự chạy các receivers được quyết định bởi thuộc tính android:priority trong intent-filter tương ứng.
4.Content Provider
Là nơi lưu trữ và cung cấp cách truy cập dữ liệu do các ứng dụng tạo nên. Đây là cách duy nhất mà các ứng dụng có thể chia sẻ dữ liệu của nhau.
Android cung cấp sẵn content providers cho 1 số kiểu dữ liệu thông dụng như âm thanh, video, thông tin sổ điện thoại,… (Xem thêm trong gói android.provider). Người lập trình cũng có thể tự tạo ra các class con (subclass) của Content Provider để lưu trữ kiểu dữ liệu của riêng mình.
Cách thức hoạt động của Content Provider:
Mọi content provider, tuy có thể khác nhau về cách thức lưu trữ dữ liệu, đều sử dụng chung 1 interface để tìm và trả về dữ liệu, bao gồm việc thêm, bớt và sửa dữ liệu. Việc này được thực hiện thông qua các đối tượng ContentResolver khi gọi getContentResolver()
ContentResolver cr = getContentResolver();
Mô hình dữ liệu
Content providers sử dụng dữ liệu dưới dạng 1 table đơn giản, trong đó mỗi hàng là 1 record và mỗi cột là dữ liệu của 1 kiểu nhất định.
Hình 3 – Ví dụ mô hình dữ liệu thông tin số điện thoại trong máy
Mỗi record có 1 trường “_ID” độc nhất để liên kết với các bảng khác.
URI (Uniform Resource Identifier)
URI là dãy các ký tự string được dùng để:
Mở 1 trang web trong trình duyệt, bắt đầu với “http://”
Chỉ tới các file tài nguyên có sẵn trong máy (VD: 1 file video, danh sách contacts,…), bắt đầu với “content://”
Dựa vào hình 3 sau:
Hình 4 – Các thành phần của 1 URI
A – Tiền tố mặc định của 1 URI, không thay đổi được.
B – Xác định content provider. Đối với các ứng dụng hãng thứ 3, đây chính là tên đầy đủ của 1 lớp.
C – Xác định loại dữ liệu nào cần lấy về. Nếu nguồn cung cấp chỉ cho lấy 1 loại duy nhất thì có thể để trống, hoặc nếu có nhiều loại thì có thể phân ra từng phần 1 (land/bus, land/train, sea/ship,…)
D – ID của 1 record, khi cần lấy về chỉ duy nhất 1 record.
Khi tạo 1 content provider, Android tự động tạo 1 constant cho URI của provider đó để đảm bảo code hiệu quả và dễ cập nhật sau này. Constant này – CONTENT_URI – sẽ xác định cho ContentProvider phải liên kết với bảng nào của nguồn cung cấp.
Ví dụ:
URI cho bảng liên kết số điện thoại với tên người là:
android.provider.Contacts.Phones.CONTENT_URI
URI cho bảng liên kết hình đại diện với tên người là:
android.provider.Contacts.Photos.CONTENT_URI
Tìm kiếm 1 Content Provider
Để băt đầu tìm kiếm (query) 1 content provider, trước nhất người lập trình cần có trong tay các yếu tố sau:
URI xác định provider.
Tên các trường (field) dữ liệu sẽ lấy về.
Các kiểu dữ liệu của những trường trên.
ID, chỉ khi cần tìm 1 record xác định nào đó.
Để bắt đầu tìm kiếm, người lập trình có thể dùng 1 trong 2 phương thức: ContentResolver.query() hoặc Activity.managedQuery(). Cả 2 phương thức trên đều có chung arguments và đều trả về 1 đối tượng con trỏ (Cursor)
public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
public final Cursor managedQuery (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
Đầu tiên, dù với query() hay managedQuery() thì ta đều cần cung cấp URI của bên cung cấp, tức CONTENT_URI. Chú ý nếu muốn giới hạn chỉ tìm kiếm 1 URI duy nhất, ta thêm ID vào cuốiURI như sau: “content://…/23”, với 23 là ID ta cần tím kiếm
Hoặc ta có thể dùng các hàm hỗ trợ như ContentUris.withAppended() và Uri.withAppendedPath().
Ví dụ:
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI,23);
Các biến truyền vào khác của query() và managedQuery() là:
projection – Tên của cột dữ liệu cần trả về. Nếu NULL thì trả về tất cả các cột.
selection – Tên các hàng dữ liệu sẽ trả về. Sử dụng với WHERE giống như khi truy vấn SQL. Nếu NULL thì trả về tất cả các hàng.
selectionArgs – quy định giá trị trả về,VD: selectionArgs = {value_as_string} sẽ ép kiểu dữ liệu trả về dưới dạng string.
sortOrder – Xác định thứ tự sắp xếp các hàng. Sử dụng với ORDER BY giống như khi truy vấn SQL. Nếu NULL thì không theo thứ tự gì cả.
Tạo mới 1 Content provider
Để tạo mới 1 content provider, cần phải thoả mãn các điều kiện:
Thiết lập hệ thống lưu trữ dữ liệu như thế nào: bằng các file hay sử dụng cơ sở dữ liệu SQLite.
Extend lớp ContentProvider.
Khai báo 1 content provider trong file AndroidManifest.xml
Để định nghĩa 1 lớp con ContentProvider, ta cần hiện thực 6 phương thức trừu tượng (abstract) được khai báo trong lớp ContentProvider:
query()
insert()
update()
delete()
getType()
onCreate()
Phương thức query() phải trả về 1 đối tượng Cursor. Mặc dù Cursor về cơ bản là 1 interface, Android cung cấp sẵn 1 số đối tượng Cursor tạo sẵn. Ví dụ; SQLiteCursor dành cho cơ sở dữ liệu SQLite, MatrixCursor cho dữ liệu không lưu trong SQLite.
Android cũng khuyên nên gọi ContentResover.notifyChange() để báo cho các listeners biếtdữ liệu sẽ được hiệu chỉnh. Ngoài ra người lập trình cũng nên thêm vài thao tác sau để giúp việc truy xuất các lớp hiệu quả hơn:
Định nghĩa một Uri dạng public static final, với tên là CONTENT_URI (như đã trình bày ở trên).
Định nghĩa tên các cột mà content provider sẽ trả về. Thôg thường thì đây cũng chính là tên các cột trong cơ sở dữ liệu SQLite mà chúng đại diện. Tiếp theo, người lập trình nên định nghĩa các public static String constants để cac1 ứng dụng khác có thể ghi rõ tên các cột và các hướng dẫn khác 1 cột interger tên “_id” (với constant _ID) cũng cần được định nghia để sử dụng khi cần lấy về các record độc nhất. Trong SQLite, trường _ID nên có kiểu như sau:INTEGER PRIMARY KEY AUTOINCREMENT, trong đó thành phần AUTOINCREMENT không nhất thiết phải có, nhưng nên được đưa vào để đảm bảo các ID luôn độc nhất, tránh trường hợp khi xóa 1 row thì ID của row tiếp theo sẽ được cấp ID giống hệ của row vừa bị xóa.
Các ghi chú giúp người khác ghi đọc biết đọc dữ liệu như thế nào.
Nếu content provider này xử lý 1 kiểu dữ liệu hoàn toàn mới, người lập trình cần định nghĩ 1 kiểu MIME mới để trả về trong sự thể hiện của ContentProvider.getType().
Để khai báo 1 Content Provider mới, trước nhất cần phải khai báo trong tag <provider> của file AndroidManifest.xml.
Hình 15 – Khai báo tag <provider> trong file AndroidManifest.xml
Trong đó, thuộc tính name là tên đầy đủ của lớp con ContentProvider, và authorities là phần xác định provider của URI “content:”
5.View
View là thành phần cơ bản để xây dựng giao diện người dùng cho 1 ứng dụng Android. View là 1 lớp căn bản của widgets (widgets được dùng để tạo các nút nhấn, text fields,…).
Lớp con ViewGroup là lớp căn bản của layouts, có thể coi như 1 cái hộp vô hình chứa nhiều Views hay ViewGroups khác và xác định các thuộc tính layout.
Sử dụng Views:
Trong 1 cửa sổ màn hình, mọi views đều được sắp xếp theo thứ tự trên 1 cây trong 1 hay nhiều file XML nằm trong res/layout.
Khi tạo 1 cây chứa các views, người lập trình cần xác định những giá trị sau:
Các thuộc tính – Chẳng hạn định xem 1 TextView sẽ hiển thị chữ gì. Mỗi view có những thuộc tính và phương thức khác nhau.
Mức độ tập trung (Focus) – Thay đổi sự tập trung tuỳ theo input của người dùng với requestFocus().
Listeners – Định các listeners để có hành động khi 1 sự kiện gì đó xảy ra với setOnFocusChangeListener(View.OnFocusChangeListener)
Nhìn thấy hay không – với setVisibility(int)
Hình 6 – Ví dụ 1 cây Views
Một số Views thường dùng:
TextView: Hiển thị 1 khung text và cho phép người dùng thay đổi.
ImageView: Hiển thị 1 hình ảnh xác định từ file tài nguyên hay qua 1 content provider.
Button: Hiển thị 1 nút nhấn.
Checkbox: Hiển thị 1 nút nhấn với 2 trạng thái khác nhau.
KeyboardView: Hiển thị bàn phím ảo để nhập liệu.
WebView: Hiển thị các trang web bằng Webkit.
Một số ViewGroups thường dùng:
LinearLayout: Các view được xếp theo 1 hàng hay 1 cột duy nhất.
AbsoluteLayout: Cho phép xác định chính xác vị trí của từng view.
TableLayout: Sắp xếp các view theo các cột và hàng. Mỗi hàng có thể có nhiều view, mà mỗi view sẽ tạo nên 1 ô.
RelativeLayout: Cho phép xác định vị trí các view theo mối quan hệ giữa chúng (VD; canh trái, phải,…)
FrameLayout: Là 1 placeholder cho phép đặt lên đó 1 view duy nhất. View đặt lên FrameLayout luôn được canh lề trái phía trên
ScrollView: Là 1 FrameLayout đặc biệt cho phép trượt (scroll) 1 danh sách dài hơn kích thước màn hình.
6.Lưu trữ dữ liệu
Android có nhiều cách để lưu trữ dữ liệu. Một cách đơn giản nhất là sử dụng hệ thống preferences. Android cho phép các activity và ứng dụng lưu giữ preferences theo dạng cặp từ khóa và giá trị, nằm giữa các lần gọi một activity. Mục đích chính là để lưu trữ thông tin cấu hình chi tiết mà người dùng hiệu chỉnh, chẳng hạn trang web người dùng xem gần đây nhất. Bạn có thể lưu trữ bất kì thông tin nào trong preferences bằng từ khóa có kiểu String và kiểu dữ liệu nguyên thủy (boolean, String…)
Preferences có thể dùng cho activity độc lập hay được chia sẻ giữa các activity của một ứng dụng. Và tương lai, Preference có thể được chia sẻ giữa các ứng dụng với nhau.
Bên cạnh việc sử dụng Preferences để lưu trữ dữ liệu thì cũng còn một cách đơn giản khác để thực hiện công việc lưu trữ dữ liệu là sử dụng tập tin. Android cung cấp 2 mô hình để truy cập tập tin:
Một cho các tập tin được đóng gói sẵn cho ứng dụng.
Một dành cho tập tin được tạo trên thiết bị thực thi ứng dụng đó.
Đối với dữ liệu tĩnh, chẳng hạn danh sách các từ dùng để kiểm tra chính tả. Cách đơn giản nhất để thực hiện điều đó là lưu vào một tập tin, và tập tin này phải được đặt trong thư mục res/raw của project. Nhờ vậy, tập tin sẽ được đặt trong tập tin APK của ứng dụng như một nguồn tài nguên cơ bản.
Ngoài 2 phương pháp trên, Android còn hỗ trợ hệ quản trị cơ sở dữ liệu quan hệ SQLite để quản lý dữ liệu lớn và có quan hệ phức tạp. Nhờ vậy, người lập trình có thể thực hiện quản lý dữ liệu một cách dễ dàng, linh hoạt hơn.
Hệ quản trị cơ sở dữ liệu SQLite
SQLite là gì
Khi lập trình trên di động hay các thiết bị có dung lượng bộ nhớ hạn chế, người ta thường dùng SQLite. SQLite là một hệ quản trị cơ sở dữ liệu nhúng được hiện thực từ chuẩn SQL-92. Giống với cái tên của nó, SQLite chiếm dung lượng nhỏ (khoảng 275KB) nên việc truy xuất dữ liệu được nhanh chóng, không chiếm dụng quá nhiều tài nguyên hệ thống. Do SQLIte là phần mềm mã nguồn mở nên nó không bị giới hạn tác quyền. Vì lý do đó mà SQLite được nhiều hãng sử dụng (Adobe, Apple, Google, Sun, Symbian) và các dự án mã nguồn mở (Mozila, PHP, Python).
Đặc biệt, đối với Android, SQLite rất thích hợp để tạo cơ sở dữ liệu cho các ứng dụng trên nền Android. Từ khi SQLite có giao diện SQL thì nó càng được sử dụng dễ dàng và thông dụng hơn.
Sqlite tuy nhẹ hơn so với các hệ cơ sở dữ liệu khác nhưng cũng không khác biệt nhiều. SQLite cũng sử dụng ngôn ngữ truy vấn SQL (SELECT, INSERT, DELETE…), thích hợp cho các ứng dụng trên điện thoại, đặc biệt là ứng dụng trên hệ điều hành Android vì Android Runtime có thể chứa cả SQLite mà không cần phải chia nhỏ ra.
SQLite có một hệ thống câulệnhSQL đầy đủ với các triggers, transactions… Các câu truy vấn cũng như các hệ cơ sở dữ liệu khác. SQLite như bản thu nhỏ của so với các hệ CSDL khác, vì vậy nó không thể có đầy đủ các chứ năng trên chiếc điện thoại di động của bạn.
SQLite là một lựa chọn thích hợp dành cho ứng dụng trên hệ điều hành Android. Ngoài dung lượng lưu trữ nhỏ gọn, SQLite còn cho phép sử dụng Unicode, kiểu dữ liệu không được cài đặt trong một số phiên bản Android.
Đặc trưng của SQLite
SQLite được hiện thực từ tiêu chuẩn SQL-92 của một ngôn ngữ SQL nhưng vẫn còn chứa một số khiếm khuyết.
Tuy SQLite hỗ trợ triggers nhưng bạn không thể viết trigger cho view. Hoặc SQLite không hỗ trợ lệnh ALTER TABLE, do đó, bạn không thể thực hiện chỉnh sửa hoặc xóa cột trong bảng.
SQLite không hỗ trợ ràng buộc khóa ngoại , các transactions lồng nhau, phép kết right outer join, full outer join.
SQLite sử dụng kiểu dữ liệu khác biệt so với hệ quản trị cơ sở dữ liệu tương ứng. Bạn có thể insert dữ liệu kiểu string vào cột kiểu integer mà không gặp phải bất kỳ lỗi nào.
Vài tiến trình hoặc luồng có thể truy cập tới cùng một cơ sở dữ liệu. Việc đọc dữ liệu có thể chạy song song, còn việc ghi dữ liệu thì không được phép chạy đồng thời.
Ngoài các khiếm khuyết trên thì Sqlite cung cấp cho người dùng gần như đầy đủ các chưng năng mà một hệ cơ sở dữ liệu cần có như tạo database; tạo bảng; thêm, xóa, sửa dữ liệu.
SQLite Manager
SQLite Manager là công cụ hiệu quả hỗ trợ cho việc xây dựng cơ sở dữ liệu cho ứng dụng Android đó là SQLite Manager, một add-on miễn phí của Firefox (https://addons.mozilla.org/en-US/firefox/addon/5817)
Hình 23 – Màn hình làm việc của addon SQlite Manager
Sử dụng SQLite Manager để quản lí cơ sở dữ liệu Android
Tạo database mới :
Vào menu Database/ New Database
Tạo bảng mới:
Tại Database vừa tạo, nhấp chuột phải chọn Create table. Thiết lập các giá trị thích hợp.
Xử lý dữ liệu:
Chọn bảng cần nhập và chọn tab Browse & Search
Nút Add: Thêm dữ liệu
Nút Duplicate: Nhân đôi dữ liệu
Nút Edit: Chỉnh sửa dữ liệu
Nút Delete: xóa dữ liệu
Thực thi câu truy vấn:
Chọn tab Execute SQL, Nhập câu lệnh SQL vào khung Enter SQL và chọn Run SQL.
Kết quả sẽ được trình bày ở khung phía dưới. Nếu câu SQL có lỗi sẽ được hiển thị tại ô Last Error.
Tùy chỉnh Database: Chọn tab DB Settings và thiết lập các giá trị như mong muốn
Import Database vào ứng dụng Android
Sau khi tạo CSDL bằng SQLite Manager,chọn menu File/Export Database. Lưu file .sql vừa tạo vào máy.
Để import database vào ứng dụng, người lập trình cần :
Build and Run chương trình Android.
Khởi động máy ảo xong, chọn nút DDMS ở góc trên bên phải chương trình Eclipse để xem cấu trúc file.
Vào thư mục chứa database có dường dẫn như sau :
/data/data/your.app.package/databases/your-db-name
Sau đó chọn nút import và chọn tập tin database. Lưu ý : Phải đổi tên file từ *.sql thành * trước khi import vào chương trình.
Bạn đang đọc bài viết Bài 2:Các thành phần cơ bản của một ứng dụng Android tại chuyên mục Lập trình Android cơ bản của Diễn đàn học website.Mọi thông tin chia sẻ, phản hồi xin gửi về hòm thư contact@diendanhocweb.com hoặc comment xuống cuối bài nhé các bạn.
Chúc bạn thành công!