編輯:關於Android編程
服務有下面兩種基本形式:
Started方式:
當一個組件(比如一個activity)使用startService啟動一個服務的時候,就是用的started方式。一旦啟動,服務就可以在後台無限期地運行,即使啟動了service組件已經被銷毀。通常一個service都是執行一個單一的任務,並且不向它的調用者返回結果。例如,它可以從網上下載,或者上傳文件。當業務完成的時候,服務應該自發地結束。
Bound方式:
當一個應用組件使用bindService啟動service的時候,就是bound方式。bound方式的service 可以以CS的方式進行交互,可以實現組件訪問服務,發送請求,獲取結果,甚至使用IPC進行跨進程通信。bound的service的生命周期等於其bound的組件的生命周期。一次可以有多個組件bind service,當所有都解綁的時候,service就被銷毀了。
盡管前文將兩種service分別講解,但是你可以讓一個service同時具有這兩種特征,就是運行的時間無限制,同時被bind。你只要實現幾個回調的方法就可以實現:onStartConmmand()可以讓組件啟動service,onBind方法可以實現bind。
無論你的service是怎樣啟動的,start,bind還是二合一,其他的應用組件都可以訪問這個service,甚至是來自不同的應用的訪問。訪問的方式和組件使用activity的方式一樣,使用一個Intent。但是你可以在manifest聲明一個service為private,來阻塞同其他應用中的訪問。更多的內容請參見Decaring the service in the manifest。
警告:service運行在宿主進程的主線程當中,它不會創建它自己的線程,而且如果不進行聲明,它也不會在其他進程中。這就意味著如果你要在service中進行一些高CPU負載或者阻塞的操作,比如說後台播放MP3或者網絡訪問,你應該service中創建新的線程來完成這些工作。使用了分離的線程,你可減小應用無響應(ANR)的錯誤的風險,而且主線程可以專注於你的activity的交互。
基本知識 The Basics
應該通過創建一個繼承自service的類來創建service(或者已經存在的子類)。在構建中,你需要根據需求重寫其生命周期方法,並且提供組件bind service的機制。你需要重寫的最重要的回調函數是:
onStartCommand()
這個函數當一個組件,例如activity調用startService()來啟動service的時候,由系統進行調用。一旦這個方法執行,那麼service就可以在後台中無限期地運 行。如果你創建了這個service,那麼你要負責在它的工作結束的 時候將其止,通過調用stopSelf()或者stopService()。如果你僅僅想要提供bind功能,那麼你不需要實現這個方法。
onCreat()
系統在service第一次創建的時候會調用這個方法,來進行一些一次性的步驟操作(在調用onStratCommand 和onBind()之前)。如果這個service已經在運行了,這個方法就不會被調用了。
onDestroy()
當service不再使用而被銷毀的時候,系統會調用這個方法。你應該在這個方法中國來清理例如線程和注冊的監聽者和接收者等等的資源。這是service接收的最後的 一個調用方法。
*******
綠色側欄:應該使用service呢,還是使用線程呢?
service僅僅是一個在後台中運行的組件,在你的應用沒有和用戶交互的時候仍然保持運行。你只有在有這樣的需求的時候才需要創建一個service。
如果你只需要在用戶和你的應用交互的時候,在主線程之外進行一些操作,那麼你僅僅使用線程就可以了。例如你想要在activity運行的時候進行音樂播放,那麼你只需要onCreate()中創建一個線程,在onStrat()中啟動這個線程,在onStop()中結束這個線程。應該考慮使用AsyncTask或者HandlerThread來替代傳統的Thread類。更多的信息請參見Processes and Thread文檔。
要強調的是,你的service在默認情況實在你的應用的主線程中運行的,所以你仍然需要創建一個新的線程來處理高負荷或者阻塞的業務。
********
如果一個組件通過startService()來啟動Service(與此同時會調用onStartCommand),這樣的話service會一直運行下去,直到它調用stopSelf()方法自己結束,或者另一個組件中調用stopService來結束這個服務。
如果一個組件通過調用bindService()來創建服務(不會調用onStartCommand),那麼這個service就會運行直到bind的組件結束bind。一旦一個這樣service解除了所有組件的綁定,那麼系統就會銷毀service。
當系統內存低並且系統需要回收資源提供給用戶正在關注的activity的時候,系統就會強制停止service。如果service和一個用戶正在關注的activity綁定,那麼久不容易被回收。如果這個service被聲明為前台(run in background),那麼這個service就幾乎不會被回收。如果service被啟動並且運行了很久,那麼系統就會隨著時間降低它在後台任務列表中的地位,更有可能被系統回收——如果你的service啟動了,你必須設計讓它優雅地被系統重新啟動。如果系統殺死了你的service,只要系統的資源重新變得充裕,就會重新啟動你的service(盡管還和onStartCommand返回值有關,後面討論)。關於系統會殺死進程的說明,參見Process and Threading。
下面將介紹如何創建不同類型的service,以及怎樣從其他的組件中進行訪問。
在manifest清單文件中聲明一個service Declaring a service in the manifest
如同activity和其他組件,你需要在manifest中來聲明service。
通過添加
... ...
更多關於在清單中聲明service的內容參見
在
為了保證你的應用的安全,你應該使用顯式intent來啟動和bind你的service,同時不要給這個service聲明intent filter過濾器。如果考慮到解決啟動的service的歧義性的至關重要性,那麼你可以給你的service提供intent filter,並且拓展Intent中的組件的名字,與此同時你必須使用setPackage()來設置包,來消除目標service的歧義。
另外,通過設置android:export設置為false來保障你的service只可以在你的應用中訪問。這可以有效地阻止其他的應用對於你的service的訪問,即使是使用了顯式的intent。
創建一個Started方式的service Creating a Started Service
一個 started service是在另外一個組件中通過調用startService()啟動的,同時會導致service的onStartCommand方法的調用。
這樣的service啟動以後,它的聲明周期和啟動它的組件無關,可以在後台中無限期地運行,盡管啟動了這個service的組件已經被銷毀。因此,這樣service應該在業務結束以後調用stopSelf()自己結束,或者其他的組件中調用stopService()來結束。
一個應用組件,例如一個activity可以通過調用startServie並傳入一個Intent參數來指定service,同時傳入service需要的使用的數據。service在onStartCommand()方法中獲得接收到Intent。
舉個例子,假設一個activity需要在一個聯機數據庫上存儲一些數據。activity可以啟動一個伴隨的service,並且把要存儲的數據傳入的到intent中傳送給startService()中。service在onStartCommand()方法中接收這個intent,連接網絡並執行數據庫交互。當交互結束,service自發結束,然後被銷毀。
********
警告:默認情況下,一個service在聲明他的應用的進程中運行,而且在應用的主線程當中。所以當你的service執行高負載或者阻塞的操作的時候,用戶正在和acivity交互,那麼會降低你的activity的性能。避免這種影響,你應該在service中啟動新的線程。
********
通常在創建一個started service的時候,需要拓展下面兩個類。
Service
這是所有servcie的基類。拓展這個類的時候,要確保你的所有業務都是在新的線程中完成的,以為service使用的是應用的主線程,默認的設置會降低acitivity的性能。
IntentService
這是一種Service的子類,它使用工作線程來處理所有的啟動請求,一次處理一個。如果你不需要你的service來同時處理過個請求,那麼這種是最佳的選擇。你只需要實現onHandleIntent(),它會處理每一個start 請求的intent,這樣你就可以做後台工作了。
下面的章節講述怎樣使用其中的一些類來構建你的service。
拓展IntentService類 Extending the IntentService class
因為大多數的started service都不需要同時處理多請求(這其實是一個危險的多線程情景),這時你使用IntentService 類可能是最好的選擇。
IntentService會完成以下業務:
- 接受在onStartCommand()中傳入的intent並在主線程之外創建默認的工作線程來執行他們。
- 創建一個工作隊列,一次向你構建的onHandleIntent()中傳入一個intent,這樣你不用擔心多線程的問題。
- 在所有的start請求都被處理以後自動定制service,所以你不要調用stopSelf()。
- 提供一個默認返回null的onBind()函數。
- 提供一個默認的onStartCommand()的構造,並發送intent給工作隊列,然後發送給你構建的onHandIntent()。
綜上所述,你需要做的就是構建onHandleIntent(),來處理client提供的工作。(當然你自己還要寫一個service的構造器)
下面是一個IntentService的構造例子:
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.IntentServiec會使用啟動service傳來的intent,在默認的工作線程中調用這個方法。
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.這裡是要完成的一些業務,比如說下載文件。這裡用睡眠5秒演示。
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}
所以你只需要一個構造器,以及onHandleIntent()方法的實現。
如果你決定重寫其他的回掉函數,比如說onCreat(),onStartCommand()或者onDestroy(),確保同時調用父類的構造,這樣IntnetService才能恰當地處理工作線程的生命。
比如說,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); }
除了onHandlerIntent(),唯一的另一個你不需要在其中調用父類的方法就是onBind()(但是如果你的service允許綁定,你只需實現它)
在下一節中,拓展基類Service怎樣構造想通類型的service。裡面需要更多的代碼,但是對於處理同時的start請求,這種更適合。
拓展Service類 Extentds the Service class
如同你在上一節中看到的那樣,使用IntentService讓你的started類型的service的構建變得十分簡潔。然而如果你需要在service中完成多線程的工作(而不是在工作線程中使用工作隊列)。那麼可以通過拓展Service類來處理每一個intent。
作為對比,下面的例子處理的表現和上面的IntentService的例子相同。也就是對於每一個start請求,使用工作線程來處理任務,並且一次處理一個請求。
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. 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 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(); } }你可以看到,這比使用IntentService工作量要多得多。
但是因為你可以自己處理每一個onStarCommand()調用,你可以處理同時處理多個請求。這個例子中並沒有提現,但是如果你想要這麼做,那麼你可以給每一個請求創建一個線程並立刻運行(而不是等待上一個請求完成)。
這裡請注意onStartCommand()會返回一個整型值。這個數值表述了在這個服務被殺掉的情況下,系統如何從其啟動它(如同上面講到的,IntentService的默認構建會為你處理這個情況,當然你也可以修改它)。onStartCommand()的返回值一定要是下面幾種常量:
START_NOT_STICKY
如果系統在onStartCommand()返回以後殺掉了這個service,那麼這個service不會被重新創建,除非還存在要傳遞的pending intent。在不必要,以及你的應用可以簡潔地重新啟動未完成工作的情況下,使用這個選項是最安全的,可以避免對你的service的重復運行。
START_STICKY
如果在你的onStartCommand()返回以後service被殺掉,就會重新創建service並重新調用onStartCommand(),但是不會再次傳遞最後的intent。系統會用一個null intent來調用onStartCommand,除非有一個要啟動的service的pending intent,這時,就會傳遞intent。這適合於音樂播放這種不需要執行命令但是會無限期地運行並等待命令的情況。
START_REDELIVER_INTENT
如果在onStartCommand()返回以後系統殺掉了service,那麼就會重新創建service並使用最新的intent 調用onStartCommand()。任何的pending intent也會輪流被傳遞。這適用於為用戶提供一個活躍的功能的service,比如說下載文件這種需要立即被重新開始的。
更多關於返回值的細節,參考每個常量的參考文檔。
啟動一個Service Starting a Service
你可以通過傳入一個Intent(指定要啟動的service)參數到startService()方法來從activity或者其他的應用組件中來啟動service。Android系統會調用service的onStartCommand()方法並傳入Intent。(不要直接調用onStartCommand)
舉個例子,例如從activity中啟動例子中的service,使用startService()和intent參數。
Intent intent = new Intent(this, HelloService.class); startService(intent);startService()方法會立即返回,Android系統會調用service的onStartCommand()方法。如果這個service現在沒有運行,系統會先調用onCreate(),然後調用onStartCommand()。
如果service不支持bind,那麼使用startService和intent就是唯一的應用組件和service進行通信的方式。但是如果你想要service返回一個結果,那麼啟動service的組件可以創建一個broadcast的PendingIntent(使用getBroadcast()),然後在啟動service的intent中傳遞這個PendingIntent。這樣的話service就可以使用broadcast來傳遞結果了。
多個啟動service的請求會導致多個對於service的onStartCommand() 的調用。但是,但是停止service只需要一個stop的請求(通過stopSelf()或者stopService())。
停止一個serviceStopping a servcie
一個started service必須自己管理自己生命周期。也就是,系統不會停止或者銷毀一個service,除非是為了獲得內存空間,這是service會在onStartCommand()返回以後繼續運行。所以service必須通過stopSelf自己停止,或者從另一個組件中調用stopService()。
一旦請求停止,使用stopSelf()或者stopService(),系統會盡快立即停止這個服務。
但是,如果你的服務現在正在處理onStartCommand的多個請求,那麼那麼你在處理完這個start請求以後不要停止這個service。因為你可以已經接受到了一個新的請求(在第一個請求的最後停止服務可能會終結第二個)。為了避免這個問題,你可以使用stopSelf(int)方法來確保你對於service的stop請求總是基於最近的start請求的。也就是當你調用stopService(int),把對應的start請求的id傳入進去(conStartCommand方法中傳入的startid)。這樣如果在你調用stopSelf(int)以前service又接收到了一個新的start請求,那麼ID就會不一致,service也不會停止。
******警告*******
在service工作結束以後把它結束掉十分重要,這樣可以防止系統資源的浪費和電量的消耗。如果必要的話其他的組件可以使用stopService()方法來結束service。即使你可以bind service,如果service接收了onStartCommand()的調用,你就應該自己來管理結束service。
******************
創建一個BoundService Creating a Bound Service
一個boundService是可以讓組件通過調用bindService()來進行bind的service,來創建一個長時間的通信(通常不允許組件使用startService來啟動)。
你如果你想要你的組件和service交互,或者向其他的程序暴露你的應用的功能(比如通過IPC),你可以使用bound Service。
要創建一個boundService,你需要實現onBind()回調函數,它返回一個定義了和service交互的通信的接口。其他的應用組件可以調用bindServie()獲取接口,並調用其中的方法。這種servcie的存在是為了服務綁定它的應用的組件,所以如果沒有組件綁定的時候,系統就會銷毀這個service(你不需要如同onStartCommand()方式啟動的service那樣來停止一個bound的service)。
要創建一個bound service,你必須要做的第一件事就是定義接口來說明客戶端是如何訪問service的。客戶端和service之間的接口,就是實現一個IBinder,它也是你的service通過onBind()方法返回的對象。客戶端一旦接收到IBinder對象,就可以開始使用接口和service進行交互了。
多個客戶端可以 立刻和servcie bind。如果一個客戶端結束了和service的交互,可以通過unbindService()方法來解除綁定。一旦沒有對象綁定這種是service,那麼他就被系統銷毀。
有多中方式可以實現一個bound service,並且它的實現比啟動一個service要復雜的多,所以boundService的更多的討論在 Bound Service文章當中。
向用戶發送通知 Sending Notifications to the User
一旦開始運行,一個service就可以使用Toast Notification或者 Status Bar Notification來通知使用者。
toast提醒是一個在當前窗口上出現的提示,一段時間後消失,而狀態欄提醒在狀態欄提產生一個圖標和一個消息,用戶可以選擇他們並進行一定的處理(比如開啟一個activiy)。
通常,狀態欄對於完成任務的後台是一個最好的提醒技術(比如說完成了文件的下載)。當用戶下拉通知中選擇了一個提醒,這個提醒就可以啟動一個activity(比如說下載了的文件的視圖)。
更多的教程請查看 Toast Notification或者Status Bar Notification。
在前台運行一個Service
一個前台的service是一種用戶可以察覺的到,並且當內存低的時候不會被回收的一種服務。一個前台服務必須要在狀態欄顯示一個通知,他一般會被設置一個“正在運行”的標題,這意味著這個通知不會被清除,除非service停止了或者同前台移除了。
例如,一個音樂播放器使用service進行播放就應該設置為一個前台的服務,因為用戶需要一直清除他們的操作。在狀態欄的提示可能會指明正在播放的歌曲,並允許用戶啟動一個activity來和音樂播放器進行交互。
如果想要你的service在前台運行,請調用startForegound()。這個方法獲取兩個參數,一個整數唯一標識了通知,還有一個配合狀態欄的Notification對象。例如:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent); startForeground(ONGOING_NOTIFICATION_ID, notification);****警告******
傳遞給startForegound的ID不可以是0。
***************
想要從前台移除servcie,調用stopForegound()方法。這個方法獲取一個boolean類型,指明是否同時將狀態欄的通知也移除。這個方法不會結束掉service。但是你如果在這種service在前台運行的時候停止service,那麼通知也會被一同移除。
更多的關於你notification的內容請參見Creating Status Bar Notification。
管理service的生命周期 Managing the Lifecycle of a Service
service的生命周期要比activity簡單地多。但是關注service是怎樣被創建和銷毀的更加重要,因為是在後台運行的,其行蹤用戶無法察覺。
service的生命周期,從他被創建和被銷毀可以遵循下面兩種路徑:
- started類型的service
這種service在另一個組件調用startService的時候被創建。接下來service就可以無限期地運行了,只能通過調用stopSelf停止。也可以是另一個組件調用stopService啦進行停止。當service停止了,系統就會銷毀它。
- bound類型的service
當另一個組件(客戶端)調用bindService的時候,這種的service就會被創建。客戶端接著就可以通過一個IBinder接口來和service通信。客戶端可以通過調用unbindService()來結束通信。多個客戶端可以綁定同一個service,當所有都解綁的時候,系統就會銷毀service(不需要自己結束)。
這兩種路徑並不是完全分離的。也就是,你可以綁定一個已經使用startService()啟動的服務。例如,一個後台的音樂服務可以通過startService(),傳入一個指定播放什麼音樂的Intent來進行創建。接下來,可能當用戶想要控制播放器或者想獲得一些當前播放的音樂的信息的時候,一個activity可以通過調用bindService來綁定service。這種情況下,stopService()或者stopSelf(),這種情況下stopService或者stopSelf就不能真正地停止一個service,直到所有的客戶端都解綁它們才可以。
實現生命周期回調
如同一個activity,service擁有生命周期回調函數,你可以通過實現它們來檢測service的狀態變化,並且在合適的時候運行一些業務。下面的service框架證明了每一個生命周期方法。
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 intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return mStartMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return mBinder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return mAllowRebind; } @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 } }
與activity的生命周期的回調方法不同,你不需要調用超類的回調方法的實現。
******************
service的生命周期。左邊的圖展示了使用startService()啟動的service,右邊的圖展示了使用bindService創建的service的生命周期。
通過實現這些方法,你可以監控監控service生命周期的內部循環:
- entire lifetime:從service的onCreate被調用,到onDestroy()返回的這段整個的生命時間。如同一個activity,一個服務在onCreate中進行一些初始的步驟,在onDestroy()中釋放所有的資源。例如,一個音樂重放service可以在onCreate創建一個播放音樂的線程,並且在onDestroy()中結束這個線程。
onCreate和onDestroy方法可以為所有的services調用,不論是使用startService()創建的還是使用bindService()創建的。
- active lifetime:從onStartCommand()或者onBind()被調用開始的生命時間。兩個方法都是分別處理從startService()h和bindService()傳來的Intent。
如果一個服務是started的,active lifetime 結束的時間就是entre lifetime結束的時間(這種service在onStartCommand()返回以後仍然活躍)。如果service是bound的,那麼active lifetime在onUnbind()返回時結束。
******提示********
盡管一個started service可以通過stopSelf()或者stopService()結束,但是沒有一個對應的回調函數(沒有onStop回調函數)。所以除非這個service有綁定,否則如果service停止,就會被銷毀——onDestroy()是接收到的唯一的回調函數。
*******************
圖二展示了service的典型的回調方法。盡管途中將兩種創建servcie的方法分開說明,但是你要清楚任何service,都可以用客戶端綁定。也就是說使用onStartCommand()(客戶端的startService())方式啟動的service,也可以通過接受onBind()回調(當客戶端調用bindService()的時候)。
更多關於創建一個可以接受綁定的service的內容,請參見Bound Service文檔。其中包含更多關於onRebind()回調函數的內容,在Manageing the Lifecycle of a Bound Service一章中。
Android中實現圓角圖片的方式有很多種:一、shape二、.9圖三、XferMode四、BitmapShader五、ClipPath其中一、二兩種方法比較簡單粗暴,三
1、概述 關於手機圖片加載器,在當今像素隨隨便便破千萬的時代,一張圖片占據的內存都相當可觀,作為高大尚程序猿的我們,有必要掌握圖片的壓縮,緩存等處理,以到達
在Android下,事件的發生是在監聽器下進行,android系統可以響應按鍵事件和觸摸屏事件,本文主要介紹了button點擊事件的方法一、實現button點
Recovery的作用 Android利用Recovery模式,進行恢復出廠設置,全量包OTA升級,增量包升級。 升級一般通過運行升級包中的META-INF/com/go