2.1. Các thành phần ứng dụng
Thành phần ứng dụng là các khối cơ bản để xây dựng nên một ứng dụng Android hoàn chỉnh . Các thành phần này được liên kết lỏng lẻo bởi các ứng dụng trong tập tin AndroidManifest.xml, tập tin AndroidManifest.xml mô tả mỗi thành phần của ứng dụng và cách chúng tương tác với nhau.
Có bốn thành phần chính có thể được sử dụng trong một ứng dụng Android:
Thành phần | Đặc tả |
---|---|
Activities | Chúng gọi giao diện người dùng và xử lý các tương tác người dùng với màn hình điện thoại thông minh. |
Services | Chúng xử lý nền kết hợp với một ứng dụng. |
Broadcast Receivers | Chúng xử lý thông tin liên lạc giữa hệ điều hành Android và các ứng dụng. |
Content Providers |
Chúng xử lý dữ liệu và các vấn đề quản lý cơ sở dữ liệu. |
2.1.1. Activity (hoạt động)
Một Activity đại diện cho một màn hình duy nhất với một giao diện người dùng. Ví dụ, một ứng dụng email có thể có một hoạt động cho thấy một danh sách các email mới, một hoạt động để soạn một email, và một hoạt động để đọc email. Nếu một ứng dụng có nhiều hơn một hoạt động, sau đó một trong số chúng được đánh dấu là hoạt động được hiển thị khi ứng dụng được khởi chạy.
Một Activity được thực hiện như một lớp con của lớp Activity như sau:
public class MainActivity extends Activity { ... }
2.1.2. Service (dịch vụ)
Một Service là một thành phần chạy trong nền để thực hiện các hoạt động lâu dài. Ví dụ, một dịch vụ có thể chơi nhạc ở chế độ nền trong khi người dùng đang ở một ứng dụng khác nhau, hoặc nó có thể lấy dữ liệu qua mạng mà không ngăn chặn người dùng tương tác với một hoạt động
Một Service được thực hiện như một lớp con của lớp Service như sau:
public class MyService extends Service { ... }
2.1.3. Broadcast Receiver
Broadcast Receiver chỉ đơn giản là phản ứng để phát các tín hiệu từ các ứng dụng khác hoặc từ hệ thống. Ví dụ, các ứng dụng cũng có thể bắt đầu chương trình phát tín hiệu để cho các ứng dụng khác biết rằng một số dữ liệu đã được tải về điện thoại và sẵn sàng cho họ sử dụng.Một máy thu phát tín hiệu được thực hiện như một lớp con của BroadcastReceiver lớp và mỗi tín hiệu được phát đi như một đối tượng Intent.
public class MyReceiver extends BroadcastReceiver { ... }
2.1.4. Content Provider (cung cấp nội dung)
Content Providers cung cấp nội dung dữ liệu từ một ứng dụng khác theo yêu cầu. Yêu cầu đó được xử lý bằng các phương thức (methods) của lớp ContentResolver. Dữ liệu có thể được lưu trữ trong hệ thống tập tin, cơ sở dữ liệu (database) hoặc ở một nơi hoàn toàn khác.
Một Content Providers được thực hiện như một lớp con của ContentProvider lớp và phải thực hiện một bộ tiêu chuẩn API cho phép các ứng dụng khác để thực hiện các giao dịch.
public class MyContentProvider extends ContentProvider { ... }
2.1.5. Additional Components (các thành phần bổ sung)
Có thành phần bổ sung sẽ được sử dụng trong việc xây dựng của các đơn vị nêu trên. Các thành phần này là:
Thành phần | Đặc tả |
---|---|
Fragments | Đại diện cho một hành vi hoặc một phần của giao diện người dùng trong một hoạt động. |
Views |
Các yếu tố giao diện người dùng được vẽ trên màn hình bao gồm các nút, danh sách các hình thức, ... |
Layouts |
Xem phân cấp kiểm soát định dạng màn hình và xuất hiện của các quan điểm. |
Intents | Tín hiệu hệ thống kết nối các thành phần với nhau. |
Resources | Các yếu tố bên ngoài, các hằng số và drawables hình ảnh.. |
Manifest | Tập tin cấu hình cho ứng dụng. |
2.1.5.1 Intents
Với các thành phần vừa nói ở trên thì Content providers được kích hoạt bởi một yêu cầu từ Content Resolver. Còn activities, services và broadcast receivers được kích hoạt bởi tin nhắn bất đồng bộ gọi là intents. Một intents có thể giữ nội dung tin nhắn. Với mỗi loại thành phần có những phương thức kích hoạt khác nhau:
Activity: được kích hoạt bởi các phương thức Context.startActivity()
hoặc Activity.startActivityForResult()
. Từ Activity này qua Activity khác sẽ có thể mang theo giá trị và truyền qua Activity được khởi động giống như truyền giá trị từ form hiện hành qua một form khác giống như trên C#. Bên Activity được khởi động có thể dùng hàm getIntent()
để lấy giá trị. Nếu một Activity khởi động một Activity khác và mong chờ nó trả về cho nó một kết quả thì gọi hàm startAtivityForResult()
thay vì gọi hàm startActivity()
. Ví dụ, một Activity đang cần một tấm hình là kết quả chọn lựa từ Activity khác gồm danh sách các hình; kết quả trả về thông qua hàm onActivityResult()
.
Service: được khởi động thông qua hàm Context.startService()
. Tương tự, trên một intents có thể dùng hàm Context.bindService()
để thiết lập một kết nối giữa các thành phần và service muốn gọi.
Một đối tượng Intent có thể đặt tên cho một thành phần mà mình muốn mởmột cách rõ ràng. Nếu làm vậy, Android tìm thấy thành phần đó và kích hoạt nó. Nhưng nếu lỡ thành phần không được đặt tên rõ ràng và có thể bị nhập nhằng tên, Android phải chọn thành phần thích hợp nhất để trả về cho intents. Nó làm vậy bằng cách so sánh đối tượng Intent với intent filters của thành phần sắp được mở. Intent filters của thành phần sẽ thông báo cho Android các loại intents mà thành phần có thể quản lý. Tất cả cả thông tin của thành phần được lưu trong tập tin Android Manifest. Dưới đây là 2 ví dụ mở rộng có dùng intents filters:
<?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <Activity Android:name="com.example.project.FreneticActivity" Android:icon="@drawable/small_pic.png" Android:label="@string/freneticLabel" . . . > <intent-filter . . . > <action Android:name="Android.intent.action.MAIN" /> <category Android:name="Android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter . . . > <action Android:name="com.example.project.BOUNCE" /> <data Android:mimeType="image/jpeg" /> <category Android:name="Android.intent.category.DEFAULT" /> </intent-filter> </Activity> . . . </application> </manifest>
Ta thấy trong ví vụ trong thẻ Intents filter đầu tiên có 2 thành phần là action "Android.intent.action.MAIN
" và category "Android.intent.category.LAUNCHER
". Nó đánh dấu Activity FreneticActivity là màn hình đầu tiên khi người dùng khởi động ứng dụng trên thiết bị Android. Nói cách khác, Activity đó là đầu vào của ứng dụng, nó là màn hình đầu tiên mà người dùng nhìn thấy khi họ khởi động ứng dụng.
Trong thẻ intents filter thứ 2 mô tả tên action mà Activity có thể thực thi trên một loại dữ liệu nào đó.
Một thành phần có thể có nhiều intents filter, mỗi cái mô tả bộ khả năng khác nhau của Activity. Nếu nó không có bất kì filter nào, nó có thể chỉ được kích hoạt bằng intents mà nó đặt tên rõ ràng như là một thành phần cần được mở.
Đối với broadcast receiver thì nó được tạo và khai báo bằng mã lệnh, intent filter được khởi động trực tiếp như là đối tượng IntentFilter. Tất cả các filter khác được thiết lập trong manifest.
2.1.5.2. Tập tin AndroidManifest
Trước khi Android khởi động thành phần ứng dụng, nó phải biết thành phần nào đang tồn tại. Vì thế, ứng dụng phải nêu rõ các thành phần trong tập tin AndroidManifest.xml mà được gói trong tập tin .apk mà tập tin .apk này giữ mã lệnh, tập tin, và các tài nguyên của ứng dụng.
Manifest là một tập tin XML và luôn luôn được đặt tên là AndroidManifest.xml cho ứng dụng. Trong tập tin manifest chứa các thành phần sau:
- Liệt kê các thành phần như Activity, service, . . . có trong ứng dụng.
- Các permission cho phép dùng các phần cứng.
- Thông số thiết lập SDK.
Ví dụ tập tin AndroidManifest.xml như sau:
<?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <Activity Android:name="com.example.project.FreneticActivity" Android:icon="@drawable/small_pic.png" Android:label="@string/freneticLabel" . . . > </Activity> . . . </application> </manifest>
Các thành phần ứng dụng có vòng đời – đó là sự bắt đầu khi Android khởi động thành phần cho đến khi kết thúc ứng dụng khi thực thể ứng dụng bị hủy. Các trạng thái ứng dụng có thể có hiệu lực hay không có hiệu lực; đối với activities, thì có tình trạng visible hoặc invisible. Trong mục này ta nói về vòng đời của activities, services, và broadcast receiver.
2.2. Vòng đời các thành phần ứng dụng Android
2.2.1 Vòng đời của Activity
Một Activity có ba trạng thái:
- Đang hiệu lực hoặc đang chạy: khi nó trực tiếp giao tác với người dùng.
- Tạm dừng: khi nó không còn được hiệu lực nhưng vẫn được nhìn thấy bởi người dùng. Nói dễ hiểu hơn, có Activity này nằm trên một Activity khác mà Activity này trong suốt hoặc không che phủ hết màn hình vì vậy Activitykhác được nhìn thấy. Một Activity đang dừng thì hoàn toàn “còn sống” nhưng có thể bị “giết đi” bởi hệ thống trong trường hợp bộ nhớ thấp.
- Dừng hẳn: nếu Activity bị che khỏa hoàn toàn bởi một Activity khác. Nó vẫn còn duy trì tất cả trạng thái và thông tin thành viên. Tuy nhiên, người dùng không còn thấy nó vì màn hình của nó ẩn đi và thường bị “giết” bởi hệthống khi không đủ bộ nhớ.
Nếu một Activity bị tạm dừng và dừng hẳn, hệ thống có thể loại bỏ nó ra khỏi bộ nhớ bằng cách gọi hàm finish()
hoặc loại bỏ nó ra khỏi tiến trình. Khi nó thể hiện trước người dùng lần nữa, nó phải khởi động và khôi phục lại các trạng thái trước đó.
Khi một Activity chuyển từ trạng thái này qua trạng thái khác, thì Activity đó sẽ gọi các phương thức protected sau: void onCreate(Bundle savedInstanceState)
, void onStart()
, void onRestart(
), void onResume()
, void onPause()
, void onStop()
, void onDestroy()
. Tất cả các phương thức này có thể viết lại (override) để phù hợp với từng công việc cụ thể đối với ứng dụng của bạn khi mà trạng thái ứng dụng thay đổi. Tất cả các Activity phải thực thi lại hàm onCreate()
để thiết lập lần đầu khi mà các đối tượng trong ứng dụng được khởi động. Nhiều activity sẽ dùng hàm onPause()
để cập nhật lại dữ liệu đã thay đổi hoặc dừng hẵn việc giao tiếp với người dùng.
Chú ý gọi siêu lớp (superclass): Việc thực thi bất kì phương thức vòng đời ứng dụng nào thì nên luôn luôn gọi hàm tương ứng của siêu lớp. Ví dụ:
Với 7 phương thức định nghĩa cho toàn bộ vòng đời ứng dụng của Activity. Nếu chú ý, ta thấy có những vòng khép kín như sau:
- Vòng khép kín toàn bộ của ứng dụng: Activity bắt đầu từ lần gọi hàm đầu tiên
onCreate()
đến hàm cuối cùngonDestroy()
. Một Activity làm tất cả các thiết lập đầu tiên cho chương trình trong hàmonCreate()
và giải phóng tất cả các tài nguyên trong hàmonDestroy()
. Ví dụ như ứng dụng có 1 luồng chạy ngầm để tải dữ liệu từ máy chủ về thì nó tạo luồng trong hàmonCreate()
và sau đó dừng hẵn luồng trong hàmonDestroy()
. - Vòng đời nhìn thấy được bởi người dùng: trong Activity từ hàm
onStart()
cho đến hàmonStop()
. Trong suốt thời gian này, người dùng có thểthấy Activity trên màn hình. Giữa 2 phương thức này, bạn có thể duy trì các tài nguyên mà cần thiết để trình bày Activity cho người dùng. Ví dụ, bạn có thể khai báo BroadcastReceiver trong hàmonStart()
để giám sát tự thay đổi ảnh hưởng đến giao diện và giải phóng nó trong hàmonStop()
khi người dùng không còn nhìn thấy những gì bạn đang trình bày. HàmonStart()
vàonStop()
được gọi nhiều lần bởi vì acitivity thay đổi nhiều lần giữa trạng thái nhìn thấy được và ẩn đi đối với người dùng. - Vòng đời chạy ngầm: từ hàm
onResume()
đến hàmonPause()
. Trong suốt thời gian này, Activity chuyển từ trạng thái resume sang pause hay ngược lại.onPause()
được gọi khi thiết bị “ngủ” hoặc khi Activity mới được khởi động, hàmonResume()
được gọi khi kết quả của một Activity hay một intent mới được đưa ra. Vì thế mã lệnh trong hai phương thức này nên ít thôi.
Hình sau mô tả những vòng khép kín và con đường mà activty phải trải qua giữa các trạng thái. Hình ô van thể hiện trạng thái của Activity, hình chữ nhật thể hiện các phương thức
Vòng đời của một Activity
Bảng sau mô tả mỗi hàm chi tiết hơn về cách sử dụng và đặt nó ở trong vòng đời ứng dụng của Activity:
Bảng mô tả lại ý nghĩa các phương thức trong vòng đời của một Activity
Phương thức | Mô tả | Có thể hủy (killable) | Theo sau hàm |
---|---|---|---|
onCreate() | Được gọi khi Activity lần đầu tiên được tạo. Đây là nơi đầu tiên bạn nên làm tất cảcác thiết lập như tạo Views, gắn dữ liệu vào danh sách hay đại loại vậy. Luôn theo sau là hàm onStart(). | Không | onStart() |
onRestart() | Được gọi sau khi Activity đã bị dừng trước đó và được được khởi động lại. Luôn theo sau hàm onStart(). | Không | onStart() |
onStart() | Được gọi chỉ trước khi acitivity trở nên nhìn thấy được với người dùng. Luôn theo sau là hàm onResume() nếu Activity chạy
ngầm hoặc onStop() nếu nó bị ẩn đi. |
Không | onResume() hay onStop() |
onResume() | Chỉ được gọi trước Activity bắt đầu tươngtác với người dùng. Tại thời điểm này, Activity nằm trên đầu của ngăn xếp Activity và người dùng chuẩn bị tương tác với chúng. Luôn theo sau là hàm onPause(). | Không | onPause() |
onPause() |
Được gọi khi hệ thống sắp bắt đầu hồi phục lại một Activity khác. Phương thức này được dùng để lưu những dữ liệu mới vừa thay đổi, dừng hẳn hiệu ứng động và và dừng hẳn những thứ khác có thể ngốn tài nguyên CPU hay đại loại vậy. Ứng dụng làm bất cứ cái gì để sao cho nó chạy nhanh bởi vì Activity kế tiếp sẽ không hồi phục lại cho đến khi acitvity hiện tại dừng lại. Nó theo sau hàm onResume() nếu Activity hiện tại sắp được thực thi hoặc theo sau là hàm onStop() nếu nó trở nên ẩn đi đối với người dùng. |
có | onResume() hay onStop() |
onStop() |
Được gọi khi người dùng không nhìn thấy được Activity nữa. Điều này có thể xảy ra bởi vì nó đang được hủy hoặc một Activitykhác (có thể Activity đang tồn tại hoặc Activity mới) được hồi phục lại và đang đè lên Activity hiện tại. Vừa theo sau là hàm onRestart() nếu Activity đang quay lại đểtương tác với người dùng hoặc hàm onDestroy() nếu Activity dự định hủy đi. |
Có |
onRestart() hay onDestroy() |
onDestroy() |
Được gọi trước khi Activity bị hủy đi. Hàm này được gọi lần cuối cùng khi mà một ativity đang kết thúc hay hệ thống chỉ đang hủy những thực thể của Activity để tiết kiệm không gian. Có thể phân biệt 2 trạng thái này bằng hàm isFinishing(). |
Có | Không |
Có thể hủy (Killable): Ý nghĩa của nó là dù muốn dù không thì hệ thống có thể “giết” tiến trình đang giữ Activity tại thời điểm sau khi phương thức kết thúc mà không thực thi dòng mã lệnh cũa một Activity khác. Ba phương thức (onPause()
, onStop()
, onDestroy()
) có giá trị là Có. Bởi vì onPause()
là phương thức đầu tiên của ba phương thức, nó là phương thức duy nhất bảo đảm được gọi trước khi tiến trình kết thúc trong khi đó onStop()
và onDestroy()
thì không. Vì thế, bạn nên dùng onPause()
để cập nhật tất cả dữ liệu mới vừa được thay đổi.
Còn những phương thức được đánh dấu là Không trong cột Có thể hủy (Killable) bảo đảm tiến trình giữ acitivity khỏi bị “giết” khỏi thời điểm mà chúng được gọi. Mặc dù vậy một ativity trong tình trạng có thể hủy bắt đầu từ khi onPause()
đến onResume()
. Nó không thể bị hủy cho đến khi hàm onPause()
kết thúc.
- Tiết kiệm tình trạng của Activity: Khi hệ thống tắt Activity để bảo toàn bộ nhớ, người dùng mong muốn Activity giữ được trạng thái Activity như lúc trước khi tắt. Để giữ lại trạng thái trước khi Activity bị tắt, bạn nên dùng hàm onSaveInstanceState()
cho Activity. Android gọi hàm này trước khi hủy acvitity. Tiếp theo sau đó, để lấy lại trạng thái của Activity thì phải thực thi hàm onRestoreInstanceState()
sau hàm onStart()
và hàm onCreate()
đã chạy trước đó. Vì thế ta có thể vừa tái tạo và lưu vết lại trạng thái Activity. Không giống như hàm onPause() hay những hàm trong vòng đời ứng dụng, hàm onSaveInstanceState()
hay hàm onRestoreInstanceState()
không phải phương thức vòng đời của ứng dụng và không phải lúc nào cũng được gọi vì không phải lúc nào người dùng cũng muốn lưu vết lại trạng thái của Activity.
- Phối hợp các activities: khi một Activity khởi động một Activity khác, cả hai Activity sẽ thay đổi trạng thái của vòng đời của mình. Activity này tạm dừng và có thể dừng hẳn trong khi cái khác thì mới khởi động. Trong trường hợp bạn có thể cần phối hợp các Activity này khi mà 2 Activity cùng nằm trong cùng một tiến trình.
2.2.1 Vòng đời Service
Một service có thể dùng trong 2 cách:
Cách 1: Nó có thể được khởi động và được cho phép dùng cho đến khi ai đó dừng hẳn nó hoặc nó tự dừng hẳn. Để khởi động dùng hàm Context.startService()
và dừng hẳn bằng hàm Context.stopService()
hoặc tựdừng bằng hàm Service.stopSelf()
hoặc Service.stopSelfResult()
. Chú ý là hàm stopService()
chỉ được gọi một lần duy nhất để dừng service trong khi đó gọi hàm startService()
bao nhiêu lần tùy thích.
Cách 2: Nó có thể được vận hành bằng mã lệnh dùng lớp interface mà nó đã định nghĩa. Client thiết lập kết nối và dùng kết nối ấy để gọi dịch vụ. Kết nối được thiết lập bằng cách gọi Context.bindService()
và được đóng bởi hàm Context.unbindService()
.
Giống như Activity, Service có những phương ứng vòng đời của riêng mình mà có thể thực thi để giám sát sự thay đổi trong trạng thái của nó. Nhưng service có ít hơn và gồm có 3 phương thức public: void onCreate()
, void onStart (Intent intent)
, void onDestroy()
.
Bằng cách thực thi những phương thức này, bạn có thể giám sát vòng đời của service:
- Toàn bộ vòng đời của service xảy ra khi gọi hàm
onCreate()
và kết thúc khi gọi hàmonDestroy()
. Giống như một Activity, một service khởi động trong hàmonCreate()
và giải phóng mọi tài nguyên trong hàmonDestroy()
. - Vòng đời “có hiệu lực” của service bắt đầu khi gọi hàm
onStart(),
và không được kết thúc bằngonStop()
vì hàm này không có thực.
Phương thức onCreate()
và onDestroy()
được gọi cho tất cả dịch vụ cho dù service khởi động bằng hàm Context.startService()
hay hàm Context.bindService()
. Tuy nhiên, onStart()
được gọi khi service đã khởi động bằng hàm startService()
. Nếu một service cho phép những service khác liên kết với nó thì phải dùng các phương thức sau: IBinder onBind (Intent intent)
, boolean onUnbind (Intent intent)
, void onRebind(Intent intent)
.
Hình sau mô tả các phương thức cho service, Mặc dù, hình chia ra 2 loại service dựa trên tiêu chí khởi động bởi hàm startService()
và hàm bindService()
. Cho dù nó khởi động bằng cách nào đi chăng nữa, thì nó vẫn cho client kết nối tới nó và bất kì service nào cũng có thể gọi 2 hàm onBind()
và onUnbind()
.
Vòng đời của một Service
2.2.3 Vòng đời BroadcastReceiver
Broadcast Receiver có một hàm cho phép người dùng viết lại là hàm void onCreate(Context curContext, Intent broadcastMsg)
.
Khi một tin nhắn phát đi cho người nhận, Android gọi phương thức onReceive()
và chuyển nó cho đối tượng Intent đang chứa tin nhắn. Broadcast receiver được xem là có hiệu lực chỉ khi nào có đang thực thi phương thức này. Khi phương thức onReceive()
kết thúc thì broadcast receiver không còn hiệu lực nữa.
Một tiến trình với một broadcast receiver thì được bảo vệ khỏi bị “giết”. Nhưng một tiến trình với các thành phần không còn hiệu lực (Activity, service,…) thì bị hệ thống “giết” bất kì lúc nào khi mà bộ nhớ không đủ cho các tiến trình khác.