Service之三种服务方式
(一)StartService
運(yùn)行Service的方法之一。任何繼承于android.content.Context的Android組件(component)都可以使用一個(gè)Intent(android.content.Intent)來(lái)開(kāi)啟一個(gè)Service。Intent里面可以以類對(duì)象(Class<?>)或者action進(jìn)行構(gòu)造,使用action構(gòu)造對(duì)于初學(xué)者來(lái)說(shuō)不太直觀,而且action構(gòu)造的適用范圍比類對(duì)象構(gòu)造使用范圍更廣,所以action構(gòu)造的方法將在本系列文章后面的內(nèi)容進(jìn)行介紹,現(xiàn)在就都使用類對(duì)象進(jìn)行構(gòu)造。
如果Service并沒(méi)有運(yùn)行,則新建一個(gè)Service(這里涉及到Service指派Task的問(wèn)題將在以后的內(nèi)容中介紹),并運(yùn)行之,相繼Service的回調(diào)onCreate(),onStart()/onStartCommand()(onStartCommand()是在Android2.0及之后SDK加入推薦使用的,用來(lái)代替老版的onStart()方法)被調(diào)用;如果Service已經(jīng)在運(yùn)行了,則只有回調(diào)onStart()/onStartCommand()被調(diào)用。
參看Android官方文檔可以發(fā)現(xiàn),onStart()和onStartCommand()均會(huì)傳入一個(gè)Intent對(duì)象參數(shù),這個(gè)Intent對(duì)象就是使用startService()時(shí)傳入的同一個(gè)Intent對(duì)象(同一個(gè)表示使用“==”比較返回true)。因?yàn)镮ntent對(duì)象可以攜帶Extra數(shù)據(jù),所以啟用Service的組件可以隨意的向Service傳遞Extra數(shù)據(jù)(使用putXXExtra()/putExtra()等方法,最常見(jiàn)的就是使用Java基本類型、String對(duì)象或者封裝在Bundle對(duì)象中作為Extra數(shù)據(jù),關(guān)于Extra數(shù)據(jù)的類型限制請(qǐng)自行參見(jiàn)文檔)。當(dāng)組件要傳數(shù)據(jù)到Service時(shí),只需要調(diào)用startService()并傳入相應(yīng)的攜帶了Extra數(shù)據(jù)的Intent對(duì)象,并在Service的onStart()/onStartCommand()中接收取出Extra數(shù)據(jù),而不用理會(huì)Service是否已經(jīng)運(yùn)行,數(shù)據(jù)始終能夠到達(dá)Service并進(jìn)行處理。這樣,就可以完成單向的IPC(進(jìn)程間通信)功能(組件->Service)。
當(dāng)不想再讓Service運(yùn)行的時(shí)候,只需要(任一)組件調(diào)用stopService()并傳入相應(yīng)的Intent對(duì)象,如果Service正在運(yùn)行,則會(huì)停止,如果Service沒(méi)有運(yùn)行,則系統(tǒng)會(huì)自動(dòng)當(dāng)什么事也沒(méi)發(fā)生。如果Service自己不想再運(yùn)行,可以在Service里使用stopSelf()自殺,可以看出,如果可以運(yùn)行到自殺代碼的話,那么Service肯定在運(yùn)行。
onStart()/onStartCommand()都會(huì)傳入?yún)?shù)startId:int,用來(lái)標(biāo)識(shí)Service的啟用號(hào)。每次startService()系統(tǒng)會(huì)自動(dòng)為開(kāi)啟的Service產(chǎn)生一個(gè)不同的startId,之前賦予它的startId(如果有)將會(huì)被覆蓋,并且這個(gè)新產(chǎn)生的startId會(huì)成為這個(gè)Service的新的startId,無(wú)論Service是否正在運(yùn)行。
考慮如下情況,當(dāng)多個(gè)組件啟用了同一個(gè)Service,Service提供互斥的服務(wù)(使用synchronized關(guān)鍵字),且要保證在Service把所有工作完成之前不能自殺,這個(gè)時(shí)候,startId就相當(dāng)有用了,在Service onStart()/onStartCommand()時(shí)把startId保存起來(lái),因?yàn)榛コ獾氖褂梅?wù),則Service是按順序提供服務(wù)的,則Service自殺的時(shí)候只用檢查當(dāng)前Service的startId與保存的這個(gè)startId是否相同,不同則說(shuō)明Service之后還有任務(wù),不能自殺,相同則說(shuō)明正在運(yùn)行的任務(wù)已經(jīng)是最后一個(gè)任務(wù)了,運(yùn)行完后就可以自殺(使用stopSelf(int startId)方法)。
啟動(dòng)服務(wù):
Intentintent=newIntent(getApplicationContext(),MyService.class);
startService(intent);
Service中代碼:
@Override
publicvoidonCreate(){
}
@Override
publicintonStartCommand(Intentintent,intflags,intstartId){
//接受傳遞過(guò)來(lái)的intent的數(shù)據(jù)等
returnSTART_STICKY;
}
@Override
publicvoidonDestroy(){
}
(二)bindService
綁定(bind)Service是開(kāi)啟Service的另一種方法,而且綁定Service幾乎可以被認(rèn)為是專門(mén)為IPC(進(jìn)程間交互)準(zhǔn)備的。綁定Serivce是通過(guò)Context.bindService()方法實(shí)現(xiàn)的,bindService和startService有一定的區(qū)別,首先就反應(yīng)在生命周期上。bindService不會(huì)回調(diào)onStart()/onStartCommand()方法,而會(huì)回調(diào)onBind()方法;Service要停止綁定則需要要調(diào)用unbindService()方法,而不用stopService()或者stopSelf()方法。
/**
* bindService
*
* @param service
* 用顯示的組件名(Class<?>方式)或者邏輯描述(action等)的Service的Intent
* @param conn
* 在Service開(kāi)啟或停止時(shí)接收信息的組件
* @param flags
* 綁定選項(xiàng),可以是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可以監(jiān)聽(tīng)服務(wù)的狀態(tài),在進(jìn)行服務(wù)綁定的時(shí),其標(biāo)志位可以為以下幾種(這里列出3種):
1).Context.BIND_AUTO_CREATE
說(shuō)明:表示收到綁定請(qǐng)求的時(shí)候,如果服務(wù)尚未創(chuàng)建,則即刻創(chuàng)建,在系統(tǒng)內(nèi)存不足需要先摧毀優(yōu)先級(jí)組件來(lái)釋放內(nèi)存,且只有駐留該服務(wù)的進(jìn)程成為被摧毀對(duì)象時(shí),服務(wù)才被摧毀
2).Context.BIND_DEBUG_UNBIND
說(shuō)明:通常用于調(diào)試場(chǎng)景中判斷綁定的服務(wù)是否正確,但容易引起內(nèi)存泄漏,因此非調(diào)試目的的時(shí)候不建議使用
3).Context.BIND_NOT_FOREGROUND
說(shuō)明:表示系統(tǒng)將阻止駐留該服務(wù)的進(jìn)程具有前臺(tái)優(yōu)先級(jí),僅在后臺(tái)運(yùn)行,該標(biāo)志位位于Froyo中引入。
Service的綁定方法bindService()中除了用了Service的Intent外,還使用到了ServiceConnection對(duì)象,這個(gè)對(duì)象除了可以為Service綁定者(caller)回調(diào)方法,還是解綁定(unbind)時(shí)需要提供的參數(shù)。bindService()方法中最后一個(gè)參數(shù)flag則是表明綁定Service時(shí)的一些設(shè)置,一般情況下可以直接使用0,有關(guān)這個(gè)問(wèn)題將在本系列文章以后的內(nèi)容中介紹。 android.content.ServiceConnection是一個(gè)接口,實(shí)現(xiàn)(implementate)這個(gè)接口有2個(gè)方法需要重寫(xiě)(Override)。一個(gè)是當(dāng)Service成功綁定后會(huì)被回調(diào)的onServiceConnected()方法,另一個(gè)是當(dāng)Service解綁定或者Service被關(guān)閉時(shí)被回調(diào)的onServiceDisconnected()。前者(onServiceConnected()方法)會(huì)傳入一個(gè)IBinder對(duì)象參數(shù),這個(gè)IBinder對(duì)象就是在Service的生命周期回調(diào)方法的onBind()方法中的返回值,它對(duì)Service的綁定式IPC起到非常重要的作用。
以下代碼是bindService()及unbindService()的慣常用法,代碼如下:
booleanmIsBound=false;
/**綁定服務(wù)*/
publicvoiddoBindService(){
bindService(newIntent(MainActivity.this,LocalService.class),mConnection,Context.BIND_AUTO_CREATE);
mIsBound=true;
}
/**解除綁定服務(wù)*/
publicvoiddoUnbindService(){
if(mIsBound){
//Detachourexistingconnection.
unbindService(mConnection);
mIsBound=false;
}
}
privateServiceConnectionmConnection=newServiceConnection(){
@Override
publicvoidonServiceConnected(ComponentNamename,IBinderservice){
mBoundService=((LocalService.LocalBinder)service).getService();
Toast.makeText(MainActivity.this,"服務(wù)連接",Toast.LENGTH_SHORT)
.show();
}
@Override
publicvoidonServiceDisconnected(ComponentNamename){
mBoundService=null;
Toast.makeText(MainActivity.this,"服務(wù)未連接",Toast.LENGTH_SHORT)
.show();
}
};
服務(wù)類中:
@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或者被停止時(shí)都會(huì)被回調(diào)。比如,當(dāng)Android系統(tǒng)資源(主要是RAM)嚴(yán)重不足時(shí),Service是很有可能被結(jié)束(kill)掉的,如果被kill掉,則onServiceDisconnected()方法會(huì)被回調(diào),但這個(gè)時(shí)候Service是沒(méi)有走完所有的生命周期的(比如不會(huì)回調(diào)onDestroy()方法)。當(dāng)然,無(wú)論Service的開(kāi)啟是使用bind還是start,一旦當(dāng)系統(tǒng)資源恢復(fù)之后,這些被kill掉的Service會(huì)以可能的最短的時(shí)間內(nèi)被系統(tǒng)自動(dòng)恢復(fù)(重新進(jìn)行新的生命周期,從回調(diào)onCreate()方法開(kāi)始)。
(三)ForegroundService
Foreground Service(意譯為前臺(tái)服務(wù))并不完全像其意譯的意思那樣是工作在前臺(tái)的Service,因?yàn)镾ervice實(shí)際上始終是工作在后臺(tái)的。由于Service工作在后臺(tái)的原因,使用者并不知道它在運(yùn)行,有時(shí)候開(kāi)發(fā)者需要使用者知道某個(gè)Service在運(yùn)行時(shí),就需要設(shè)計(jì)一種方案來(lái)解決這個(gè)問(wèn)題,F(xiàn)oreground Service就出現(xiàn)了。Foreground Service說(shuō)簡(jiǎn)單點(diǎn)其實(shí)就是在Service開(kāi)啟的時(shí)候使用通知(Notification),這也是Android官方推薦的方式,或者一些其它的方式(甚至可以是Activity,但使用Activity是基本沒(méi)有必要的)來(lái)告知用戶這個(gè)Service正在運(yùn)行。
在程序開(kāi)啟了Service,則使用一個(gè)“正在運(yùn)行”的通知表明服務(wù)正在運(yùn)行就可以了,也就是在Service的onCreate()回調(diào)或者onStart()/onStartCommand()回調(diào)中即可。雖然通知并不是一定需要的,或者說(shuō)故意不提示用戶有服務(wù)正在運(yùn)行(稍稍流氓一點(diǎn)的程序就會(huì)這樣),但是某些應(yīng)用商場(chǎng)的應(yīng)用審核就把通知提示做為了審核項(xiàng)目的。為了在Service周期(Life Cycle)結(jié)束的時(shí)候通知也能自動(dòng)消失,所以需要在Service的onDestroy()回調(diào)里面寫(xiě)上取消通知的代碼。以上就是配合通知自己實(shí)現(xiàn)的Foreground Service了。
當(dāng)然,除了自己處理通知的方法外,Google在Android 2.0(SDK level 5)以上的SDK提供了一個(gè)直接而簡(jiǎn)單的方法。直接使用Service.startForeground()和Service.stopForeground()進(jìn)行處理(注意,這兩個(gè)方法是Service類的)。下面看下Google提供的兩個(gè)接口:
/**
* 讓service成為Foreground Service,并且產(chǎn)生一個(gè)“正在運(yùn)行”
* 的通知。默認(rèn)情況下,service是后臺(tái)的,這意味著service在系統(tǒng)
* 回收內(nèi)存(比如在瀏覽器里顯示大圖片)的時(shí)候可以被毫無(wú)顧忌的
* kill掉。如果你比較在意這個(gè)service的掛掉,比如像后臺(tái)音樂(lè)播放
* 器這種突然掛了會(huì)影響用戶的情況,就可以使用Foreground
* Service來(lái)提示用戶。
*
* 參數(shù)
* 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屬性,允許在低內(nèi)存時(shí)被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對(duì)象會(huì)發(fā)布,使用stopForeground()之后,通知會(huì)被撤銷,當(dāng)Service銷毀(比如stopService()被調(diào)用)之后,通知也會(huì)被撤銷。stopForeground()僅僅只是去掉service的foreground屬性,并不會(huì)讓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里面調(diào)用 startForeground方法讓服務(wù)前臺(tái)運(yùn)行,然后再onDestroy里面調(diào)用stopForeground解除前臺(tái)運(yùn)行既可!
引申閱讀:
1.檢查Android后臺(tái)服務(wù)是否正在運(yùn)行?
通過(guò)系統(tǒng)級(jí)提供的方法ActivityManager.getRunningServices獲取到當(dāng)前正在運(yùn)行的服務(wù)
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的區(qū)別?
我們拿服務(wù)來(lái)進(jìn)行一個(gè)后臺(tái)長(zhǎng)時(shí)間的動(dòng)作,為了不阻塞線程,然而,Thread就可以達(dá)到這個(gè)效果,為什么我們不直接使用Thread去代替服務(wù)呢?
1). Thread:Thread 是程序執(zhí)行的最小單元,它是分配CPU的基本單位。可以用 Thread 來(lái)執(zhí)行一些異步的操作。
2). Service:Service
是android的一種機(jī)制,當(dāng)它運(yùn)行的時(shí)候如果是Local Service,那么對(duì)應(yīng)的 Service 是運(yùn)行在主進(jìn)程的 main
線程上的。如:onCreate,onStart 這些函數(shù)在被系統(tǒng)調(diào)用的時(shí)候都是在主進(jìn)程的 main 線程上運(yùn)行的。如果是Remote
Service,那么對(duì)應(yīng)的 Service 則是運(yùn)行在獨(dú)立進(jìn)程的 main 線程上。因此請(qǐng)不要把 Service
理解成線程,它跟線程半毛錢(qián)的關(guān)系都沒(méi)有!
既然這樣,那么我們?yōu)槭裁匆?br />
Service 呢?其實(shí)這跟 android 的系統(tǒng)機(jī)制有關(guān),我們先拿 Thread 來(lái)說(shuō)。Thread 的運(yùn)行是獨(dú)立于 Activity
的,也就是說(shuō)當(dāng)一個(gè) Activity 被 finish 之后,如果你沒(méi)有主動(dòng)停止
Thread 或者 Thread 里的 run 方法沒(méi)有執(zhí)行完畢的話,Thread 也會(huì)一直執(zhí)行。因此這里會(huì)出現(xiàn)一個(gè)問(wèn)題:當(dāng) Activity
被 finish 之后,你不再持有該 Thread 的引用。另一方面,你沒(méi)有辦法在不同的 Activity 中對(duì)同一 Thread 進(jìn)行控制。
舉個(gè)例子:如果你的
Thread 需要不停地隔一段時(shí)間就要連接服務(wù)器做某種同步的話,該 Thread 需要在 Activity
沒(méi)有start的時(shí)候也在運(yùn)行。這個(gè)時(shí)候當(dāng)你 start 一個(gè) Activity 就沒(méi)有辦法在該 Activity 里面控制之前創(chuàng)建的
Thread。因此你便需要?jiǎng)?chuàng)建并啟動(dòng)一個(gè) Service ,在 Service 里面創(chuàng)建、運(yùn)行并控制該
Thread,這樣便解決了該問(wèn)題(因?yàn)槿魏?br />
Activity 都可以控制同一 Service,而系統(tǒng)也只會(huì)創(chuàng)建一個(gè)對(duì)應(yīng) Service 的實(shí)例)。
因此你可以把 Service 想象成一種消息服務(wù),而你可以在任何有 Context
的地方調(diào)用
Context.startService、Context.stopService、Context.bindService,Context.unbindService,來(lái)控制它,你也可以在
Service 里注冊(cè) BroadcastReceiver,在其他地方通過(guò)發(fā)送 broadcast 來(lái)控制它,當(dāng)然這些都是 Thread
做不到的。
最后感謝分享,轉(zhuǎn)載于:https://www.juwends.com/tech/android/android-service-1.html(1-3)
總結(jié)
以上是生活随笔為你收集整理的Service之三种服务方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何注销淘宝店铺会员卡(如何注销淘宝店)
- 下一篇: 头脑不清醒反应迟钝是怎么回事儿(头脑不清