Service
là một
thành phần ứng dụng có thể thực hiện
các thao tác diễn ra trong thời gian dài trong nền. Nền tảng này không cung cấp giao diện người dùng. Một lần
đã khởi động, dịch vụ có thể tiếp tục chạy trong một khoảng thời gian, ngay cả sau khi người dùng chuyển sang một dịch vụ khác
. Ngoài ra, một thành phần có thể liên kết với một dịch vụ để tương tác với dịch vụ đó và thậm chí là thực hiện
giao tiếp liên quy trình (IPC). Ví dụ: một dịch vụ có thể xử lý các giao dịch mạng, phát
nhạc, thực hiện thao tác I/O tệp hoặc tương tác với nhà cung cấp nội dung, tất cả đều ở chế độ nền.
Thận trọng: Một dịch vụ chạy trong luồng chính của máy chủ lưu trữ quá trình; dịch vụ không tạo chuỗi riêng và không chạy trong một quy trình riêng trừ phi bạn chỉ định khác. Bạn nên chạy mọi hoạt động chặn trên một luồng riêng trong dịch vụ để tránh Ứng dụng Lỗi Không phản hồi (ANR).
Loại dịch vụ
Sau đây là 3 loại dịch vụ:
- Nền trước
-
Dịch vụ trên nền trước thực hiện một số thao tác mà người dùng. Ví dụ: ứng dụng âm thanh sẽ dùng dịch vụ trên nền trước để phát một bản âm thanh. Dịch vụ trên nền trước phải hiển thị một Thông báo. Các dịch vụ trên nền trước tiếp tục chạy ngay cả khi người dùng không tương tác thông qua ứng dụng.
Khi sử dụng một dịch vụ trên nền trước, bạn phải hiển thị thông báo để người dùng chủ động biết rằng dịch vụ đang chạy. Thông báo này không thể bị loại bỏ trừ khi dịch vụ bị ngừng hoặc bị xóa khỏi nền trước.
Tìm hiểu thêm về cách định cấu hình dịch vụ trên nền trước trong .
Lưu ý: WorkManager API mang đến cách linh hoạt để lên lịch tác vụ, đồng thời có thể chạy các công việc này làm dịch vụ trên nền trước nếu cần. Trong nhiều trường hợp, việc sử dụng WorkManager nên sử dụng trực tiếp các dịch vụ trên nền trước.
- Thông tin khái quát
- Dịch vụ nền thực hiện một thao tác mà không được người dùng trực tiếp chú ý
người dùng. Ví dụ: nếu một ứng dụng dùng một dịch vụ để thu gọn bộ nhớ,
thường là một dịch vụ nền.
Lưu ý: Nếu ứng dụng của bạn nhắm đến API cấp 26 trở lên, thì hệ thống sẽ áp dụng các quy định hạn chế khi chạy ở chế độ nền khi ứng dụng đó không chạy ở nền trước. Trong hầu hết trong trường hợp cụ thể, bạn không nên truy cập vào thông tin vị trí từ màu nền. Thay vào đó, lập lịch tác vụ bằng WorkManager.
- Đã ràng buộc
- Một dịch vụ sẽ bị liên kết khi một thành phần ứng dụng liên kết với dịch vụ đó bằng cách gọi
bindService()
. Dịch vụ ràng buộc cung cấp máy khách-máy chủ cho phép các thành phần tương tác với dịch vụ, gửi yêu cầu, nhận kết quả, và thậm chí làm như vậy trên các quy trình có giao tiếp liên quy trình (IPC). Một dịch vụ ràng buộc chỉ chạy miễn là một thành phần ứng dụng khác được liên kết với thành phần đó. Nhiều thành phần có thể liên kết với dịch vụ cùng lúc, nhưng khi tất cả chúng huỷ liên kết, dịch vụ sẽ bị huỷ.
Mặc dù tài liệu này thường thảo luận riêng về các dịch vụ được bắt đầu và dịch vụ ràng buộc,
dịch vụ của bạn có thể hoạt động theo cả hai cách—có thể khởi động (chạy vô thời hạn) và cũng cho phép
ràng buộc. Vấn đề chỉ đơn giản là bạn có triển khai một vài phương thức gọi lại hay không: onStartCommand()
để cho phép các thành phần khởi động và onBind()
để cho phép liên kết.
Bất kể dịch vụ của bạn được bắt đầu, bị ràng buộc hay cả hai, mọi thành phần của ứng dụng
có thể sử dụng dịch vụ (ngay cả từ một ứng dụng riêng biệt) theo cách tương tự như bất kỳ thành phần nào cũng có thể sử dụng
một hoạt động – bằng cách bắt đầu hoạt động đó bằng Intent
. Tuy nhiên, bạn có thể khai báo
dịch vụ ở chế độ riêng tư trong tệp kê khai và chặn quyền truy cập từ các ứng dụng khác.
Vấn đề này sẽ được thảo luận thêm trong phần Khai báo dịch vụ trong
tệp kê khai.
Chọn giữa một dịch vụ và một luồng
Dịch vụ chỉ đơn giản là một thành phần có thể chạy trong nền ngay cả khi người dùng không tương tác với ứng dụng, do đó, bạn chỉ nên tạo dịch vụ nếu đó là việc bạn của bạn.
Nếu bạn phải thực hiện công việc bên ngoài luồng chính, nhưng chỉ khi người dùng đang tương tác
với ứng dụng của mình, bạn nên tạo một chuỗi mới trong ngữ cảnh của một ứng dụng khác
thành phần. Ví dụ: nếu bạn muốn phát một số bản nhạc nhưng chỉ khi hoạt động đang chạy,
bạn có thể tạo một chuỗi trong onCreate()
,
hãy bắt đầu chạy chiến dịch trong onStart()
,
và dừng hoạt động đó trong onStop()
.
Ngoài ra, hãy cân nhắc sử dụng nhóm luồng và trình thực thi trong gói java.util.concurrent
hoặc coroutine của Kotlin thay vì coroutine truyền thống
Thread
. Xem
Tài liệu Tạo luồng trên Android để biết thêm thông tin về
chuyển quá trình thực thi sang các luồng trong nền.
Hãy nhớ rằng nếu bạn sử dụng một dịch vụ, dịch vụ đó vẫn chạy trong luồng chính của ứng dụng bằng cách mặc định, do đó bạn vẫn nên tạo một luồng mới trong dịch vụ nếu luồng đó hoạt động chuyên sâu hoặc hoạt động chặn.
Thông tin cơ bản
Để tạo một dịch vụ, bạn phải tạo một lớp con của Service
hoặc sử dụng một lớp con
của các lớp con hiện có. Trong quá trình triển khai, bạn phải ghi đè một số phương thức gọi lại
xử lý các khía cạnh chính của vòng đời dịch vụ và cung cấp một cơ chế cho phép các thành phần
ràng buộc với dịch vụ, nếu thích hợp. Đây là những phương thức gọi lại quan trọng nhất mà bạn nên
ghi đè:
onStartCommand()
- Hệ thống gọi phương thức này bằng cách gọi
startService()
khi một thành phần khác (chẳng hạn như một hoạt động) yêu cầu bắt đầu dịch vụ. Khi phương thức này thực thi, dịch vụ sẽ bắt đầu và có thể chạy trong nền vô thời hạn. Nếu triển khai việc này, bạn có trách nhiệm dừng dịch vụ khi công việc hoàn tất bằng cách gọistopSelf()
hoặcstopService()
. Nếu chỉ muốn cung cấp ràng buộc, bạn không nên cần triển khai phương thức này. onBind()
- Hệ thống gọi phương thức này bằng cách gọi
bindService()
khi một thành phần khác muốn liên kết với dịch vụ (chẳng hạn như để thực hiện RPC). Trong khi triển khai phương thức này, bạn phải cung cấp giao diện mà ứng dụng khách dùng để giao tiếp với dịch vụ bằng cách trả về mộtIBinder
. Bạn phải luôn triển khai phương thức này; tuy nhiên, nếu không muốn cho phép ràng buộc, bạn nên trả về rỗng. onCreate()
- Hệ thống sẽ gọi phương thức này để thực hiện các quy trình thiết lập một lần khi dịch vụ
được tạo ban đầu (trước khi gọi
onStartCommand()
hoặconBind()
). Nếu dịch vụ đang chạy, phương pháp này không có tên. onDestroy()
- Hệ thống sẽ gọi phương thức này khi dịch vụ không còn được sử dụng và đang bị huỷ bỏ. Dịch vụ của bạn nên triển khai phương thức này để dọn dẹp mọi tài nguyên như luồng, luồng đã đăng ký trình nghe hoặc người nhận. Đây là lệnh gọi cuối cùng mà dịch vụ nhận được.
Nếu một thành phần bắt đầu dịch vụ bằng cách gọi startService()
(dẫn đến lệnh gọi đến onStartCommand()
), thì dịch vụ
tiếp tục chạy cho đến khi tự dừng với stopSelf()
hoặc một ứng dụng khác
sẽ dừng nó bằng cách gọi stopService()
.
Nếu một thành phần gọi
bindService()
để tạo dịch vụ và onStartCommand()
không được gọi thì dịch vụ sẽ chạy
chỉ khi thành phần đó được liên kết với nó. Sau khi dịch vụ không bị ràng buộc khỏi tất cả ứng dụng,
hệ thống sẽ huỷ bỏ nó.
Hệ thống Android chỉ dừng một dịch vụ khi bộ nhớ sắp hết và phải khôi phục hệ thống
cho hoạt động tập trung vào người dùng. Nếu dịch vụ bị liên kết với một hoạt động có người dùng
tập trung vào, ít có khả năng bị giết hơn; nếu được khai báo là chạy ở nền trước thì dịch vụ đó hiếm khi bị tắt.
Nếu dịch vụ được bắt đầu và đang hoạt động trong thời gian dài, hệ thống sẽ hạ vị trí của dịch vụ đó
vào danh sách tác vụ ở chế độ nền theo thời gian và dịch vụ này rất dễ bị
tắt – nếu dịch vụ của bạn được bắt đầu, bạn phải thiết kế dịch vụ để xử lý các lần khởi động lại một cách linh hoạt
bị hệ thống tạo ra. Nếu tắt dịch vụ, hệ thống sẽ khởi động lại ngay khi tài nguyên trở nên
nhưng điều này cũng phụ thuộc vào giá trị mà bạn trả về từ onStartCommand()
. Thông tin khác
về thời điểm hệ thống có thể huỷ bỏ một dịch vụ, hãy xem phần Quy trình và luồng
tài liệu.
Trong các phần sau, bạn sẽ tìm hiểu cách tạo
startService()
và
bindService()
phương thức dịch vụ, cũng như cách sử dụng
chúng từ các thành phần khác của ứng dụng.
Khai báo một dịch vụ trong tệp kê khai
Bạn phải khai báo tất cả các dịch vụ trong đơn đăng ký tệp kê khai, giống như cách bạn thực hiện đối với các hoạt động và các thành phần khác.
Để khai báo dịch vụ, hãy thêm phần tử <service>
là con của <application>
. Dưới đây là ví dụ:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
Xem phần tử <service>
để biết thêm thông tin về việc khai báo dịch vụ của bạn trong tệp kê khai.
Bạn cũng có thể thêm các thuộc tính khác vào phần tử <service>
để
xác định các thuộc tính như quyền cần thiết để bắt đầu dịch vụ và quy trình trong
mà dịch vụ sẽ chạy. android:name
là thuộc tính bắt buộc duy nhất — thuộc tính này chỉ định tên lớp của dịch vụ. Sau
bạn xuất bản ứng dụng của mình, hãy giữ nguyên tên này để tránh nguy cơ bị lỗi
mã do phụ thuộc vào ý định tường minh để bắt đầu hoặc liên kết dịch vụ (đọc bài đăng trên blog, Những điều
Điều đó không thể thay đổi).
Thận trọng: Để đảm bảo rằng ứng dụng của bạn được bảo mật, hãy luôn sử dụng
ý định tường minh khi bắt đầu Service
và không khai báo bộ lọc ý định cho
các dịch vụ của bạn. Việc sử dụng ý định ngầm ẩn để bắt đầu một dịch vụ sẽ gây ra mối nguy hiểm về bảo mật vì bạn không thể
sự chắc chắn về dịch vụ phản hồi ý định và người dùng không thể biết dịch vụ nào
bắt đầu. Kể từ Android 5.0 (API cấp 21), hệ thống sẽ đưa ra một ngoại lệ nếu bạn gọi
bindService()
với ý định ngầm ẩn.
Bạn có thể đảm bảo rằng dịch vụ chỉ được cung cấp cho ứng dụng của bạn bằng cách
bao gồm android:exported
và đặt thuộc tính đó thành false
. Thao tác này sẽ ngăn các ứng dụng khác khởi động
ngay cả khi sử dụng một ý định tường minh.
Lưu ý:
Người dùng có thể xem những dịch vụ nào đang chạy trên thiết bị của mình. Nếu họ thấy
một dịch vụ mà người dùng không nhận ra hoặc tin tưởng, họ có thể ngừng dịch vụ đó. Trong
để tránh việc dịch vụ của bạn bị người dùng vô tình dừng, bạn cần
để thêm
android:description
cho
<service>
trong tệp kê khai ứng dụng. Trong phần mô tả,
cung cấp một câu ngắn giải thích chức năng và lợi ích của dịch vụ
mà nó cung cấp.
Đang tạo một dịch vụ đã bắt đầu
Dịch vụ đã bắt đầu là dịch vụ mà một thành phần khác bắt đầu bằng cách gọi startService()
, dẫn đến một lệnh gọi đến
onStartCommand()
.
Khi một dịch vụ được bắt đầu, nó có vòng đời độc lập với
đã khởi động nó. Dịch vụ có thể chạy trong nền vô thời hạn, ngay cả khi
thành phần bắt đầu sẽ bị huỷ bỏ. Do đó, dịch vụ sẽ tự dừng khi hoạt động
hoàn tất bằng cách gọi stopSelf()
hoặc một thành phần khác có thể
hãy dừng hoạt động đó bằng cách gọi stopService()
.
Một thành phần của ứng dụng, chẳng hạn như một hoạt động, có thể khởi động dịch vụ bằng cách gọi startService()
và truyền một Intent
chỉ định dịch vụ và bao gồm mọi dữ liệu để dịch vụ sử dụng. Dịch vụ nhận được
Intent
này trong phương thức onStartCommand()
.
Ví dụ: giả sử một hoạt động cần lưu một số dữ liệu vào cơ sở dữ liệu trực tuyến. Hoạt động
có thể bắt đầu một dịch vụ đồng hành và phân phối dịch vụ đó dữ liệu để lưu bằng cách truyền một ý định đến startService()
. Dịch vụ nhận được ý định trong onStartCommand()
, kết nối với Internet và thực hiện
giao dịch cơ sở dữ liệu. Khi giao dịch hoàn tất, dịch vụ sẽ tự dừng và
bị huỷ bỏ.
Thận trọng: Một dịch vụ chạy trong cùng một quy trình với ứng dụng nơi khai báo và nằm trong luồng chính của ứng dụng đó theo mặc định. Nếu dịch vụ của bạn thực hiện các thao tác chuyên sâu hoặc chặn trong khi người dùng tương tác với một hoạt động từ cùng một hoạt động thì dịch vụ sẽ làm chậm hiệu suất hoạt động. Để tránh ảnh hưởng đến ứng dụng hiệu suất, hãy bắt đầu một luồng mới bên trong dịch vụ.
Lớp Service
là lớp cơ sở
cho tất cả các dịch vụ. Khi mở rộng lớp này, bạn cần phải tạo một luồng mới trong đó
dịch vụ có thể hoàn tất mọi công việc của mình; thì dịch vụ sẽ sử dụng chuỗi chính của ứng dụng bằng cách
mặc định. Các tiện ích này có thể làm chậm hiệu suất của mọi hoạt động mà ứng dụng của bạn đang chạy.
Khung Android cũng cung cấp IntentService
lớp con của Service
sử dụng
luồng worker để xử lý tất cả yêu cầu bắt đầu, từng yêu cầu một. Việc sử dụng lớp này không
được đề xuất cho các ứng dụng mới vì tính năng này sẽ không hoạt động tốt kể từ Android 8 Oreo, do
giới thiệu Giới hạn thực thi ở chế độ nền.
Hơn nữa, tính năng này cũng sẽ không được dùng nữa kể từ Android 11.
Bạn có thể dùng jobIntentService làm một
thay thế cho IntentService
tương thích với các phiên bản Android mới hơn.
Các phần sau đây mô tả cách bạn có thể triển khai dịch vụ tuỳ chỉnh của riêng mình. Tuy nhiên, bạn nên đặc biệt cân nhắc việc dùng WorkManager cho hầu hết các trường hợp sử dụng. Tham khảo hướng dẫn xử lý nền trên Android để xem có giải pháp nào phù hợp với nhu cầu của bạn hay không.
Mở rộng lớp Service
Bạn có thể mở rộng lớp Service
để xử lý từng ý định đến. Dưới đây là cách triển khai cơ bản:
Kotlin
class HelloService : Service() { private var serviceLooper: Looper? = null private var serviceHandler: ServiceHandler? = null // Handler that receives messages from the thread private inner class ServiceHandler(looper: Looper) : Handler(looper) { override fun handleMessage(msg: Message) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000) } catch (e: InterruptedException) { // Restore interrupt status. Thread.currentThread().interrupt() } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1) } } override fun onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply { start() // Get the HandlerThread's Looper and use it for our Handler serviceLooper = looper serviceHandler = ServiceHandler(looper) } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show() // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job serviceHandler?.obtainMessage()?.also { msg -> msg.arg1 = startId serviceHandler?.sendMessage(msg) } // If we get killed, after returning from here, restart return START_STICKY } override fun onBind(intent: Intent): IBinder? { // We don't provide binding, so return null return null } override fun onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show() } }
Java
public class HelloService extends Service { private Looper serviceLooper; private ServiceHandler serviceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread().interrupt(); } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work doesn't disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler serviceLooper = thread.getLooper(); serviceHandler = new ServiceHandler(serviceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = serviceHandler.obtainMessage(); msg.arg1 = startId; serviceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } }
Mã mẫu xử lý tất cả cuộc gọi đến trong onStartCommand()
và đăng tác vụ lên Handler
chạy trên luồng trong nền. API này hoạt động giống như IntentService
và xử lý lần lượt tất cả yêu cầu.
Ví dụ: bạn có thể thay đổi mã để chạy công việc trên một nhóm luồng, chẳng hạn như nếu muốn chạy nhiều yêu cầu cùng lúc.
Lưu ý rằng phương thức onStartCommand()
phải trả về một
số nguyên. Số nguyên là một giá trị mô tả cách hệ thống sẽ tiếp tục dịch vụ trong
trường hợp hệ thống tắt nó. Giá trị trả về
từ onStartCommand()
phải là một trong những giá trị sau
hằng số:
START_NOT_STICKY
- Nếu hệ thống tắt dịch vụ sau khi
onStartCommand()
trả về, thì không tạo lại dịch vụ trừ phi có trạng thái đang chờ xử lý ý định phân phối. Đây là tuỳ chọn an toàn nhất để tránh chạy dịch vụ của bạn khi không cần thiết và khi ứng dụng của bạn có thể chỉ cần khởi động lại mọi công việc chưa hoàn tất. START_STICKY
- Nếu hệ thống tắt dịch vụ sau khi
onStartCommand()
trả về, hãy tạo lại dịch vụ và gọionStartCommand()
, nhưng không phân phối lại ý định cuối cùng. Thay vào đó, hệ thống gọionStartCommand()
bằng một ý định rỗng trừ phi có ý định đang chờ xử lý để bắt đầu dịch vụ. Trong trường hợp đó, các ý định đó được phân phối. Ứng dụng này phù hợp với các trình phát nội dung đa phương tiện (hoặc các dịch vụ tương tự) không thực thi các lệnh nhưng đang chạy vô thời hạn và đang chờ công việc. START_REDELIVER_INTENT
- Nếu hệ thống tắt dịch vụ sau khi
onStartCommand()
trở lại, hãy tạo lại dịch vụ và gọionStartCommand()
với ý định gần đây nhất đã được gửi đến . Lần lượt mọi ý định đang chờ xử lý sẽ được phân phối. Chế độ này phù hợp với các dịch vụ chủ động thực hiện công việc cần được tiếp tục ngay lập tức, chẳng hạn như tải tệp xuống.
Để biết thêm thông tin chi tiết về các giá trị trả về này, hãy xem tài liệu tham khảo được liên kết cho mỗi hằng số.
Bắt đầu một dịch vụ
Bạn có thể bắt đầu dịch vụ từ một hoạt động hoặc thành phần ứng dụng khác bằng cách
đang truyền một Intent
đến startService()
hoặc startForegroundService()
. Chiến lược phát hành đĩa đơn
Hệ thống Android gọi phương thức onStartCommand()
của dịch vụ và truyền phương thức này Intent
,
xác định dịch vụ nào cần bắt đầu.
Lưu ý: Nếu ứng dụng của bạn nhắm đến API cấp 26 trở lên, thì hệ thống
áp dụng các hạn chế đối với việc sử dụng hoặc tạo các dịch vụ nền, trừ phi ứng dụng
ở nền trước. Nếu một ứng dụng cần tạo một dịch vụ trên nền trước,
ứng dụng sẽ gọi startForegroundService()
. Phương thức đó tạo một dịch vụ nền, nhưng
báo hiệu cho hệ thống rằng dịch vụ đó sẽ tự quảng bá cho
nền trước. Sau khi được tạo, dịch vụ phải gọi
startForeground()
phương thức trong
5 giây.
Ví dụ: một hoạt động có thể bắt đầu dịch vụ mẫu trong phần trước (HelloService
) bằng cách sử dụng một ý định tường minh với startService()
, như minh hoạ dưới đây:
Kotlin
startService(Intent(this, HelloService::class.java))
Java
startService(new Intent(this, HelloService.class));
Phương thức startService()
trả về ngay lập tức và
hệ thống Android sẽ gọi phương thức onStartCommand()
của dịch vụ. Nếu dịch vụ chưa chạy, trước tiên, hệ thống sẽ gọi onCreate()
, sau đó gọi
onStartCommand()
Nếu dịch vụ cũng không cung cấp mối liên kết, thì ý định được phân phối bằng startService()
sẽ là phương thức giao tiếp duy nhất giữa
thành phần ứng dụng và dịch vụ. Tuy nhiên, nếu bạn muốn dịch vụ gửi lại kết quả,
ứng dụng khởi động dịch vụ có thể tạo PendingIntent
cho một thông báo truyền tin
(với getBroadcast()
) và phân phối đến dịch vụ
trong Intent
khởi động dịch vụ. Sau đó, dịch vụ này có thể sử dụng
truyền tin để cung cấp kết quả.
Nhiều yêu cầu bắt đầu dịch vụ dẫn đến nhiều lệnh gọi tương ứng đến
onStartCommand()
. Tuy nhiên, chỉ có một yêu cầu dừng
dịch vụ (với stopSelf()
hoặc stopService()
) là bắt buộc để dừng dịch vụ đó.
Ngừng dịch vụ
Dịch vụ được bắt đầu phải quản lý vòng đời riêng của dịch vụ đó. Tức là hệ thống không dừng lại hoặc
huỷ bỏ dịch vụ trừ phi dịch vụ phải khôi phục bộ nhớ hệ thống và dịch vụ
sẽ tiếp tục chạy sau khi onStartCommand()
trở về. Chiến lược phát hành đĩa đơn
dịch vụ phải tự dừng bằng cách gọi stopSelf()
hoặc một dịch vụ khác
thành phần có thể dừng nó bằng cách gọi stopService()
.
Sau khi được yêu cầu dừng bằng stopSelf()
hoặc stopService()
, hệ thống sẽ huỷ dịch vụ ngay khi
nhất có thể.
Nếu dịch vụ của bạn xử lý đồng thời nhiều yêu cầu đến onStartCommand()
, thì bạn không nên dừng
dịch vụ của bạn khi xử lý xong một yêu cầu bắt đầu (vì bạn có thể đã nhận được một yêu cầu mới)
bắt đầu yêu cầu (việc dừng ở cuối yêu cầu đầu tiên sẽ chấm dứt yêu cầu thứ hai). Cần tránh
vấn đề này, bạn có thể sử dụng stopSelf(int)
để đảm bảo rằng yêu cầu của bạn
ngừng dịch vụ sẽ luôn dựa trên yêu cầu bắt đầu gần đây nhất. Tức là khi gọi stopSelf(int)
, bạn sẽ truyền mã nhận dạng của yêu cầu bắt đầu (startId
được gửi tới onStartCommand()
) mà yêu cầu ngừng của bạn dẫn đến
tương ứng. Sau đó, nếu dịch vụ nhận được yêu cầu bắt đầu mới trước khi bạn gọi được stopSelf(int)
, thì mã nhận dạng sẽ không khớp và dịch vụ sẽ không dừng.
Thận trọng: Để tránh lãng phí tài nguyên hệ thống và sử dụng
nguồn pin, hãy đảm bảo rằng ứng dụng của bạn dừng dịch vụ khi hoạt động xong.
Nếu cần, các thành phần khác có thể dừng dịch vụ bằng cách gọi stopService()
. Ngay cả khi bạn bật liên kết cho dịch vụ,
bạn phải luôn tự dừng dịch vụ nếu dịch vụ nhận được lệnh gọi đến onStartCommand()
.
Để biết thêm thông tin về vòng đời của một dịch vụ, hãy xem phần Quản lý vòng đời của một dịch vụ dưới đây.
Tạo một dịch vụ ràng buộc
Dịch vụ ràng buộc là dịch vụ cho phép các thành phần của ứng dụng liên kết với dịch vụ đó bằng cách gọi bindService()
để tạo một kết nối lâu dài.
Thường thì đối tượng này không cho phép các thành phần bắt đầu bằng cách gọi startService()
.
Tạo một dịch vụ ràng buộc khi bạn muốn tương tác với dịch vụ từ các hoạt động và các thành phần khác trong ứng dụng của bạn hoặc để hiển thị một số chức năng của ứng dụng cho các ứng dụng khác thông qua giao tiếp liên quy trình (IPC).
Để tạo một dịch vụ ràng buộc, hãy triển khai phương thức gọi lại onBind()
để trả về một IBinder
xác định giao diện giao tiếp với dịch vụ. Sau đó, các thành phần khác của ứng dụng có thể gọi
bindService()
để truy xuất giao diện và
bắt đầu gọi các phương thức trên dịch vụ. Dịch vụ này chỉ hoạt động để phân phát thành phần ứng dụng
bị liên kết với dịch vụ, vì vậy, khi không có thành phần nào liên kết với dịch vụ, hệ thống sẽ huỷ bỏ dịch vụ đó.
Bạn không cần dừng một dịch vụ ràng buộc theo cách tương tự như khi bạn dừng một dịch vụ ràng buộc
đã bắt đầu thông qua onStartCommand()
.
Để tạo một dịch vụ ràng buộc, bạn phải xác định giao diện chỉ định cách ứng dụng có thể
giao tiếp với dịch vụ. Giao diện này giữa các dịch vụ
đồng thời ứng dụng phải là phương thức triển khai của IBinder
và là ứng dụng mà dịch vụ của bạn phải cung cấp
trả về từ phương thức gọi lại onBind()
. Sau khi ứng dụng nhận được IBinder
, ứng dụng có thể bắt đầu
tương tác với dịch vụ thông qua giao diện đó.
Nhiều ứng dụng có thể liên kết với dịch vụ cùng một lúc. Khi khách hàng tương tác xong với
thì dịch vụ sẽ gọi unbindService()
để huỷ liên kết.
Khi không có ứng dụng nào liên kết với dịch vụ, hệ thống sẽ huỷ bỏ dịch vụ.
Có nhiều cách để triển khai một dịch vụ ràng buộc và việc triển khai sẽ hiệu quả hơn phức tạp hơn so với dịch vụ bắt đầu. Vì những lý do này, cuộc thảo luận về dịch vụ ràng buộc sẽ xuất hiện trong tài liệu riêng về Dịch vụ ràng buộc.
Gửi thông báo cho người dùng
Khi đang chạy, dịch vụ có thể thông báo cho người dùng về các sự kiện bằng cách sử dụng thông báo trên thanh thông báo nhanh hoặc thông báo trên thanh trạng thái.
Thông báo trên thanh thông báo nhanh là thông báo xuất hiện trên bề mặt của cửa sổ hiện tại chỉ cho trước khi biến mất. Thông báo trên thanh trạng thái cung cấp biểu tượng trên thanh trạng thái cùng với mà người dùng có thể chọn để thực hiện hành động (chẳng hạn như bắt đầu một hoạt động).
Thông thường, thông báo trên thanh trạng thái là kỹ thuật phù hợp nhất để sử dụng khi các tác vụ ở chế độ nền, chẳng hạn như tải tệp xuống đã hoàn tất và bây giờ người dùng có thể thực hiện hành động với tệp đó. Khi người dùng chọn thông báo trên chế độ xem mở rộng, thông báo có thể bắt đầu một hoạt động (chẳng hạn như hiển thị tệp được tải xuống).
Quản lý vòng đời của một dịch vụ
Vòng đời của một dịch vụ đơn giản hơn nhiều so với một hoạt động. Tuy nhiên, điều quan trọng là quan trọng là bạn chú ý kỹ đến cách dịch vụ của bạn được tạo và huỷ bỏ vì có thể chạy trong nền mà người dùng không biết.
Vòng đời của dịch vụ (từ khi được tạo cho đến khi bị huỷ) có thể tuân theo một trong hai đường dẫn sau:
- Một dịch vụ đã bắt đầu
Dịch vụ được tạo khi một thành phần khác gọi
startService()
. Sau đó, dịch vụ này sẽ chạy vô thời hạn và phải tự dừng bằng cách gọistopSelf()
. Một thành phần khác cũng có thể ngăn bằng cách gọistopService()
. Khi dừng dịch vụ, hệ thống sẽ huỷ bỏ dịch vụ đó. - Dịch vụ ràng buộc
Dịch vụ được tạo khi một thành phần khác (ứng dụng) gọi
bindService()
. Sau đó, máy khách sẽ liên lạc với dịch vụ thông qua giao diệnIBinder
. Ứng dụng có thể đóng kết nối bằng cách gọiunbindService()
. Nhiều ứng dụng có thể liên kết với cùng một dịch vụ và khi tất cả chúng huỷ liên kết, hệ thống sẽ huỷ bỏ dịch vụ. Dịch vụ không cần tự dừng.
Hai đường dẫn này không hoàn toàn riêng biệt. Bạn có thể liên kết với một dịch vụ đã
bắt đầu bằng startService()
. Ví dụ: bạn có thể
bắt đầu dịch vụ nhạc nền bằng cách gọi startService()
kèm theo Intent
xác định nhạc cần phát. Sau đó,
có thể là khi người dùng muốn thực hiện một số quyền kiểm soát đối với trình phát hoặc có thông tin về
bài hát hiện tại, một hoạt động có thể liên kết với dịch vụ bằng cách gọi bindService()
. Trong những trường hợp như vậy, stopService()
hoặc stopSelf()
không thực sự dừng dịch vụ cho đến khi tất cả ứng dụng huỷ liên kết.
Triển khai phương thức gọi lại trong vòng đời
Giống như hoạt động, dịch vụ có các phương thức gọi lại trong vòng đời mà bạn có thể triển khai để giám sát các thay đổi về trạng thái của dịch vụ và thực hiện công việc vào những thời điểm thích hợp. Bộ xương sau đây Service cho biết từng phương thức vòng đời:
Kotlin
class ExampleService : Service() { private var startMode: Int = 0 // indicates how to behave if the service is killed private var binder: IBinder? = null // interface for clients that bind private var allowRebind: Boolean = false // indicates whether onRebind should be used override funonCreate
() { // The service is being created } override funonStartCommand
(intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override funonBind
(intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override funonUnbind
(intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override funonRebind
(intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override funonDestroy
() { // The service is no longer used and is being destroyed } }
Java
public class ExampleService extends Service { int startMode; // indicates how to behave if the service is killed IBinder binder; // interface for clients that bind boolean allowRebind; // indicates whether onRebind should be used @Override public voidonCreate
() { // The service is being created } @Override public intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return startMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return binder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return allowRebind; } @Override public voidonRebind
(Intent intent) { // A client is binding to the service withbindService()
, // after onUnbind() has already been called } @Override public voidonDestroy
() { // The service is no longer used and is being destroyed } }
Lưu ý: Không giống như các phương thức gọi lại trong vòng đời hoạt động, bạn không cần thiết để gọi phương thức triển khai lớp cấp cao của các phương thức gọi lại này.
Hình 2 minh hoạ các phương thức gọi lại điển hình cho một dịch vụ. Mặc dù hình này tách riêng
các dịch vụ do startService()
tạo từ những
do bindService()
tạo, hãy giữ lại
hãy lưu ý rằng bất kỳ dịch vụ nào, dù được khởi đầu như thế nào, đều có khả năng cho phép khách hàng ràng buộc với dịch vụ đó.
Dịch vụ ban đầu được bắt đầu bằng onStartCommand()
(do ứng dụng gọi startService()
)
vẫn có thể nhận cuộc gọi đến onBind()
(khi khách hàng gọi
bindService()
).
Bằng cách triển khai các phương pháp này, bạn có thể theo dõi hai vòng lặp lồng nhau của dịch vụ vòng đời:
- Toàn bộ thời gian hoạt động của một dịch vụ xảy ra trong khoảng thời gian từ khi gọi
onCreate()
đến khionDestroy()
trả về. Giống như một hoạt động, một dịch vụ thực hiện việc thiết lập ban đầu trongonCreate()
và phát hành tất cả tài nguyên còn lại trongonDestroy()
. Ví dụ: một Dịch vụ phát nhạc có thể tạo chuỗi chứa nhạc được phát trongonCreate()
, sau đó có thể dừng chuỗi đó trongonDestroy()
.Lưu ý:
onCreate()
và phương thứconDestroy()
được gọi cho tất cả các dịch vụ, cho dù chúng dostartService()
hoặcbindService()
tạo. - Thời gian hoạt động của một dịch vụ bắt đầu bằng một lệnh gọi đến
onStartCommand()
hoặconBind()
. Mỗi phương thức đều được truyềnIntent
đã được truyền đếnstartService()
hoặcbindService()
.Nếu dịch vụ được bắt đầu, thời gian hoạt động sẽ kết thúc tại cùng thời điểm với toàn bộ thời gian kết thúc (dịch vụ vẫn hoạt động ngay cả sau khi
onStartCommand()
trả về). Nếu dịch vụ bị ràng buộc, thời gian hoạt động sẽ kết thúc khionUnbind()
trả về.
Lưu ý: Mặc dù dịch vụ đã bắt đầu bị dừng bởi một cuộc gọi đến
stopSelf()
hoặc stopService()
, thì sẽ không có lệnh gọi lại tương ứng cho lệnh
(không có lệnh gọi lại onStop()
). Trừ phi dịch vụ được liên kết với một ứng dụng,
hệ thống sẽ huỷ lệnh đó khi dịch vụ bị dừng – onDestroy()
là lệnh gọi lại duy nhất nhận được.
Để biết thêm thông tin về việc tạo dịch vụ cung cấp mối liên kết, hãy xem tài liệu về Dịch vụ ràng buộc,
bao gồm nhiều thông tin hơn về onRebind()
phương thức gọi lại trong phần Quản lý vòng đời của
một dịch vụ ràng buộc.