什么是Service
先看一下google官方的介紹:
Service
是一個(gè)可以在后臺執(zhí)行長時(shí)間運(yùn)行操作而不提供用戶界面的應(yīng)用組件。服務(wù)可由其他應(yīng)用組件啟動(dòng)岔乔,而且即使用戶切換到其他應(yīng)用船老,服務(wù)仍將在后臺繼續(xù)運(yùn)行咖熟。 此外,組件可以綁定到服務(wù)努隙,以與之進(jìn)行交互球恤,甚至是執(zhí)行進(jìn)程間通信 (IPC)。 例如荸镊,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)咽斧、播放音樂,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互躬存,而所有這一切均可在后臺進(jìn)行
總結(jié)一下张惹,Service是一個(gè)應(yīng)用組件,它有以下特點(diǎn):
1.不需要提供用戶界面
2.可以在后臺長時(shí)間運(yùn)行
默認(rèn)情況下岭洲,服務(wù)運(yùn)行在UI線程宛逗,執(zhí)行耗時(shí)操作需要開辟子線程,否則會(huì)引起ANR盾剩。
服務(wù)通常分為兩種形式:
1.普通服務(wù)雷激,通過startService啟動(dòng)替蔬,一旦啟動(dòng),服務(wù)即可在后臺無限期運(yùn)行屎暇,即使啟動(dòng)服務(wù)的組件已被銷毀也不受影響承桥。
直到stopService被調(diào)用,或者Service本身調(diào)用了stopSelf根悼,才會(huì)停止凶异。
2.Bound 服務(wù),當(dāng)應(yīng)用組件通過調(diào)用 [bindService()](https://developer.android.google.cn/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務(wù)時(shí)挤巡,服務(wù)即處于“綁定”狀態(tài)剩彬。綁定服務(wù)提供了一個(gè)客戶端-服務(wù)器接口,允許組件與服務(wù)進(jìn)行交互矿卑、發(fā)送請求喉恋、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作粪摘。 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí)瀑晒,綁定服務(wù)才會(huì)運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù)徘意,但全部取消綁定后苔悦,該服務(wù)即會(huì)被銷毀。
實(shí)際上椎咧,普通Service也可以與組件綁定玖详,關(guān)鍵在于是否實(shí)現(xiàn)了[onStartCommand()](https://developer.android.google.cn/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int))(允許組件啟動(dòng)服務(wù))和 onBind()(允許綁定服務(wù))這兩個(gè)回調(diào)方法。這樣的服務(wù)需要解除綁定并stop才會(huì)銷毀勤讽。
關(guān)鍵方法和生命周期
onCreate()
首次創(chuàng)建服務(wù)時(shí)蟋座,系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序(在調(diào)用 onStartCommand() 或 onBind() 之前)。如果服務(wù)已在運(yùn)行脚牍,則不會(huì)調(diào)用此方法向臀。
onStartCommand()
當(dāng)另一個(gè)組件(如 Activity)通過調(diào)用 startService() 請求啟動(dòng)服務(wù)時(shí),系統(tǒng)將調(diào)用此方法诸狭。一旦執(zhí)行此方法券膀,服務(wù)即會(huì)啟動(dòng)并可在后臺無限期運(yùn)行。 如果您實(shí)現(xiàn)此方法驯遇,則在服務(wù)工作完成后芹彬,需要由您通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)。(如果您只想提供綁定叉庐,則無需實(shí)現(xiàn)此方法舒帮。)
onBind()
當(dāng)另一個(gè)組件想通過調(diào)用 bindService() 與服務(wù)綁定時(shí),系統(tǒng)將調(diào)用此方法。在此方法的實(shí)現(xiàn)中玩郊,必須通過返回 IBinder 提供一個(gè)接口肢执,供客戶端用來與服務(wù)進(jìn)行通信。請務(wù)必實(shí)現(xiàn)此方法瓦宜,但如果您并不希望允許綁定蔚万,則應(yīng)返回 null岭妖。
onUnbind()
當(dāng)組件通過unbindService與Service解綁時(shí)临庇,系統(tǒng)會(huì)調(diào)用此方法。
onDestroy()
當(dāng)服務(wù)不再使用且將被銷毀時(shí)昵慌,系統(tǒng)將調(diào)用此方法假夺。服務(wù)應(yīng)該實(shí)現(xiàn)此方法來清理所有資源,如線程斋攀、注冊的偵聽器已卷、接收器等。 這是服務(wù)接收的最后一個(gè)調(diào)用
如果組件通過調(diào)用 startService() 啟動(dòng)服務(wù)(這會(huì)導(dǎo)致對 onStartCommand() 的調(diào)用)淳蔼,則服務(wù)將一直運(yùn)行侧蘸,直到服務(wù)使用 stopSelf() 自行停止運(yùn)行,或由其他組件通過調(diào)用 stopService() 停止它為止鹉梨。
如果組件是通過調(diào)用 bindService() 來創(chuàng)建服務(wù)(且未調(diào)用 onStartCommand()讳癌,則服務(wù)只會(huì)在該組件與其綁定時(shí)運(yùn)行。一旦該服務(wù)與所有客戶端之間的綁定全部取消存皂,系統(tǒng)便會(huì)銷毀它晌坤。
也就是說,startService與stopService旦袋,bindService與unbindService是對應(yīng)關(guān)系骤菠,startService必須通過stopService來停止,這時(shí)候調(diào)用的是onStartCommand()和onDestroy()疤孕;bindService必須通過unbindService來停止商乎,這時(shí)候調(diào)用的是onBind()和onDestroy()。
到這里祭阀,Service的生命周期已經(jīng)很清楚了:
onCreate()→onStartCommand()/onBind()(→onUnbind())→onDestroy()
注:系統(tǒng)因內(nèi)存過低等原因鹉戚,回收掉服務(wù)的時(shí)候,onDestroy是不會(huì)執(zhí)行的柬讨。 如果服務(wù)已經(jīng)運(yùn)行崩瓤,調(diào)用startService時(shí),不會(huì)重新執(zhí)行onCreate踩官,只會(huì)執(zhí)行onStartCommand()却桶;
Service實(shí)例:
1.普通Service:
首先定義一個(gè)Service,并重寫相應(yīng)方法
public class MyService extends Service{
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d("Service", "----->onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Log.d("Service", "----->onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("Service", "----->onDestroy");
}
}
然后在Activity中啟動(dòng)/停止該服務(wù)
public class ServiceActivity extends Activity implements OnClickListener{
private Button startButton;
private Button stopButton;
Intent i;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.service_activity);
startButton=(Button) findViewById(R.id.start_service);
stopButton=(Button) findViewById(R.id.stop_service);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.start_service:
i=new Intent(this,MyService.class);
startService(i);
break;
case R.id.stop_service:
i=new Intent(this,MyService.class);
stopService(i);
break;
default:
break;
}
}
}
當(dāng)然,也不要忘記在manifest文件中注冊颖系,四大組件都是需要注冊的嗅剖。
這樣,一個(gè)簡單的服務(wù)就完成了嘁扼。
2.Bound服務(wù)
Bound服務(wù)是將啟動(dòng)Service的組件(通常是Activity)與Service綁定起來信粮,這樣Activity和Service可以非常簡單的進(jìn)行通信。實(shí)例如下:
1.定義一個(gè)Binder類趁啸,并在onbind方法中返回它的實(shí)例:
public class MyService extends Service{
private MyBinder mBinder=new MyBinder();
public class MyBinder extends Binder{
public void startDownload(){
Log.d("Service", "---->Start Download.....");
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d("Service", "----->onBind");
return mBinder;
}
}
2.在Activity中啟動(dòng)時(shí)强缘,使用bindService方法。
該方法有三個(gè)參數(shù):
Intent不傅,表示要啟動(dòng)的Service旅掂;
connection:實(shí)現(xiàn)ServiceConnection接口的類,該接口中有兩個(gè)方法:onServiceDisconnected和onServiceConnected访娶,onbind方法返回的Binder會(huì)傳入onServiceConnected商虐,從而對服務(wù)進(jìn)行操作;
Flag:通常選用BIND_AUTO_CREATE崖疤,bindService時(shí)會(huì)自動(dòng)創(chuàng)建服務(wù)秘车。
代碼如下:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Service", "----->onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("Service", "----->onServiceConnected");
mBinder=(MyService.MyBinder) service;
mBinder.startDownload();
}
};
Intent i=new Intent(this,MyService.class);
bindService(i, connection , BIND_AUTO_CREATE);
注意:onServiceDisconnected函數(shù)并不是unbind時(shí)候調(diào)用的,正常情況下該函數(shù)不會(huì)被調(diào)用劫哼,只有意外斷開連接的時(shí)候叮趴,比如Service被系統(tǒng)回收等,才會(huì)調(diào)用沦偎。
同一個(gè)Service可以被許多組件同時(shí)綁定疫向,返回的也是相同的Binder對象。只有當(dāng)所有組件都解綁時(shí)豪嚎,Service才會(huì)銷毀搔驼。
3.遠(yuǎn)程Service:
遠(yuǎn)程服務(wù)是指,單獨(dú)運(yùn)行在一個(gè)進(jìn)程中的服務(wù)侈询。使用遠(yuǎn)程Service也很簡單舌涨,只要設(shè)置manifest文件里Service的屬性:android:process=":remote"(意思是在當(dāng)前應(yīng)用下新建一個(gè)進(jìn)程,名字是包名+remote)
使用遠(yuǎn)程Service不會(huì)產(chǎn)生ANR問題扔字,它獨(dú)立運(yùn)行在新進(jìn)程中囊嘉,會(huì)產(chǎn)生一個(gè)問題,如何與Activity進(jìn)行通信革为?可以試一下扭粱,遠(yuǎn)程Service是不能與Activity綁定的,bindService不能用在遠(yuǎn)程Service上震檩,所以這里就涉及到IPC的概念了琢蛤。
介紹幾種比較常用的方法:
AIDL通信
首先新建一個(gè)AIDL文件蜓堕,在文件中定義好Activity與Service通信的方法:
在名為ServiceAIDL.aidl的文件中,定義以下方法:
package com.training.service;
interface ServiceAIDL {
int startDownload();
}
保存之后在gen文件夾下會(huì)生成對應(yīng)的.java文件博其。
然后在Service中實(shí)現(xiàn)該接口套才,代碼如下:
ServiceAIDL.Stub mBinder = new ServiceAIDL.Stub() {
@Override
public void startDownload() throws RemoteException {
// TODO Auto-generated method stub
Log.d("Service", "----->Remote Service Start download.....");
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d("Service", "----->onBind");
return mBinder;
}
ServiceAIDL.Stub是一個(gè)實(shí)現(xiàn)了AIDL文件中定義的接口ServiceAIDL的類。它繼承自Binder類慕淡,Service中取得它的實(shí)例之后背伴,在onBind方法中返回,就可以把該實(shí)例傳遞到Activity中了峰髓。
Activity中通過ServiceConnection來操作binder:
private ServiceConnection reoteConnection=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mServiceAIDL=ServiceAIDL.Stub.asInterface(service) ;
try {
int result=mServiceAIDL.plus(3, 5);
Log.d("Service", "Result is "+ result);
mServiceAIDL.startDownload();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Intent i=new Intent(this,RemoteService.class);
bindService(i, reoteConnection , BIND_AUTO_CREATE);
這樣傻寂,一個(gè)簡單的AIDL通信就完成了。
遠(yuǎn)程Service是可以垮應(yīng)用共享的儿普,可以通過隱式Intent從任何Activity啟動(dòng)并操作他崎逃。
Messager通信
1:Service中定義兩個(gè)Messenger:
一個(gè)server端的messenger,該messenger會(huì)通過onbind方法傳遞給client端眉孩,在client端通過該messenge發(fā)送消息給server一個(gè)client端的messenger,用來接收client端的messenger勒葱,用該messenger發(fā)送消息回client
代碼如下:
static final int MSG_CLIENT_TO_SERVER=1;
static final int MSG_SERVER_TO_CLIENT=2;
private Messenger clientMessenger;
private Messenger serverMessenger = new Messenger(new ServerHandler());
class ServerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
Log.d("Service", "ThreadName:"+Thread.currentThread().getName());
switch (msg.what) {
case MSG_CLIENT_TO_SERVER:
Log.d("Service", "Get message from client to server!");
//msg.replyTo用來攜帶一個(gè)Messenger浪汪,此處接收到消息是客戶端傳來的,所以攜帶的是client Messenger凛虽,這樣就獲取到了客戶端的messenger死遭,然后可以通過此messenger發(fā)送msg給客戶端
//注意,客戶端發(fā)送消息時(shí)凯旋,必須將自己的messenger賦值給replyTo
clientMessenger = msg.replyTo;
Message toClientMsg=Message.obtain(null, MSG_SERVER_TO_CLIENT);
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
clientMessenger.send(toClientMsg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
default:
break;
}
}
這段代碼的意思是呀潭,接收到客戶端發(fā)送的消息時(shí),打印log至非,然后獲取到client的messenger钠署,等待2秒之后,發(fā)送消息回client荒椭。
2:client端谐鼎,通用也要定義兩個(gè)messenger。
private Messenger serverMessenger;
private Messenger clientMessenger=new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if(msg.what==MessagerService.MSG_SERVER_TO_CLIENT)
Log.d("Service", "Receive message from server");
}
});
然后在connection中獲取到Service的messenger:
private ServiceConnection messengerConnection=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
serverMessenger=new Messenger(service);
}
};
定義了一個(gè)Button趣惠,點(diǎn)擊的時(shí)候狸棍,會(huì)發(fā)送消息給Service:
Message msg=Message.obtain(null, MessagerService.MSG_CLIENT_TO_SERVER);
msg.replyTo=clientMessenger;
try {
serverMessenger.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
這樣,一個(gè)簡單的messenger通信就完成了味悄。
當(dāng)然也不要忘記 注冊Service草戈,定義成remote模式讓Service運(yùn)行在獨(dú)立進(jìn)程中,還要將Service與Activity綁定起來侍瑟。
IntentService
IntentService是系統(tǒng)提供給的一個(gè)已經(jīng)繼承自Service類的特殊類唐片,用戶只需要覆寫onHandlerIntent()方法,該方法會(huì)將耗時(shí)任務(wù)自動(dòng)運(yùn)行在子線程當(dāng)中,運(yùn)行完畢后牵触,系統(tǒng)會(huì)調(diào)用stopself來銷毀Service淮悼。
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
Log.w("IntentService", "Start download in onHandleIntent......");
Log.w("IntentService", "thread name:" + Thread.currentThread().getName());
}
}
前臺Service
Service是默認(rèn)運(yùn)行在后臺的,優(yōu)先級相對比較低揽思,容易被系統(tǒng)回收掉袜腥。想讓Service一直處于運(yùn)行狀態(tài)的話,前臺服務(wù)會(huì)在通知欄顯示一條消息钉汗,一般不會(huì)被系統(tǒng)回收掉羹令。
主要代碼如下:
public class ForegroundService extends Service{
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
showNotification();
}
private void showNotification(){
NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(this)
.setContentTitle("前臺Service")
.setContentText("Service is running")
.setSmallIcon(R.drawable.ic_launcher);//設(shè)置通知內(nèi)容
Intent intent=new Intent(this,ServiceActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this , 0 ,intent, 0);
mBuilder.setContentIntent(pendingIntent);//設(shè)置點(diǎn)擊響應(yīng)
Notification notification=mBuilder.build();//構(gòu)建通知
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1,notification);//顯示通知
startForeground(1, notification);//啟動(dòng)服務(wù)
}
}
然后在Activity中啟動(dòng)服務(wù)就可以看見了
關(guān)于Service的其他:
1:onStartCommand(Intent intent, int flags, int startId)方法:
intent:啟動(dòng)Service時(shí)候傳遞進(jìn)來的intent
flags:通常為 0,或者START_FLAG_REDELIVERY(1), START_FLAG_RETRY(2).表示Service的啟動(dòng)方式
startid:一個(gè)唯一的整型,用于表示此次Client執(zhí)行startService(...)的請求請求標(biāo)識损痰,在多次startService(...)的情況下福侈,呈現(xiàn)0,1,2....遞增
還有一個(gè)關(guān)鍵的返回值,通常有三個(gè)值可選:
START_NOT_STICKY:當(dāng)Service因?yàn)閮?nèi)存不足而被系統(tǒng)kill后卢未,接下來未來的某個(gè)時(shí)間內(nèi)肪凛,即使系統(tǒng)內(nèi)存足夠可用,系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service辽社。除非程序中Client明確再次調(diào)用startService(...)啟動(dòng)此Service伟墙。
START_STICKY:當(dāng)Service因?yàn)閮?nèi)存不足而被系統(tǒng)kill后,接下來未來的某個(gè)時(shí)間內(nèi)滴铅,當(dāng)系統(tǒng)內(nèi)存足夠可用的情況下戳葵,系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service,一旦創(chuàng)建成功后將回調(diào)onStartCommand(...)方法汉匙,但其中的Intent將是null拱烁,pendingintent除外。
START_REDELIVER_INTENT:與START_STICKY唯一不同的是噩翠,回調(diào)onStartCommand(...)方法時(shí)戏自,其中的Intent將是非空,將是最后一次調(diào)用startService(...)中的intent绎秒。
2:Broadcast Receiver由于生命周期非常短浦妄,只要幾秒鐘,所以不能作為 Bound Service的發(fā)起者见芹。