Tổng quan về Service trong hệ điều hành Android

17:25 07/10/2022

Service được coi là một trong bốn thành phần lớn của Android, đóng vai trò như một thành phần hết sức quan trọng. Đối với bất cứ ai nếu muốn trở thành một Android Developer thực thụ thì bắt buộc phải nắm rõ và hiểu biết sâu về “Service”.

Trước hết, biểu tượng của Service trong hệ điều hành là một robot chiến binh Android với thanh kiếm tượng trưng cho Activity, trong đó có hai chiếc ăng ten được xem là Broadcast Receiver, Intent là cánh tay còn View là đôi chân. Ngoài ra, Content Provider lại giống như một nơi cung cấp nhiên liệu cho robot, cùng với đó là một thành phần nằm trong thân robot tuy nhiên không thể nhìn thấy là Service, sở hữu một chiếc quạt gió chạy liên tục, khi robot chiến đấu hay kể cả khi ngủ nghỉ thì chiếc quạt vẫn liên tục hoạt động. 

1. Service là 

Một Service là thành phần (component) có thể thực hiện các hoạt động lâu dài trong background và nó không cung cấp một giao diện người dùng nào. Dù vậy, một thành phần khác của ứng dụng này có thể khởi động và nó sẽ tiếp tục chạy trong background ngay cả khi người dùng chuyển sang ứng dụng khác. Ngoài ra, một thành phần có thể liên kết tùy ý (bind) với một Service để tương tác với Service đó, thậm chí là thực hiện truyền thông liên tiến trình IPC (interprocess communication – IPC có thể được hiểu như một hoạt động chia sẽ dữ liệu qua nhiều tiến trình, thông thường sử dụng giao thức truyền thông và phải có Client và Server). Lấy ví dụ về trường hợp điển hình, một Service có thể thực hiện các giao dịch mạng, chơi nhạc, ra vào file I/O hoặc tương tác với một content provider, tất cả đều xuất phát từ background.

2. Phân loại Service 

2.1. Foreground Service

Ở trong trường hợp này, một Foreground Service có thể thực hiện một số thao tác mà người dùng dễ dàng sử dụng. Ví dụ: một ứng dụng nghe nhạc có thể chơi một bản nhạc và điều khiển nó bằng Foreground Service. Tuy vậy, một điều bắt buộc là Foreground Service cần phải được cấp quyền hiển thị thông báo và sẽ tiếp tục chạy ngay cả khi người dùng không tương tác với ứng dụng.

2.1.1 Xây dựng ứng dụng nghe nhạc

Bước 1: Xây dựng giao diện

Bước 2: Ánh xạ các view sang file MainActivity.java

Bước 3: Xây dựng layout chơi nhạc

Bước 4: Tạo thông báo và tạo ra 1 class với tên tùy ý

Bước 5: Đăng ký lớp vừa tạo bên trong file AndroidManifest.xml

android:name=”.MyNotificationChangnel”

Bước 6: Chuẩn bị tài nguyên ảnh pause và play trong thư mục drawable. Đồng thời, tạo ra thư mục raw để chứa file nhạc mp3

Bước 7: Tạo ra class MySong.java

public class MySong implements Serializable {
private String name;
private String title;
private int img;
private int resouce;

public MySong(String name, String title, int img, int resouce) {
this.name = name;
this.title = title;
this.img = img;
this.resouce = resouce;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public int getImg() {
return img;
}

public void setImg(int img) {
this.img = img;
}

public int getResouce() {
return resouce;
}

public void setResouce(int resouce) {
this.resouce = resouce;
}
}

Bước 8: Tạo ra 2 phương thức trong file MainActivity.java

  • Background Service

Cụ thể, một Background Service sẽ thực hiện các hoạt động dù không được người dùng chú ý trực tiếp. Ví dụ: ứng dụng sử dụng một service để thu gom bộ nhớ từ người chơi thì service đó là một Background Service, hoạt động mà người dùng không nhất thiết phải để ý.

Bởi những lí do như vậy, Service sẽ được coi là Background Service nếu không nằm 1 trong 3 trường hợp sau:

