Service之三种服务方式
(一)StartService
運行Service的方法之一。任何繼承于android.content.Context的Android組件(component)都可以使用一個Intent(android.content.Intent)來開啟一個Service。Intent里面可以以類對象(Class<?>)或者action進行構造,使用action構造對于初學者來說不太直觀,而且action構造的適用范圍比類對象構造使用范圍更廣,所以action構造的方法將在本系列文章后面的內容進行介紹,現在就都使用類對象進行構造。
如果Service并沒有運行,則新建一個Service(這里涉及到Service指派Task的問題將在以后的內容中介紹),并運行之,相繼Service的回調onCreate(),onStart()/onStartCommand()(onStartCommand()是在Android2.0及之后SDK加入推薦使用的,用來代替老版的onStart()方法)被調用;如果Service已經在運行了,則只有回調onStart()/onStartCommand()被調用。
參看Android官方文檔可以發現,onStart()和onStartCommand()均會傳入一個Intent對象參數,這個Intent對象就是使用startService()時傳入的同一個Intent對象(同一個表示使用“==”比較返回true)。因為Intent對象可以攜帶Extra數據,所以啟用Service的組件可以隨意的向Service傳遞Extra數據(使用putXXExtra()/putExtra()等方法,最常見的就是使用Java基本類型、String對象或者封裝在Bundle對象中作為Extra數據,關于Extra數據的類型限制請自行參見文檔)。當組件要傳數據到Service時,只需要調用startService()并傳入相應的攜帶了Extra數據的Intent對象,并在Service的onStart()/onStartCommand()中接收取出Extra數據,而不用理會Service是否已經運行,數據始終能夠到達Service并進行處理。這樣,就可以完成單向的IPC(進程間通信)功能(組件->Service)。
當不想再讓Service運行的時候,只需要(任一)組件調用stopService()并傳入相應的Intent對象,如果Service正在運行,則會停止,如果Service沒有運行,則系統會自動當什么事也沒發生。如果Service自己不想再運行,可以在Service里使用stopSelf()自殺,可以看出,如果可以運行到自殺代碼的話,那么Service肯定在運行。
onStart()/onStartCommand()都會傳入參數startId:int,用來標識Service的啟用號。每次startService()系統會自動為開啟的Service產生一個不同的startId,之前賦予它的startId(如果有)將會被覆蓋,并且這個新產生的startId會成為這個Service的新的startId,無論Service是否正在運行。
考慮如下情況,當多個組件啟用了同一個Service,Service提供互斥的服務(使用synchronized關鍵字),且要保證在Service把所有工作完成之前不能自殺,這個時候,startId就相當有用了,在Service onStart()/onStartCommand()時把startId保存起來,因為互斥的使用服務,則Service是按順序提供服務的,則Service自殺的時候只用檢查當前Service的startId與保存的這個startId是否相同,不同則說明Service之后還有任務,不能自殺,相同則說明正在運行的任務已經是最后一個任務了,運行完后就可以自殺(使用stopSelf(int startId)方法)。
啟動服務:
Intentintent=newIntent(getApplicationContext(),MyService.class);
startService(intent);
Service中代碼:
@Override
publicvoidonCreate(){
}
@Override
publicintonStartCommand(Intentintent,intflags,intstartId){
//接受傳遞過來的intent的數據等
returnSTART_STICKY;
}
@Override
publicvoidonDestroy(){
}
(二)bindService
綁定(bind)Service是開啟Service的另一種方法,而且綁定Service幾乎可以被認為是專門為IPC(進程間交互)準備的。綁定Serivce是通過Context.bindService()方法實現的,bindService和startService有一定的區別,首先就反應在生命周期上。bindService不會回調onStart()/onStartCommand()方法,而會回調onBind()方法;Service要停止綁定則需要要調用unbindService()方法,而不用stopService()或者stopSelf()方法。
/**
* bindService
*
* @param service
* 用顯示的組件名(Class<?>方式)或者邏輯描述(action等)的Service的Intent
* @param conn
* 在Service開啟或停止時接收信息的組件
* @param flags
* 綁定選項,可以是0,BIND_AUTO_CREATE,BIND_DEBUG_UNBIND,BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT,BIND_ALLOW_OOM_MANAGEMENT或者BIND_WAIVE_PRIORITY
* @return 綁定成功為true,否則為false
*/
public abstract boolean bindService(Intent service, ServiceConnection conn,int flags);
ServiceConnection可以監聽服務的狀態,在進行服務綁定的時,其標志位可以為以下幾種(這里列出3種):
1).Context.BIND_AUTO_CREATE
說明:表示收到綁定請求的時候,如果服務尚未創建,則即刻創建,在系統內存不足需要先摧毀優先級組件來釋放內存,且只有駐留該服務的進程成為被摧毀對象時,服務才被摧毀
2).Context.BIND_DEBUG_UNBIND
說明:通常用于調試場景中判斷綁定的服務是否正確,但容易引起內存泄漏,因此非調試目的的時候不建議使用
3).Context.BIND_NOT_FOREGROUND
說明:表示系統將阻止駐留該服務的進程具有前臺優先級,僅在后臺運行,該標志位位于Froyo中引入。
Service的綁定方法bindService()中除了用了Service的Intent外,還使用到了ServiceConnection對象,這個對象除了可以為Service綁定者(caller)回調方法,還是解綁定(unbind)時需要提供的參數。bindService()方法中最后一個參數flag則是表明綁定Service時的一些設置,一般情況下可以直接使用0,有關這個問題將在本系列文章以后的內容中介紹。 android.content.ServiceConnection是一個接口,實現(implementate)這個接口有2個方法需要重寫(Override)。一個是當Service成功綁定后會被回調的onServiceConnected()方法,另一個是當Service解綁定或者Service被關閉時被回調的onServiceDisconnected()。前者(onServiceConnected()方法)會傳入一個IBinder對象參數,這個IBinder對象就是在Service的生命周期回調方法的onBind()方法中的返回值,它對Service的綁定式IPC起到非常重要的作用。
以下代碼是bindService()及unbindService()的慣常用法,代碼如下:
booleanmIsBound=false;
/**綁定服務*/
publicvoiddoBindService(){
bindService(newIntent(MainActivity.this,LocalService.class),mConnection,Context.BIND_AUTO_CREATE);
mIsBound=true;
}
/**解除綁定服務*/
publicvoiddoUnbindService(){
if(mIsBound){
//Detachourexistingconnection.
unbindService(mConnection);
mIsBound=false;
}
}
privateServiceConnectionmConnection=newServiceConnection(){
@Override
publicvoidonServiceConnected(ComponentNamename,IBinderservice){
mBoundService=((LocalService.LocalBinder)service).getService();
Toast.makeText(MainActivity.this,"服務連接",Toast.LENGTH_SHORT)
.show();
}
@Override
publicvoidonServiceDisconnected(ComponentNamename){
mBoundService=null;
Toast.makeText(MainActivity.this,"服務未連接",Toast.LENGTH_SHORT)
.show();
}
};
服務類中:
@Override
publicvoidonCreate(){
}
/**綁定的IBinder*/
privatefinalIBindermBinder=newLocalBinder();
publicclassLocalBinderextendsBinder{
publicLocalServicegetService(){
returnLocalService.this;
}
}
@Override
publicIBinderonBind(Intentintent){
returnmBinder;
}
@Override
publicbooleanonUnbind(Intentintent){
//TODOAuto-generatedmethodstub
returnsuper.onUnbind(intent);
}
http://t.zoukankan.com/需要注意的是,onServiceDisconnected()方法在Service被顯示的(explicitly)unbind或者被停止時都會被回調。比如,當Android系統資源(主要是RAM)嚴重不足時,Service是很有可能被結束(kill)掉的,如果被kill掉,則onServiceDisconnected()方法會被回調,但這個時候Service是沒有走完所有的生命周期的(比如不會回調onDestroy()方法)。當然,無論Service的開啟是使用bind還是start,一旦當系統資源恢復之后,這些被kill掉的Service會以可能的最短的時間內被系統自動恢復(重新進行新的生命周期,從回調onCreate()方法開始)。
(三)ForegroundService
Foreground Service(意譯為前臺服務)并不完全像其意譯的意思那樣是工作在前臺的Service,因為Service實際上始終是工作在后臺的。由于Service工作在后臺的原因,使用者并不知道它在運行,有時候開發者需要使用者知道某個Service在運行時,就需要設計一種方案來解決這個問題,Foreground Service就出現了。Foreground Service說簡單點其實就是在Service開啟的時候使用通知(Notification),這也是Android官方推薦的方式,或者一些其它的方式(甚至可以是Activity,但使用Activity是基本沒有必要的)來告知用戶這個Service正在運行。
在程序開啟了Service,則使用一個“正在運行”的通知表明服務正在運行就可以了,也就是在Service的onCreate()回調或者onStart()/onStartCommand()回調中即可。雖然通知并不是一定需要的,或者說故意不提示用戶有服務正在運行(稍稍流氓一點的程序就會這樣),但是某些應用商場的應用審核就把通知提示做為了審核項目的。為了在Service周期(Life Cycle)結束的時候通知也能自動消失,所以需要在Service的onDestroy()回調里面寫上取消通知的代碼。以上就是配合通知自己實現的Foreground Service了。
當然,除了自己處理通知的方法外,Google在Android 2.0(SDK level 5)以上的SDK提供了一個直接而簡單的方法。直接使用Service.startForeground()和Service.stopForeground()進行處理(注意,這兩個方法是Service類的)。下面看下Google提供的兩個接口:
/**
* 讓service成為Foreground Service,并且產生一個“正在運行”
* 的通知。默認情況下,service是后臺的,這意味著service在系統
* 回收內存(比如在瀏覽器里顯示大圖片)的時候可以被毫無顧忌的
* kill掉。如果你比較在意這個service的掛掉,比如像后臺音樂播放
* 器這種突然掛了會影響用戶的情況,就可以使用Foreground
* Service來提示用戶。
*
* 參數
* id The identifier for this notification as per
* NotificationManager.notify(int, Notification).
* notification The Notification to be displayed.
*/
publicfinalvoid startForeground (int id, Notification notification)
/**
* 去掉service的foreground屬性,允許在低內存時被kill掉
*
* Parameters
* removeNotification If true, the notification previouslyprovided to startForeground(int,Notification)
* will be removed. Otherwise it willremain until a later call removes
* it (or the service is destroyed).
*/
publicfinalvoid stopForeground (boolean removeNotification)
使用startForeground()之后,給出的Notification對象會發布,使用stopForeground()之后,通知會被撤銷,當Service銷毀(比如stopService()被調用)之后,通知也會被撤銷。stopForeground()僅僅只是去掉service的foreground屬性,并不會讓service停止。
android官方描述如下:
Running a Service in the Foreground
A foreground service is a service that's considered to be something the
user is actively aware of and thus not a candidate for the system to
kill when low on memory. A foreground service must provide a
notification for the status bar, which is placed under
the "Ongoing" heading, which means that the notification cannot be
dismissed unless the service is either stopped or removed from the
foreground.
For example, a music player that plays music from a service should be
set to run in the foreground, because the user is explicitly aware of
its operation. The notification in the status bar might indicate the
current song and allow the user to launch an activity
to interact with the music player.
To request that your service run in the foreground, callstartForeground().
This method takes two parameters: an integer that uniquely identifies the notification and theNotificationfor
the status bar. For example:
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, notification);
To remove the service from the foreground, callstopForeground(). This method takes a boolean, indicating whether to remove the status bar notification as well. This method doesnotstop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.
Note:The methodsstartForeground()andstopForeground()were introduced in Android 2.0 (API Level 5). In order to run your service in the foreground on older versions of the platform, you must use the previoussetForeground()method—see thestartForeground()documentation for information about how to provide backward compatibility.
For more information about notifications, seeCreating Status Bar Notifications.
我們只需要在onStartCommand里面調用 startForeground方法讓服務前臺運行,然后再onDestroy里面調用stopForeground解除前臺運行既可!
引申閱讀:
1.檢查Android后臺服務是否正在運行?
通過系統級提供的方法ActivityManager.getRunningServices獲取到當前正在運行的服務
privatebooleanisServiceRunning(){
ActivityManagermanager=(ActivityManager)getSystemService(ACTIVITY_SERVICE);
for(RunningServiceInfoservice:manager.getRunningServices(Integer.MAX_VALUE)){
if("com.example.MyService".equals(service.service.getClassName())){
returntrue;
}
}
returnfalse;
}
2.Service和Thread的區別?
我們拿服務來進行一個后臺長時間的動作,為了不阻塞線程,然而,Thread就可以達到這個效果,為什么我們不直接使用Thread去代替服務呢?
1). Thread:Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作。
2). Service:Service
是android的一種機制,當它運行的時候如果是Local Service,那么對應的 Service 是運行在主進程的 main
線程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程的 main 線程上運行的。如果是Remote
Service,那么對應的 Service 則是運行在獨立進程的 main 線程上。因此請不要把 Service
理解成線程,它跟線程半毛錢的關系都沒有!
既然這樣,那么我們為什么要用
Service 呢?其實這跟 android 的系統機制有關,我們先拿 Thread 來說。Thread 的運行是獨立于 Activity
的,也就是說當一個 Activity 被 finish 之后,如果你沒有主動停止
Thread 或者 Thread 里的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這里會出現一個問題:當 Activity
被 finish 之后,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。
舉個例子:如果你的
Thread 需要不停地隔一段時間就要連接服務器做某種同步的話,該 Thread 需要在 Activity
沒有start的時候也在運行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 里面控制之前創建的
Thread。因此你便需要創建并啟動一個 Service ,在 Service 里面創建、運行并控制該
Thread,這樣便解決了該問題(因為任何
Activity 都可以控制同一 Service,而系統也只會創建一個對應 Service 的實例)。
因此你可以把 Service 想象成一種消息服務,而你可以在任何有 Context
的地方調用
Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在
Service 里注冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,當然這些都是 Thread
做不到的。
最后感謝分享,轉載于:https://www.juwends.com/tech/android/android-service-1.html(1-3)
總結
以上是生活随笔為你收集整理的Service之三种服务方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何注销淘宝店铺会员卡(如何注销淘宝店)
- 下一篇: HMACSHA1 加密算法