建立 Android 服務Creating Android Services

本指南將討論「Xamarin Android 服務」,這是可在沒有作用中使用者介面的情況下執行工作的 Android 元件。服務非常常用於在背景中執行的工作,例如耗費時間的計算、下載檔案、播放音樂等等。它會說明服務適用的各種不同案例,並示範如何同時執行這些作業,以進行長時間執行的背景工作,以及提供介面來進行遠端程序呼叫。This guide discusses Xamarin.Android services, which are Android components that allow work to be done without an active user interface. Services are very commonly used for tasks that are performed in the background, such as time consuming calculations, downloading files, playing music, and so on. It explains the different scenarios that services are suited for and shows how to implement them both for performing long-running background tasks as well as for providing an interface for remote procedure calls.

Android 服務總覽Android Services Overview

行動應用程式不像桌面應用程式。Mobile apps are not like desktop apps. 桌上型電腦擁有膨脹佈滿的資源,例如螢幕房地產、記憶體、儲存空間,以及連線電源供應器、行動裝置。Desktops have copious amounts of resources such as screen real estate, memory, storage space, and a connected power supply, mobile devices do not. 這些條件約束會強制行動應用程式以不同的方式運作。These constraints force mobile apps to behave differently. 例如,行動裝置上的小型螢幕通常表示一次只會顯示一個應用程式(亦即活動)。For example, the small screen on a mobile device typically means that only one app (i.e. Activity) is visible at a time. 其他活動會移至背景,並推送到無法執行任何工作的已暫停狀態。Other Activities are moved to the background and pushed into a suspended state where they cannot perform any work. 不過,因為 Android 應用程式在背景中並不表示應用程式無法繼續運作。However, just because an Android application is in the background does not mean that it is impossible for app to keep working.

Android 應用程式由下列四個主要元件中的至少一個組成:活動廣播接收者內容提供者_和_服務Android applications are made up of at least one of the following four primary components: Activities, Broadcast Receivers, Content Providers, and Services. 活動是許多絕佳 Android 應用程式的基石,因為它們提供的 UI 可讓使用者與應用程式互動。Activities are the cornerstone of many great Android applications because they provide the UI that allows a user to interact with the application. 不過,在執行並行或背景工作時,活動不一定是最佳選擇。However, when it comes to performing concurrent or background work, Activities are not always the best choice.

