什么是Service
先看一下google官方的介紹:
Service
是一個可以在后臺執(zhí)行長時間運行操作而不提供用戶界面的應(yīng)用組件。服務(wù)可由其他應(yīng)用組件啟動,而且即使用戶切換到其他應(yīng)用,服務(wù)仍將在后臺繼續(xù)運行。 此外,組件可以綁定到服務(wù),以與之進行交互,甚至是執(zhí)行進程間通信 (IPC)。 例如,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)、播放音樂,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互,而所有這一切均可在后臺進行
總結(jié)一下,Service是一個應(yīng)用組件,它有以下特點:
1.不需要提供用戶界面
2.可以在后臺長時間運行
默認情況下,服務(wù)運行在UI線程,執(zhí)行耗時操作需要開辟子線程,否則會引起ANR。
服務(wù)通常分為兩種形式:
1.普通服務(wù),通過startService啟動,一旦啟動,服務(wù)即可在后臺無限期運行,即使啟動服務(wù)的組件已被銷毀也不受影響。
直到stopService被調(diào)用,或者Service本身調(diào)用了stopSelf,才會停止。
2.Bound 服務(wù),當應(yīng)用組件通過調(diào)用 [bindService()](https://developer.android.google.cn/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務(wù)時,服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個客戶端-服務(wù)器接口,允許組件與服務(wù)進行交互、發(fā)送請求、獲取結(jié)果,甚至是利用進程間通信 (IPC) 跨進程執(zhí)行這些操作。 僅當與另一個應(yīng)用組件綁定時,綁定服務(wù)才會運行。 多個組件可以同時綁定到該服務(wù),但全部取消綁定后,該服務(wù)即會被銷毀。
實際上,普通Service也可以與組件綁定,關(guān)鍵在于是否實現(xiàn)了[onStartCommand()](https://developer.android.google.cn/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int))(允許組件啟動服務(wù))和 onBind()(允許綁定服務(wù))這兩個回調(diào)方法。這樣的服務(wù)需要解除綁定并stop才會銷毀。
關(guān)鍵方法和生命周期
onCreate()
首次創(chuàng)建服務(wù)時,系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序(在調(diào)用 onStartCommand() 或 onBind() 之前)。如果服務(wù)已在運行,則不會調(diào)用此方法。
onStartCommand()
當另一個組件(如 Activity)通過調(diào)用 startService() 請求啟動服務(wù)時,系統(tǒng)將調(diào)用此方法。一旦執(zhí)行此方法,服務(wù)即會啟動并可在后臺無限期運行。 如果您實現(xiàn)此方法,則在服務(wù)工作完成后,需要由您通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)。(如果您只想提供綁定,則無需實現(xiàn)此方法。)
onBind()
當另一個組件想通過調(diào)用 bindService() 與服務(wù)綁定時,系統(tǒng)將調(diào)用此方法。在此方法的實現(xiàn)中,必須通過返回 IBinder 提供一個接口,供客戶端用來與服務(wù)進行通信。請務(wù)必實現(xiàn)此方法,但如果您并不希望允許綁定,則應(yīng)返回 null。
onUnbind()
當組件通過unbindService與Service解綁時,系統(tǒng)會調(diào)用此方法。
onDestroy()
當服務(wù)不再使用且將被銷毀時,系統(tǒng)將調(diào)用此方法。服務(wù)應(yīng)該實現(xiàn)此方法來清理所有資源,如線程、注冊的偵聽器、接收器等。 這是服務(wù)接收的最后一個調(diào)用
如果組件通過調(diào)用 startService() 啟動服務(wù)(這會導(dǎo)致對 onStartCommand() 的調(diào)用),則服務(wù)將一直運行,直到服務(wù)使用 stopSelf() 自行停止運行,或由其他組件通過調(diào)用 stopService() 停止它為止。
如果組件是通過調(diào)用 bindService() 來創(chuàng)建服務(wù)(且未調(diào)用 onStartCommand(),則服務(wù)只會在該組件與其綁定時運行。一旦該服務(wù)與所有客戶端之間的綁定全部取消,系統(tǒng)便會銷毀它。
也就是說,startService與stopService,bindService與unbindService是對應(yīng)關(guān)系,startService必須通過stopService來停止,這時候調(diào)用的是onStartCommand()和onDestroy();bindService必須通過unbindService來停止,這時候調(diào)用的是onBind()和onDestroy()。
到這里,Service的生命周期已經(jīng)很清楚了:
onCreate()→onStartCommand()/onBind()(→onUnbind())→onDestroy()
注:系統(tǒng)因內(nèi)存過低等原因,回收掉服務(wù)的時候,onDestroy是不會執(zhí)行的。 如果服務(wù)已經(jīng)運行,調(diào)用startService時,不會重新執(zhí)行onCreate,只會執(zhí)行onStartCommand();
Service實例:
1.普通Service:
首先定義一個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中啟動/停止該服務(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;
}
}
}
當然,也不要忘記在manifest文件中注冊,四大組件都是需要注冊的。
這樣,一個簡單的服務(wù)就完成了。
2.Bound服務(wù)
Bound服務(wù)是將啟動Service的組件(通常是Activity)與Service綁定起來,這樣Activity和Service可以非常簡單的進行通信。實例如下:
1.定義一個Binder類,并在onbind方法中返回它的實例:
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中啟動時,使用bindService方法。
該方法有三個參數(shù):
Intent,表示要啟動的Service;
connection:實現(xiàn)ServiceConnection接口的類,該接口中有兩個方法:onServiceDisconnected和onServiceConnected,onbind方法返回的Binder會傳入onServiceConnected,從而對服務(wù)進行操作;
Flag:通常選用BIND_AUTO_CREATE,bindService時會自動創(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時候調(diào)用的,正常情況下該函數(shù)不會被調(diào)用,只有意外斷開連接的時候,比如Service被系統(tǒng)回收等,才會調(diào)用。
同一個Service可以被許多組件同時綁定,返回的也是相同的Binder對象。只有當所有組件都解綁時,Service才會銷毀。
3.遠程Service:
遠程服務(wù)是指,單獨運行在一個進程中的服務(wù)。使用遠程Service也很簡單,只要設(shè)置manifest文件里Service的屬性:android:process=":remote"(意思是在當前應(yīng)用下新建一個進程,名字是包名+remote)
使用遠程Service不會產(chǎn)生ANR問題,它獨立運行在新進程中,會產(chǎn)生一個問題,如何與Activity進行通信?可以試一下,遠程Service是不能與Activity綁定的,bindService不能用在遠程Service上,所以這里就涉及到IPC的概念了。
介紹幾種比較常用的方法:
AIDL通信
首先新建一個AIDL文件,在文件中定義好Activity與Service通信的方法:
在名為ServiceAIDL.aidl的文件中,定義以下方法:
package com.training.service;
interface ServiceAIDL {
int startDownload();
}
保存之后在gen文件夾下會生成對應(yīng)的.java文件。
然后在Service中實現(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是一個實現(xiàn)了AIDL文件中定義的接口ServiceAIDL的類。它繼承自Binder類,Service中取得它的實例之后,在onBind方法中返回,就可以把該實例傳遞到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);
這樣,一個簡單的AIDL通信就完成了。
遠程Service是可以垮應(yīng)用共享的,可以通過隱式Intent從任何Activity啟動并操作他。
Messager通信
1:Service中定義兩個Messenger:
一個server端的messenger,該messenger會通過onbind方法傳遞給client端,在client端通過該messenge發(fā)送消息給server一個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用來攜帶一個Messenger,此處接收到消息是客戶端傳來的,所以攜帶的是client Messenger,這樣就獲取到了客戶端的messenger,然后可以通過此messenger發(fā)送msg給客戶端
//注意,客戶端發(fā)送消息時,必須將自己的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ā)送的消息時,打印log,然后獲取到client的messenger,等待2秒之后,發(fā)送消息回client。
2:client端,通用也要定義兩個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);
}
};
定義了一個Button,點擊的時候,會發(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();
}
這樣,一個簡單的messenger通信就完成了。
當然也不要忘記 注冊Service,定義成remote模式讓Service運行在獨立進程中,還要將Service與Activity綁定起來。
IntentService
IntentService是系統(tǒng)提供給的一個已經(jīng)繼承自Service類的特殊類,用戶只需要覆寫onHandlerIntent()方法,該方法會將耗時任務(wù)自動運行在子線程當中,運行完畢后,系統(tǒng)會調(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是默認運行在后臺的,優(yōu)先級相對比較低,容易被系統(tǒng)回收掉。想讓Service一直處于運行狀態(tài)的話,前臺服務(wù)會在通知欄顯示一條消息,一般不會被系統(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è)置點擊響應(yīng)
Notification notification=mBuilder.build();//構(gòu)建通知
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1,notification);//顯示通知
startForeground(1, notification);//啟動服務(wù)
}
}
然后在Activity中啟動服務(wù)就可以看見了
關(guān)于Service的其他:
1:onStartCommand(Intent intent, int flags, int startId)方法:
intent:啟動Service時候傳遞進來的intent
flags:通常為 0,或者START_FLAG_REDELIVERY(1), START_FLAG_RETRY(2).表示Service的啟動方式
startid:一個唯一的整型,用于表示此次Client執(zhí)行startService(...)的請求請求標識,在多次startService(...)的情況下,呈現(xiàn)0,1,2....遞增
還有一個關(guān)鍵的返回值,通常有三個值可選:
START_NOT_STICKY:當Service因為內(nèi)存不足而被系統(tǒng)kill后,接下來未來的某個時間內(nèi),即使系統(tǒng)內(nèi)存足夠可用,系統(tǒng)也不會嘗試重新創(chuàng)建此Service。除非程序中Client明確再次調(diào)用startService(...)啟動此Service。
START_STICKY:當Service因為內(nèi)存不足而被系統(tǒng)kill后,接下來未來的某個時間內(nèi),當系統(tǒng)內(nèi)存足夠可用的情況下,系統(tǒng)將會嘗試重新創(chuàng)建此Service,一旦創(chuàng)建成功后將回調(diào)onStartCommand(...)方法,但其中的Intent將是null,pendingintent除外。
START_REDELIVER_INTENT:與START_STICKY唯一不同的是,回調(diào)onStartCommand(...)方法時,其中的Intent將是非空,將是最后一次調(diào)用startService(...)中的intent。
2:Broadcast Receiver由于生命周期非常短,只要幾秒鐘,所以不能作為 Bound Service的發(fā)起者。