Tổng quan về dịch vụ

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ọi stopSelf() hoặc stopService(). 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ột IBinder. 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ặc onBind()). 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()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ọi onStartCommand(), nhưng không phân phối lại ý định cuối cùng. Thay vào đó, hệ thống gọi onStartCommand() 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ọi onStartCommand() 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ọi stopSelf(). Một thành phần khác cũng có thể ngăn bằng cách gọi stopService(). 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ện IBinder. Ứng dụng có thể đóng kết nối bằng cách gọi unbindService(). 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 fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // 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 void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // 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. Vòng đời dịch vụ. Sơ đồ bên trái cho biết vòng đời khi dịch vụ được tạo bằng startService() và sơ đồ ở bên phải cho biết vòng đời khi dịch vụ được tạo cùng với bindService().

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 khi onDestroy() 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 trong onCreate() và phát hành tất cả tài nguyên còn lại trong onDestroy(). Ví dụ: một Dịch vụ phát nhạc có thể tạo chuỗi chứa nhạc được phát trong onCreate(), sau đó có thể dừng chuỗi đó trong onDestroy().

    Lưu ý: onCreate() và phương thức onDestroy() được gọi cho tất cả các dịch vụ, cho dù chúng do startService() hoặc bindService() 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ặc onBind(). Mỗi phương thức đều được truyền Intent đã được truyền đến startService() hoặc bindService().

    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 khi onUnbind() 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.