Service是在一段不定的時間運行在後臺,不和用戶交互應用組件。每一個Service必須在manifest中 經過<service>來聲明。能夠經過contect.startservice和contect.bindserverice來啓動。
Service和其餘的應用組件同樣,運行在進程的主線程中。這就是說若是service須要不少耗時或者阻塞的操做,須要在其子線程中實現。
service的兩種模式(startService()/bindService()不是徹底分離的):
- 本地服務 Local Service 用於應用程序內部。
它能夠啓動並運行,直至有人中止了它或它本身中止。在這種方式下,它以調用Context.startService()啓動,而以調用Context.stopService()結束。它能夠調用Service.stopSelf() 或 Service.stopSelfResult()來本身中止。不論調用了多少次startService()方法,你只須要調用一次stopService()來中止服務。
用於實現應用程序本身的一些耗時任務,好比查詢升級信息,並不佔用應用程序好比Activity所屬線程,而是單開線程後臺執行,這樣用戶體驗比較好。
- 遠程服務 Remote Service 用於android系統內部的應用程序之間。
它能夠經過本身定義並暴露出來的接口進行程序操做。客戶端創建一個到服務對象的鏈接,並經過那個鏈接來調用服務。鏈接以調用Context.bindService()方法創建,以調用 Context.unbindService()關閉。多個客戶端能夠綁定至同一個服務。若是服務此時尚未加載,bindService()會先加載它。
可被其餘應用程序複用,好比天氣預報服務,其餘應用程序不須要再寫這樣的服務,調用已有的便可。
生命週期
Service的生命週期並不像Activity那麼複雜,它只繼承了onCreate(),onStart(),onDestroy()三個方法,當咱們第一次啓動Service時,前後調用了onCreate(),onStart()這兩個方法,當中止Service時,則執行onDestroy()方法,這裏須要注意的是,若是Service已經啓動了,當咱們再次啓動Service時,不會在執行onCreate()方法,而是直接執行onStart()方法。
而啓動service,根據onStartCommand的返回值不一樣,有兩個附加的模式:
1. START_STICKY 用於顯示啓動和中止service。
2. START_NOT_STICKY或START_REDELIVER_INTENT用於有命令須要處理時才運行的模式。
服務不能本身運行,須要經過調用Context.startService()或Context.bindService()方法啓動服務。這兩個方法均可以啓動Service,可是它們的使用場合有所不一樣。
1. 使用startService()方法啓用服務,調用者與服務之間沒有關連,即便調用者退出了,服務仍然運行。
若是打算採用Context.startService()方法啓動服務,在服務未被建立時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。
若是調用startService()方法前服務已經被建立,屢次調用startService()方法並不會致使屢次建立服務,但會致使屢次調用onStart()方法。
採用startService()方法啓動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
2. 使用bindService()方法啓用服務,調用者與服務綁定在了一塊兒,調用者一旦退出,服務也就終止,大有「不求同時生,必須同時死」的特色。
onBind()只有採用Context.bindService()方法啓動服務時纔會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,屢次調用Context.bindService()方法並不會致使該方法被屢次調用。
採用Context.bindService()方法啓動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。
看看官方給出的比較流程示意圖:
官方文檔告訴咱們,一個service能夠同時start而且bind,在這樣的狀況,系統會一直保持service的運行狀態若是service已經start了或者BIND_AUTO_CREATE標誌被設置。若是沒有一個條件知足,那麼系統將會調用onDestory方法來終止service.全部的清理工做(終止線程,反註冊接收器)都在onDestory中完成。
擁有service的進程具備較高的優先級
官方文檔告訴咱們,Android系統會盡可能保持擁有service的進程運行,只要在該service已經被啓動(start)或者客戶端鏈接(bindService)到它。當內存不足時,須要保持,擁有service的進程具備較高的優先級。
1. 若是service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變爲前臺進程以免被killed。
2. 若是當前service已經被啓動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,可是比那些不可見的進程更重要,這就意味着service通常不會被killed.
3. 若是客戶端已經鏈接到service (bindService),那麼擁有Service的進程則擁有最高的優先級,能夠認爲service是可見的。
4. 若是service可使用startForeground(int, Notification)方法來將service設置爲前臺狀態,那麼系統就認爲是對用戶可見的,並不會在內存不足時killed。
若是有其餘的應用組件做爲Service,Activity等運行在相同的進程中,那麼將會增長該進程的重要性。
本地service
1.不需和Activity交互的本地服務
public
class LocalService
extends Service {
private
static
final String TAG =
"LocalService";
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,
"onBind");
return
null;
}
@Override
public
void onCreate() {
Log.i(TAG,
"onCreate");
super.onCreate();
}
@Override
public
void onDestroy() {
Log.i(TAG,
"onDestroy");
super.onDestroy();
}
@Override
public
void onStart(Intent intent,
int startId) {
Log.i(TAG,
"onStart");
super.onStart(intent, startId);
}
}
Activity:
public
class ServiceActivity
extends Activity {
@Override
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.servicedemo);
((Button) findViewById(R.id.startLocalService)).setOnClickListener(
new View.OnClickListener(){
@Override
public
void onClick(View view) {
// TODO Auto-generated method stub
startService(new Intent("com.demo.SERVICE_DEMO"));
}
});
((Button) findViewById(R.id.stopLocalService)).setOnClickListener(
new View.OnClickListener(){
@Override
public
void onClick(View view) {
// TODO Auto-generated method stub
stopService(new Intent("com.demo.SERVICE_DEMO"));
}
});
}
}
在AndroidManifest.xml添加:
<
service
android:name
=".LocalService"
>
<
intent-filter
>
<
action
android:name
="com.demo.SERVICE_DEMO"
/>
<
category
android:name
="android.intent.category.default"
/>
</
intent-filter
>
</
service
>
不然啓動服務時會提示new Intent找不到"com.demo.SERVICE_DEMO"。
對於這類不需和Activity交互的本地服務,是使用startService/stopService的最好例子。
運行時能夠發現第一次startService時,會調用onCreate和onStart,在沒有stopService前,不管點擊多少次startService,都只會調用onStart。而stopService時調用onDestroy。再次點擊stopService,會發現不會進入service的生命週期的,即不會再調用onCreate,onStart和onDestroy。
而onBind在startService/stopService中沒有調用。
2.本地服務和Activity交互
對於這種case,官方的sample(APIDemo\app.LocalService)是最好的例子:
/**
* This is an example of implementing an application service that runs locally
* in the same process as the application. The {@link LocalServiceController}
* and {@link LocalServiceBinding} classes show how to interact with the
* service.
*
* <p>Notice the use of the {@link NotificationManager} when interesting things
* happen in the service. This is generally how background services should
* interact with the user, rather than doing something more disruptive such as
* calling startActivity().
*/
public
class LocalService
extends Service {
private NotificationManager mNM;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public
void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon in the status bar.
showNotification();
}
@Override
public
int onStartCommand(Intent intent,
int flags,
int startId) {
Log.i(
"LocalService",
"Received start id " + startId +
": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
@Override
public
void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(R.string.local_service_started);
// Tell the user we stopped.
Toast.makeText(
this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
/**
* Show a notification while this service is running.
*/
private
void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
// Set the icon, scrolling text and timestamp
Notification notification =
new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(
this, 0,
new Intent(
this, LocalServiceController.
class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(
this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.local_service_started, notification);
}
}
這裏能夠發現onBind須要返回一個IBinder對象。也就是說和上一例子LocalService不一樣的是,
1. 添加了一個public內部類繼承Binder,並添加getService方法來返回當前的Service對象;
2. 新建一個IBinder對象——new那個Binder內部類;
3. onBind方法返還那個IBinder對象。
Activity:
/**
* <p>Example of binding and unbinding to the {@link LocalService}.
* This demonstrates the implementation of a service which the client will
* bind to, receiving an object through which it can communicate with the service.</p>
*/
public
class LocalServiceBinding
extends Activity {
private
boolean mIsBound;
private LocalService mBoundService;
@Override
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
Toast.makeText(LocalServiceBinding.
this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService =
null;
Toast.makeText(LocalServiceBinding.
this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
private OnClickListener mBindListener =
new OnClickListener() {
public
void onClick(View v) {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(
new Intent(LocalServiceBinding.
this,
LocalService.
class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound =
true;
}
};
private OnClickListener mUnbindListener =
new OnClickListener() {
public
void onClick(View v) {
if (mIsBound) {
// Detach our existing connection.
unbindService(
mConnection);
mIsBound =
false;
}
}
};
}
明顯看出這裏面添加了一個名爲ServiceConnection類,並實現了onServiceConnected(從IBinder獲取Service對象)和onServiceDisconnected(set Service to null)。
而bindService和unbindService方法都是操做這個ServiceConnection對象的。
AndroidManifest.xml裏添加:
<
service
android:name
=".app.LocalService"
/>
<
activity
android:name
=".app.LocalServiceBinding"
android:label
="@string/activity_local_service_binding"
>
<
intent-filter
>
<
action
android:name
="android.intent.action.MAIN"
/>
<
category
android:name
="android.intent.category.SAMPLE_CODE"
/>
</
intent-filter
>
</
activity
>
這裏沒什麼特別的,由於service沒有須要什麼特別的action,因此只是聲明service而已,而activity和普通的沒差異。
運行時,發現調用次序是這樣的:
bindService:
1.LocalService : onCreate
2.LocalService : onBind
3.Activity: onServiceConnected
unbindService: 只是調用onDestroy
可見,onStart是不會被調用的,而onServiceDisconnected沒有調用的緣由在上面代碼的註釋有說明。
介紹onStartCommand()須要用到的幾個常量
(引自官方文檔)
START_NOT_STICKY
-
If the system kills the service after onStartCommand() returns,
do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.
-
START_STICKY
-
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but
do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
-
START_REDELIVER_INTENT
-
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as do wnloading a file.
Running a Service in the Foreground
具體內容查看官方文檔,主要是使用 startForeground() 和 stopForeground()方法。