  • Ứng dụng của người dùng hiện có Activity hiển thị
  • Ứng dụng của người dùng có Foreground Service đang chạy
  • Ứng dụng của người dùng được kết nối với một ứng dụng Foreground khác, ở đây là phương thức ràng buộc Service bằng cách sử dụng các Content Providers của chúng

2.3. Demo ứng dụng chạy nhạc mà không hiển thị giao diện

Bước 1: Tạo thư mục raw trong res, đưa file nhạc vào thư mục raw

Bước 2: Tạo class MyBackgroundService.java

 

Bước 3: Cài đặt trong file activity_main.xml

Bước 4: Cài đặt trong file MainActivity.java

Bước 5: Khai báo Service trong file AndroidManifest.xml

Ví dụ : <service android:name=”.MyBackgroundService”/>

Lưu ý: Background Service sẽ chạy ổn định trên android O trở lên

2.4. Bound Service

Một service được gọi là Bound khi một thành phần của ứng dụng ràng buộc với nó bởi lời gọi bindService(). Nó cung cấp một giao diện Client – Server cho phép các thành phần tương tác với nó, bao gồm: gửi yêu cầu, nhận kết quả và thậm chí là IPC. Nhìn chung, Bound Service chỉ chạy miễn là có một thành phần ràng buộc với nó. Mặc dù có thể có nhiều thành phần ràng buộc với Bound Service cùng lúc, nhưng khi tất cả tháo bỏ ràng buộc (unbound) thì nó sẽ hủy bỏ. Bên cạnh đó, Service còn được phân chia là Started Service và Bound Service.

Một Started Service hay là Unbound Service là service được khởi động bằng phương thức startService() từ thành phần khác. Thậm chí, nó sẽ tiếp tục chạy trong background kể cả khi thành phần khởi tạo nó bị phá hủy và đây cũng được xem là một Background Service theo cách chia trên.

2.4.1. Sử dụng Bound Service để chơi nhạc

Bước 1: Tạo file layout activity_main.xml

Bước 2: Tạo ra lớp MusicBoundService.java

Bước 4: Tạo thư mục raw và copy file nhạc vào

Bước 5: Khai báo Service trong file AndroidManifest.xm

3. Độ ưu tiên các loại Service

Xét riêng về hệ điều hành này, Android bắt buộc phải dừng một service khi bộ nhớ ít và phải khôi phục tài nguyên hệ thống cho Activity đang được sử dụng. Nếu Service được ràng buộc với một Activity đang sử dụng thì rất ít khả năng bị tiêu hủy. Tuy nhiên, nếu Service được khai báo và chạy ở chế độ Foreground thì nó cũng khó để xóa bớt.

Về trường hợp Service là Started và chạy lâu dài, hệ thống sẽ làm giảm vị trí ưu tiên của nó, bởi vì phụ thuộc vào process thì các loại service sẽ được xếp theo độ ưu tiên sau: Bound Service khó bị tiêu hủy nhất, tiếp theo là Foreground Service và Background Service.

Dựa vào những lí do trên, Background Service được coi là Service dễ bị tiêu hủy nhất nên cần phải xử lý một cách thích hợp, tùy thuộc vào giá trị trả về trong onStartCommand() mà Service có thể được khởi động lại. 

4. Các giá trị trả về trong onStartCommand().

Khi Service bị hệ thống tiêu hủy do thiếu bộ nhớ thì dưới đây là 5 giá trị trả về thường dùng trong onStartCommand() để thông báo với hệ thống.

4.1. START_NOT_STICKY

Nếu hệ thống tiêu hủy service khi giá trị này được trả về thì service này sẽ không được khởi động lại trừ khi có một Intent đang được chờ ở onStartCommand(). Vì vậy, đây là lựa chọn an toàn nhất để tránh chạy Service khi không cần thiết và ứng dụng cũng có thể khởi động lại một cách đơn giản các công việc chưa hoàn thành.

4.2. START_STICKY

Giá trị này được trả về trong onStartCommand khi service bị hệ thống tiêu hủy. Nếu onStartCommand không có một Intent nào chờ nữa thì Service sẽ được hệ thống khởi động lại với một Intent null.

4.3. START_REDELEVER_INTENT

Nếu Service bị tiêu hủy thì nó sẽ được khởi động lại với một Intent là Intent cuối cùng mà Service được nhận. Điều này thích hợp với các service đang thực hiện công việc muốn tiếp tục ngay tức thì như download file. 

4.4. START_STICKY_COMPATIBILITY

Giá trị này cũng giống như START_STICKY nhưng không chắc chắn trong việc đảm bảo khởi động lại service.

4.5. DEFAULT

Là một sự lựa chọn tiềm năng giữa START_STICKY_COMPATIBILITY hoặc START_STICKY

public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {

                    onStart(intent, startId);

                    return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;

 }

5. Các phương thức quan trọng trong vòng đời Service

Khi tạo một service, chúng ta buộc phải kế thừa lớp Service do Android cung cấp. Tuy nhiên, nếu muốn thực thi thì điều tiên quyết nữa là phải override một vài phương thức quan trọng từ việc xử lý vòng đời của Service và cung cấp một cơ chế cho phép các thành phần liên kết với Service nếu thích hợp.

5.1. onStartCommand()

Hệ thống gọi phương thức này khi một thành phần khác như Activity gọi đến Service bằng câu lệnh startService(). Khi phương thức này được thực hiện, dịch vụ sẽ được khởi động và có thể chạy trong background vô thời hạn. Đến một lúc nào đó công việc hoàn thành, chúng ta nên dừng lại bằng cách gọi stopService() từ một thành phần khác, hoặc cho chính Service gọi stopSelf(). Trong trường hợp nếu chỉ muốn ràng buộc buộc với Service thì không nên sử dụng onStartCommand().

5.2. onBind()

Hệ thống sẽ gọi phương thức này khi một thành phần khác gọi đến Service bằng câu lệnh bindService(). Khi triển khai phương thức này, yêu cầu bắt buộc là phải cung cấp một giao diện để client có thể giao tiếp với Service thông qua một đối tượng IBinder do Service trả về. Dù khi kế thừa từ lớp Service của Android phải luôn luôn override phương thức này, nhưng nếu không muốn ràng buộc (bind) thì chúng ta hoàn toàn có thể return null.

5.3. onCreate()

Nói một cách khái quát, hệ thống gọi phương thức này khi Service được khởi tạo và nó chỉ chạy một lần trước khi onStartCommand() hoặc onBind() được gọi. Nếu Service đã chạy thì phương thức này sẽ không được gọi lại lần nào nữa.

5.4. onDestroy()

Hệ thống gọi phương thức trên khi Service không được sử dụng nữa và đang bị hủy (destroy). Lúc này, chúng ta cũng nên giải phóng tài nguyên như các Threads, Listeners hay Receivers ở.

6. Khi Started và Bound Service chạy đồng thời

Như chúng ta đã biết, StartedService được start bằng lời gọi startService() từ một thành phần nào đó và nó sẽ chỉ dừng lại khi thành công gọi stopSelf() hoặc một thành phần gọi stopService(). Về phần Bound Service, nó sẽ được khởi chạy khi một thành phần gọi bindService() và dừng lại khi tất các các thành phần ràng buộc (bind) với nó hủy liên kết (unbind). Ngược lại, nếu Started Service gọi stopSelf() hoặc được gọi stopService() mà service vẫn chưa vào onDestroy thì cần một điều kiện nữa để nó bị hủy trong tình huống các thành phần ràng buộc với BoundService chưa hủy hết liên kết. Bên cạnh đó, nếu tất cả các thành phần ràng buộc với Bound Service unbind thì Service cũng chưa được hủy cho đến khi Started Service tự gọi stopSelf() hoặc được gọi stopService(). Lúc này, điều kiện cần là cả hai loại service đều được hủy thì Service mới được hủy.

Vòng đời của Bound Service khi chạy cùng với một Started Service

Như vậy, thông qua những kiến thức trên đã giải thích khá chi tiết về Service trong hệ điều hành Android, đồng thời cũng là nền tảng vô cùng quan trọng đối với Android Developer. 

Bộ môn CNTT

Cao đẳng FPT Mạng cá cược bóng đá Hà Nội

Cùng chuyên mục

Đăng Kí học Fpoly 2023

Bình Luận