Service(1)
一個服務本質上接收兩種格式:
Started
當一個組件(比方活動)調用startService的時候 ,一個服務啟動。
一旦啟動,一個服務能夠在後臺無限期的執行。即使啟動他的組件被銷毀了。通常,一個啟動的服務執行一個單一的操作,不給調用者返回一個結果。舉例,服務可能通過網絡下載或者更新文件。
當操作完畢,服務應該自己停止自己。
Bound
當一個應用組件調用bindService綁定到他上面服務被bound。一個bound的服務,提供一個clientserver界面。同意組件和這個服務交互,發送請求。得到結果,甚至通過進程間通信實現這些。
一個bound服務,僅僅有在還有一個組件綁定到他上面才會運行。多個組件能夠立即綁定到這個服務,可是當他們都解綁了,服務被銷毀。
盡管這個文件通常獨立的討論這兩種服務。我們的服務能夠以這兩種方式工作。
能夠被啟動(無限期運行)並且同意綁定。主要就看我們是不是實現一對回調方法:onStartCommand同意組件啟動他,onBind同意綁定。
不管我們的程序是否啟動,bound,或者全部。不論什麽應用組件都能夠使用這個服務(即使從一個分開的程序)。不論什麽組件也能夠以相同的方式使用活動,通過intent啟動他。然後。我們能夠聲明服務為私有的。在manifest文件裏。阻止其它程序訪問他。
在下文會講吧。
小心:一個服務在他的宿主進程的主線程中執行,服務不創建他自己的線程。不在一個獨立的進程中執行(除非我們另外指定)。這就意味著。假設我們的服務去做不論什麽CPU集中的工作或者堵塞動作(比方mp3播放或網絡工作),我們應該在服務中新建一個線程做這些工作。
通過使用獨立的線程。我們能夠減少ANR錯誤的風險,而且我們的主線程能夠一直保持獻身於用戶交互使用活動。
The Basics
要創建一個服務,我們必須創建一個Service的子類(或者一個應經存在的子類)。在我們的實現中,假設有須要,重寫一些回調方法處理服務生命周期的主要方便和為組件提供一個綁定到服務的機制。最重要的能夠重寫的回調方法例如以下:
onStartCommand()
系統調用這種方法當還有一個組件,比方活動。通過調用startService要求這個服務啟動。一旦方法執行。服務被啟動。能夠在後臺無限期執行。假設我們實現這種方法,當服務的工作完畢後是我們的責任去停止服務。通過調用 stopSelf() 或 stopService()(假設只想提供綁定,不須要實現這種方法,非常明顯啊,這是start用的)
onBind()
系統調用這種方法當還有一個組件調用BingService希望綁定到這個服務上(比方運行RPC)。
在方法的實現中,我們必須提供一個接口。client用來和服務交流。通過返回一個LBinder。
必須總是實現這種方法,假設不同意綁定。應該返回null。
onCreate()
當服務第一次創建的時候系統調用他,執行一次性的設置程序(before it calls either onStartCommand() or onBind() 在??前)。
假設服務應經執行了,這種方法不會調用。
onDestroy()
系統調用這種方法當服務不在被使用而且將要被銷毀。我們的服務應該實現這種方法去清理不論什麽資源,比方線程,註冊的監聽器,接收器等。這是服務接收的最後一個調用的方法。
假設一個組件通過調用startService啟動服務(導致一個onStartCommand調用,然後這個服務保持執行,直到通過stopSelf停止或者還有一個組件調用stopService停止他。
假設一個組件調用bindService去創建一個服務(onStartCommand不會調用)。然後這個服務僅僅有在組件綁定上才運行。一旦服務和所以的client解除綁定,系統應該銷毀他。
安卓系統會強制停止一個服務僅僅有當內存非常低,必須為用戶操作的活動恢復系統資源的時候。假設服務綁定到用戶聚焦的活動上。他不太可能比殺死。假設服務被聲明run in the foreground (一會講),他差點兒不會被殺死。
否則,一個服務被啟動。長時間運行,系統會減少他在後臺任務的位置隨著時間推移。服務會變得非常easy被殺死,假設我們的服務啟動。我們必須非常優雅的設計去處理系統的重新啟動。假設系統殺死我們的服務。一旦資源可用會重新啟動服務(盡管這個也依靠我們從onStartCommand返回的值,一會講)。
For more information about when the system might destroy a service, see the Processes and Threading document.
Should you use a service or a thread?
A service is simply a component that can run in the background even when the user is not interacting with your application. Thus, you should create a service only if that is what you need.
一個服務不過一個能夠後天執行的組件,即使用戶不和我們的應用交互。這樣,我們應再唯獨須要的時候才創建一個服務。
假設須要在main線程之外運行任務,可是僅僅有在用戶和程序交互的時候,我們可能僅僅創建一個新線程而不是一個服務。舉例,想播放音樂。可是僅僅有活動運行的時候才願意,能夠在onCreate中創建一個線程,在onStart中開始運行。然後在onStop停止。
也能夠考慮使用 AsyncTask or HandlerThread,取代傳統線程。See the Processes and Threading document for more information about threads.
記住。假設使用一個服務,他仍然默認在主線程中執行,所以假設執行密集和堵塞操作。應該仍然創建一個新的線程。
以下開始講,怎樣創建每種類型的服務和從其它應用組件怎樣使用它。
Declaring a service in the manifest
和活動(其它組件一樣)。必須在應用的manifest文件裏定義所以的服務。
在<application>元素中加入一個<service>元素作為子元素。例如以下:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
能夠在<service>元素中包括其它元素定義屬性,不日啟動這個服務的權限和這個服務應該執行的進程。Android:name 是唯一必須的屬性,指定了服務的類名。
一旦公布應用,不應該改變這個名稱。由於這樣做,打破顯式的意圖或者綁定這個服務的代碼。查看blog Things That Cannot Change) 。
為了確保app的安全。總是使用顯式意圖啟動和綁定服務。不要給服務聲明intent過濾器。
To ensure your app is secure, always use an explicit intent when starting or binding your Service and do not declare intent filters for the service. If it‘s critical that you allow for some amount of ambiguity as to which service starts, you can supply intent
filters for your services and exclude the component name from the Intent, but you then must set the package for the intent with setPackage(), which provides sufficient disambiguation for the target service.
此外,設置 android:exported 屬性為false阻止其它程序啟動我們的服務。
這樣能夠有效的阻止其它程序啟動我們的服務,即使使用顯式意圖。(和活動非常像啊).
Creating a Started Service
一個被啟動的服務是還有一個組件通過調用startService啟動的,導致服務調用他的onStartCommand方法。
當一個服務是被啟動。它的生命周期獨立於啟動他的組件,服務能夠在後臺無限的執行,及時啟動他的組件被銷毀了。因此,服務應該在任務完畢的時候調用stopSelf停止或者其它組件調用stopService停止。
一個應用組件,比方一個活動能夠通過調用startService啟動服務,傳遞說明這個服務的和包括這個服務使用的信息的intent。
服務在onStartCommand方法中接收這個Intent。
打個例如,如果一個活動須要保存一些數據到線上的數據庫。活動能夠啟動一個同伴服務,通過傳遞一個inent給startService傳送給服務須要保存的數據。服務在onStartCommand接收這個Intent,連接網絡。運行數據庫事務。當事務結束,服務停止自己。然後被銷毀。
小心:默認,一個服務在和主線程所在的進程中運行。所以,假設服務運行集中的或者堵塞動作當用戶和應用程序的活動交互。服務會減緩活動表現。為了避免影響應用的表現。應該在服務中啟動一個新線程。
習慣上,有兩個類能夠實現去創建一個被啟動的服務:
Service
全部服務的基礎類。
當繼承這個類,非常重要去創建一個新的線程去運行所以的服務操作,由於服務默認使用應用的主線程,可能會減緩其它活動的運行。
IntentService
這是Service的一個子類。使用工作線程處理全部的啟開始請求,一次一個。假設不要求服務同一時候處理多個請求。這是最好的選擇。我們要做的就是實現onHandleIntent,為每個開始請求接收Intent,這樣就能夠做後臺工作。
This is a subclass of Service that uses a worker thread to handle all start requests, one at a time. This is the best option if you don‘t require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent(), which
receives the intent for each start request so you can do the background work.
Extending the IntentService class
由於大多數的被啟動的服務不須要同一時候處理多個請求(實際上多線程非常危急)。這時候應該最好使用IntentService。
IntentService做了以下的事:
The IntentService does the following:
? 創建一個默認的工作線程運行所以傳遞給onStartCommand的intents獨立於應用的主線程(新的線程了,所以不會幹擾,那還說我自己要新啟線程。應該是直接Service須要)
? 創建一個工作序列,一次傳遞一個intent給onHandleIntent實現。所以永遠不用操心多線程
? 全部的開啟請求被處理完之後停止服務,所以永遠不用調用stopSelf
? 提供onBind的默認實現。返回null
? 提供onStartCommand的默認實現,這種方法發送intent給工作序列。然後發送intent給onHandleIntent實現。
全部這些加起來。我們須要做的就是實現onHandleIntent。運行client提交的任務(盡管,還須要提供一個小的構造器給服務,以下會看到)
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
全部須要做的就是:一個構造器和一個onHandleIntent的實現
假設決定重寫其它的回調方法,比方onCreate,onStartCommand,onDe。確定調用父類的實現,這樣IntentService能夠妥善處理工作線程的生命。
舉例,onStartCommand方法必須返回默認的實現(這是intent怎樣被交付給onHandleIntent)。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
除了onHandleIntent,唯一不須要調用的父類方法是onBind(可是你只須要實現服務同意綁定)。
在以下會看到Service實現的相同的服務。會有很多其它的代碼。可是假設要同一時候處理多個開始請求是合適的。
Extending the Service class
上面展示的,使用IntentService讓我們實現一個啟動服務很easy。假設,然而。須要服務運行多線程(而不是處理啟動請求通過一個工作序列)。然後能夠繼承Service類去處理每個Intent。
作為對照,以下的代碼是一個Service類的實現。運行和上面IntentService一樣的工作。那就是,為每個啟動請求。他使用一個工作線程去運行這個任務,一次僅僅處理一個請求。
For comparison, the following example code is an implementation of the Service class that performs the exact same work as the example above using IntentService. That is, for each start request, it uses a worker thread to perform the job and processes only one
request at a time.
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 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.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 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 will not 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
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@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 = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.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();
}
}
然後。由於處理每個調用給onStartCommand,能夠同一時候運行多個請求(和上面什麽差別。沒懂?)。這個樣例沒有這做,可是假設想這麽做。為每個請求創建一個新線程(就是新建一個線程,上面的樣例沒有新建一個線程。多個線程怎麽停啊,這是個問題??),然後立刻運行他們(而不是等待上一個運行完成)
多個停止是,為每個request設置一個Id。然後假設這個ID和正在運行的不一致就不會停止,上面的樣例不行啊,多個線程。多次調用這個stop。萬一有一個成功呢,還須要研究。怎麽停止。下文也沒講清楚啊。
註意到,onStartCommand方法必須返回一個整數,這個整數是一個數值描寫敘述系統應該怎樣繼續這個服務在這個事件當系統殺死他(上面調理,對於Intentservice默認的實現幫我們處理。盡管我們能夠改動他)。onStartCommand的返回值必須的以下其中的一個常量:
START_NOT_STICKY
If the system kills the service after onStartCommand() returns, do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart
any unfinished jobs.
START_STICKY
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the
service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
START_REDELIVER_INTENT
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively
performing a job that should be immediately resumed, such as downloading a file.
Stopping a service
A started service must manage its own lifecycle. That is, the system does not stop or destroy the service unless it must recover system memory and the service continues to run after onStartCommand() returns. So, the service must stop itself by calling stopSelf()
or another component can stop it by calling stopService().
Once requested to stop with stopSelf() or stopService(), the system destroys the service as soon as possible.
說是這個ID是建立在最新的一個請求的。也就是說前面的請求的id和傳給onStartCommand方法的ID不一致,僅僅要有新請求。id就變,會不會有開始僅僅有一個運行。然後其它等到這個運行了還沒運行。不就stop了。呵呵
However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn‘t stop the service when you‘re done processing a start request, because you might have since received a new start request (stopping at the end of the first
request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request
(the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.
Caution: It‘s important that your application stops its services when it‘s done working, to avoid wasting system resources and consuming battery power. If necessary, other components can stop the service by calling stopService(). Even if you enable binding
for the service, you must always stop the service yourself if it ever received a call to onStartCommand().
?
Creating a Bound Service
一個綁定服務是一個服務同意應用組件通過調用bindService綁定到他上面創建一個長期存在的關系(通常不同意組件調用startService啟動他)。
當我們想和來自程序中的活動或者其它組件的服務交互或者通過進程通信暴露一些應用程序的功能給其它應用。應該創建一個綁定服務。
(1)要創建一個綁定服務,必須實現onBind方法,返回一個定義和服務通信的接口的IBinder(2)其它應用組件然後能夠調用bindService(這種方法有個ServiceConnection參數,須要設置一些)方法獲得這個接口,開始調用服務上的方法。
要創建一個綁定服務,第一件要做的事就是定義指定client怎樣跟服務通信的接口。(一般我們的是不在服務中創建一個Binder(實現IBinder接口)的實例)。這個接口必須是一個IBinder的實現,同一時候是服務必須在onBind回調方法返回的。一旦client收到這個IBinder。能夠通過那個接口和服務交互。
多個client能夠同一時候綁定到一個服務。當一個client和服務運行完交互,調用unBindService去解綁。一旦沒有client綁定到服務上,系統銷毀這個服務。
有多種方式實現一個綁定服務,並且實現比啟動的服務更復雜。所以這個綁定服務在獨立的綁定服務文檔討論。
?
Managing the Lifecycle of a Service
服務的生命周期和活動非常像。
然而,更重要的是,我們應該更關註我們的服務怎樣創建和銷毀。由於一個服務能夠在用戶不知道情況下在後臺執行。
服務的生命周期重創建到銷毀有這兩個路勁:
? A started service
還有一個組件調用startService()啟動這個服務.服務然後在後臺無限期執行,必須調用stopSelf停止自己。
還有一程序也能夠調用stopService停止。當服務停止,系統銷毀他。
? A bound service
當還有一個組件(一個client)調用bindService的時候,服務被創建。client然後通過IBinder接口和服務通信。多個client能夠綁定到同一個服務。當他們所有解綁,系統銷毀這個服務(服務不須要自己停止)。
這兩個路徑不是全然分離的。
那就是。能夠綁定到一個已經由startSercie啟動的服務。
舉例,一個後臺音樂服務能夠通過調用statService用一個確認要播放的音樂Intent啟動。然後,可能當用戶想運行一些控制在這個播放器或者得到當前歌曲的信息。一個活動通過調用bindService綁定到服務上。
在這種樣例,stopService() or stopSelf() 不能停止服務直到全部的client解綁。(全部client解綁,應該也不停止服務吧。除非調用這個兩個方法)
Implementing the lifecycle callbacks
和活動非常像。一個服務有生命周期回調方法我們能夠實現去監聽服務的狀態改變,在適合的實際運行工作。以下的骨架服務展示了每個生命周期方法。
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // 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 mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@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
}
}
Note:不像活動的生命周期回來方法,不用調用父類的這些方法的實現。
Figure 2. The service lifecycle. The diagram on the left shows the lifecycle when the service is created with startService() and the diagram on the right shows the lifecycle when the service is created with bindService().
通過實現寫方法,能夠監聽兩個嵌套的服務生命周期循環。
? 服務的整個生命周期發生在onCreate被調用和onDestroy返回之間。
項一個活動。服務在onCreate進行初始化設置,在onDestroy中釋放全部資源。舉例。一個音樂播放服務能夠在onCreate中創建音樂播放的線程。然後在onDestroy中停止這個線程。
能夠看到。onCreate()和 onDestroy()方法被全部服務調用,不管他們的由startService() 還是bindService()創建。
? 服務的活躍生命時間 從onStartCommand或者onBind開始。每一個方法分別處理來自startService或者bindService傳遞的intent
假設服務是被啟動,活躍生命周期隨著整個生命周期結束而結束(服務仍然是活躍的,甚至在onStartCommand返回之後)。假設服務是綁定的,活躍生命時間隨著onUnbind返回結束。
Note:盡管一個被啟動的服務由stopSelf() or stopService()停止, 服務沒有一個回調方法(沒有onStop回調)。所以。除非服務綁定到一個client。否則當服務停止系統銷毀他。onDestroy是被接收唯一的回調方法。
上圖闡明了服務的典型的回調方法。
盡管該圖分離了由startService和bindService創建的服務,在心中銘記。不論什麽服務。不管怎樣啟動,能夠潛在的同意client綁定他。
所以,一個最初有onStartCommand方法(通過client調用startService)啟動的服務。仍然能夠接受一個onBind(當一個client調用bindService) bind 能夠被啟動嗎,應該是不能夠吧。
查看Bound Service很多其它。
?
?
Service(1)