在 Android 中背景工作的主要機制是_服務_。The primary mechanism for background work in Android is the service. Android 服務是設計用來在沒有使用者介面的情況下執行某些工作的元件。An Android service is a component that is designed to do some work without a user interface. 服務可能會下載檔案、播放音樂,或將篩選套用至影像。A service might download a file, play music, or apply a filter to an image. 服務也可以用於 Android 應用程式之間的處理序間通訊(IPC)。Services can also be used for interprocess communication (IPC) between Android applications. 例如,一個 Android 應用程式可能會使用來自另一個應用程式的音樂播放機服務,或應用程式可能會透過服務向其他應用程式公開資料(例如人員的連絡人資訊)。For example one Android app might use the music player service that is from another app or an app might expose data (such as a person's contact information) to other apps via a service.

服務及其執行背景工作的能力,對於提供順暢且流暢的使用者介面十分重要。Services, and their ability to perform background work, are crucial to providing a smooth and fluid user interface. 所有 Android 應用程式都有_主執行緒_(也稱為_UI 執行緒_),活動會在其上執行。All Android applications have a main thread (also known as a UI thread) on which the Activities are run. 為了讓裝置保持回應,Android 必須能夠以每秒60畫面的速率來更新使用者介面。To keep the device responsive, Android must be able to update the user interface at the rate of 60 frames per second. 如果 Android 應用程式在主執行緒上執行太多工作,則 Android 會捨棄畫面格,而使 UI 變得不變(有時也稱為_janky_)。If an Android app performs too much work on the main thread, then Android will drop frames, which in turn causes the UI to appear jerky (also sometimes referred to as janky). 這表示在 UI 執行緒上執行的任何工作,都應該在兩個框架之間的時間範圍內完成,大約是16毫秒(每隔60個框架1秒)。This means that any work performed on the UI thread should complete in the time span between two frames, approximately 16 milliseconds (1 second every 60 frames).

若要解決此問題,開發人員可以使用活動中的執行緒來執行某些會封鎖 UI 的工作。To address this concern, a developer may use threads in an Activity to perform some work that would block the UI. 不過,這可能會造成問題。However, this could cause problems. Android 可能會摧毀並重新建立活動的多個實例。It is very possible that Android will destroy and recreate the multiple instances of the Activity. 不過,Android 不會自動終結執行緒,這可能會導致記憶體流失。However, Android will not automatically destroy the threads, which could result in memory leaks. 其中一個主要的範例是當裝置旋轉– Android 會嘗試終結活動的實例,然後再重新建立一個新的實例:A prime example of this is when the device is rotated – Android will try to destroy the instance of the Activity and then recreate a new one:

當裝置旋轉時,會終結實例1,並建立實例2

這是潛在的記憶體流失,– 活動的第一個實例所建立的執行緒仍在執行中。This is a potential memory leak – the thread created by the first instance of the Activity will still be running. 如果執行緒具有活動第一個實例的參考,這會使 Android 無法進行垃圾收集物件的工作。If the thread has a reference to the first instance of the Activity, this will prevent Android from garbage collecting the object. 不過,仍會建立活動的第二個實例(接著可能會建立新的執行緒)。However, the second instance of the Activity is still created (which in turn might create a new thread). 快速連續旋轉裝置數次可能會耗盡所有的 RAM,並強制 Android 終止整個應用程式來回收記憶體。Rotating the device several times in rapid succession may exhaust all the RAM and force Android to terminate the entire application to reclaim memory.

根據經驗法則,如果要執行的工作應該 outlive 活動,則應該建立服務來執行該工作。As a rule of thumb, if the work to be performed should outlive an Activity, then a service should be created to perform that work. 不過,如果工作僅適用于活動的內容,則建立執行緒來執行工作可能會比較適當。However, if the work is only applicable in the context of an Activity, then creating a thread to perform the work might be more appropriate. 例如,針對剛新增至相片圖庫應用程式的相片建立縮圖,可能會發生在服務中。For example, creating a thumbnail for a photo that was just added to a photo gallery app should probably occur in a service. 不過,執行緒可能更適合用來播放一些音樂,只有當活動在前景時才會聽到。However, a thread might be more appropriate to play some music that should only be heard while an Activity is in the foreground.

背景工作可以分成兩個廣泛的分類:Background work can be broken down into two broad classifications:

  1. 長時間執行的工作 – 這是持續進行,直到明確停止為止。Long Running Task – This is work that is ongoing until explicitly stopped. _長時間_執行工作的範例是串流音樂或必須監視從感應器收集之資料的應用程式。An example of a long running task is an app that streams music or that must monitor data collected from a sensor. 即使應用程式沒有可見的使用者介面,這些工作也必須執行。These tasks must run even though the application has no visible user interface.
  2. 定期工作 – ( 有時稱為作業)週期性_工作_是持續時間相對較短(數秒)的工作,而且會依排程執行(也就是一天一次,也可能只在接下來的60秒)。Periodic Tasks – (sometimes referred to as a job) A periodic task is one that is of relatively short in duration (several seconds) and is run on a schedule (i.e. once a day for a week or perhaps just once in the next 60 seconds). 其中一個範例是從網際網路下載檔案,或產生影像的縮圖。An example of this is downloading a file from the internet or generating a thumbnail for an image.

Android 服務有四種不同的類型:There are four different types of Android services:

  • 系結的服務 – 系結_服務_,這是一種服務, 其系結了一些其他元件(通常是活動)。Bound Service – A bound service is a service that has some other component (typically an Activity) bound to it. 系結服務所提供的介面,可讓系結元件和服務彼此互動。A bound service provides an interface that allows the bound component and the service to interact with each other. 一旦不再有用戶端系結至服務,Android 就會關閉服務。Once there are no more clients bound to the service, Android will shut the service down.

  • IntentServiceIntentServiceService 類別的特殊子類別,可簡化服務的建立和使用。IntentService – An IntentService is a specialized subclass of the Service class that simplifies service creation and usage. IntentService 的目的是要處理個別的自發呼叫。An IntentService is meant to handle individual autonomous calls. 不同于可以同時處理多個呼叫的服務,IntentService 更像_工作佇列處理器_– 工作會排入佇列,而 IntentService 會在單一背景工作執行緒上一次處理一個作業。Unlike a service, which can concurrently handle multiple calls, an IntentService is more like a work queue processor – work is queued up and an IntentService processes each job one at a time on a single worker thread. 一般來說,IntentService 不會系結至活動或片段。Typically, anIntentService is not bound to an Activity or a Fragment.

  • 啟動服務 – 已_啟動_的服務是由其他 Android 元件(例如活動)啟動的服務,並會在背景中持續執行,直到明確告知服務停止為止。Started Service – A started service is a service that has been started by some other Android component (such as an Activity) and is run continuously in the background until something explicitly tells the service to stop. 與系結服務不同的是,已啟動的服務沒有任何直接系結到它的用戶端。Unlike a bound service, a started service does not have any clients directly bound to it. 基於這個理由,請務必設計已啟動的服務,讓它們可以視需要適當地重新開機。For this reason, it is important to design started services so that they may be gracefully restarted as necessary.

  • 混合式服務混合式服務,這項服務具有已_啟動服務_和系結_服務_的特性。Hybrid Service – A hybrid service is a service that has the characteristics of a started service and a bound service. 當元件系結至混合式服務,或可能由某個事件啟動時,就可以啟動它。A hybrid service can be started by when a component binds to it or it may be started by some event. 用戶端元件不一定會系結至混合式服務。A client component may or may not be bound to the hybrid service. 混合式服務會持續執行,直到明確告知停止,或直到沒有其他用戶端系結為止。A hybrid service will keep running until it is explicitly told to stop, or until there are no more clients bound to it.

要使用哪種類型的服務非常依賴應用程式需求。Which type of service to use is very dependent on application requirements. 根據經驗法則,IntentService 或系結服務對於 Android 應用程式必須執行的大部分工作都已足夠,因此應該將喜好設定提供給這兩種服務類型的其中一種。As a rule of thumb, an IntentService or a bound service are sufficient for most tasks that an Android application must perform, so preference should be given to one of those two types of services. IntentService 是「一次性」工作的理想選擇,例如下載檔案,而系結服務則適用于需要經常與活動/片段互動時。An IntentService is a good choice for "one-shot" tasks, such as downloading a file, while a bound service would be suitable when frequent interactions with an Activity/Fragment is required.

雖然大部分的服務都是在背景執行,但還是有一個特殊的子類別,稱為「前景服務」。While most services run in the background, there is a special sub-category known as a foreground service. 這是提供較高優先順序的服務(與一般服務相比),可為使用者執行一些工作(例如播放音樂)。This is a service that is given a higher priority (compared to a normal service) to perform some work for the user (such as playing music).

您也可以在同一部裝置上的自己進程中執行服務,這有時稱為_遠端服務_,或作為_跨進程服務_。It is also possible to run a service in it's own process on the same device, this is sometimes referred to as a remote service or as an out-of-process service. 這需要更多的建立工作,但在應用程式需要與其他應用程式共用功能時很有用,但在某些情況下,可以改善應用程式的使用者體驗。This does require more effort to create, but can be useful for when an application needs to share functionality with other applications, and can, in some cases, improve the user experience of an application.

Android 8.0 中的背景執行限制Background Execution Limits in Android 8.0

從 Android 8.0 (API 層級26)開始,Android 應用程式不再能夠在背景中自由執行。Starting in Android 8.0 (API level 26), an Android application no longer have the ability to run freely in the background. 在前景時,應用程式可以啟動並執行服務,而不受限制。When in the foreground, an app can start and run services without restriction. 當應用程式移至背景時,Android 會授與應用程式一段特定的時間來啟動和使用服務。When an application moves into the background, Android will grant the app a certain amount of time to start and use services. 經過該時間之後,應用程式就無法再啟動任何服務,而且任何已啟動的服務都將終止。Once that time has elapsed, the app can no longer start any services and any services that were started will be terminated. 此時,應用程式不可能執行任何工作。At this point it is not possible for the app to perform any work. 如果符合下列其中一個條件,Android 會將應用程式視為前景:Android considers an application to be in the foreground if one of the following conditions are met:

  • 有一個可見的活動(已啟動或已暫停)。There is a visible activity (either started or paused).
  • 應用程式已啟動前景服務。The app has started a foreground service.
  • 另一個應用程式會在前景中,並使用應用程式中的元件(在背景中則為)。Another app is in the foreground and is using components from an app that would be otherwise in the background. 例如,如果應用程式 A 位於前景,則系結至應用程式 B 所提供的服務。應用程式 B 也會被視為前景,而 Android 不會在背景中終止。An example of this is if Application A, which is in the foreground, is bound to a service provided by Application B. Application B would then also be considered in the foreground, and not terminated by Android for being in the background.

在某些情況下,即使應用程式在背景中,Android 還是會喚醒應用程式,並將這些限制放寬幾分鐘,讓應用程式能夠執行一些工作:There are some situations where, even though an app is in the background, Android will wake up the app and relax these restrictions for a few minutes, allowing the app to perform some work:

  • 應用程式會收到高優先順序的 Firebase 雲端訊息。A high priority Firebase Cloud Message is received by the app.
  • 應用程式會收到廣播。The app receives a broadcast.
  • 應用程式會接收並執行 PendingIntent 以回應通知。The application receives and executes a PendingIntent in response to a Notification.

現有的 Xamarin 應用程式可能必須變更其執行背景工作的方式,以避免 Android 8.0 上可能發生的任何問題。Existing Xamarin.Android applications may have to change how they perform background work to avoid any issues that might arise on Android 8.0. 以下是 Android 服務的一些實用替代方案:Here are some practical alternatives to an Android service:

  • 使用 Android 工作排程器或Firebase 作業發送器,將工作排程在背景中執行– 這兩個程式庫提供一個架構,讓應用程式將背景工作隔離到_作業_,這是離散的工作單位。Schedule work to run in the background using the Android Job Scheduler or the Firebase Job Dispatcher – These two libraries provide a framework for applications to segregate background work in to jobs, a discrete unit of work. 然後,應用程式可以使用作業系統來排程作業,以及有關作業何時可執行檔一些準則。Apps can then schedule the job with the operating system along with some criteria about when the job can run.
  • 在前景中啟動服務– 前景服務適用于應用程式必須在背景執行某些工作,而且使用者可能需要定期與該工作互動的情況。Start the service in the foreground – a foreground service is useful for when the app must perform some task in the background and the user may need to periodically interact with that task. 前景服務會顯示持續性通知,讓使用者知道應用程式正在執行背景工作,也會提供一種方式來監視或與工作互動。The foreground service will display a persistent notification so that the user is aware that the app is running a background task and also provides a way to monitor or interact with the task. 其中一個範例是播客應用程式,它會向使用者播放播客,或下載播客集,讓您可以在稍後欣賞。An example of this would be a podcasting app that is playing back a podcast to the user or perhaps downloading a podcast episode so that it can be enjoyed later.
  • 使用高優先順序的 Firebase 雲端訊息(FCM) – 當 Android 收到應用程式的高優先順序 FCM 時,它會允許該應用程式在短時間內于背景執行服務。Use a high priority Firebase Cloud Message (FCM) – When Android receives a high priority FCM for an app, it will allow that app to run services in the background for a short period of time. 這是讓背景服務在背景中輪詢應用程式的好方法。This would be a good alternative to having a background service that polls an app in the background.
  • 延遲當應用程式進入前景 – 時,如果先前的解決方案都不可行,則應用程式必須開發自己的方法,以便在應用程式進入前景時暫停和繼續工作。Defer work for when the app comes into the foreground – If none of the previous solutions are viable, then apps must develop their own way to pause and resume work when the app comes to the foreground.