Service基礎(chǔ)使用
之前的文章一直介紹Activity的使用,很多知識和用法單一的配合Activity使用,這次將總結(jié)Android四大組件之二——Service.
本文將要介紹以下內(nèi)容:
- Service是什么
- 兩種Service啟動
- Service 前臺服務(wù)與Notification
- 后臺定時(shí)服務(wù)
- IntentService
Service是什么
Service 是一個(gè)可以在后臺執(zhí)行長時(shí)間運(yùn)行操作而不使用用戶界面的應(yīng)用組件。服務(wù)可由其他應(yī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)行。
上面說了這么多可以做的耗時(shí)操作,但不要真的認(rèn)為Service默認(rèn)會運(yùn)行在子線程中,他也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中,他同樣執(zhí)行在UI線程中,因此,不要在Service中執(zhí)行耗時(shí)操作,除非你在Service中創(chuàng)建了子線程來完成耗時(shí)操作。
Service不因程序切換到后臺或者用戶切換到另一個(gè)APP而停止,但如果應(yīng)用程序被殺死,他當(dāng)然就也消失了。
兩種Service的啟動
服務(wù)啟動分為兩種形式,一種是通過StartService()啟動Service,另一種是通過BindService(),至于先啟動在綁定這個(gè)我們最后再說。本文主要介紹的就是這兩種,這兩種有什么區(qū)別呢?我們先看一張他們的生命周期圖片,然后在慢慢細(xì)說。

StartService()
當(dāng)應(yīng)用組件(如 Activity)通過調(diào)用 startService() 啟動服務(wù)時(shí),服務(wù)即處于“啟動”狀態(tài)。一旦啟動,服務(wù)即可在后臺無限期運(yùn)行,即使啟動服務(wù)的組件已被銷毀也不受影響。 已啟動的服務(wù)通常是執(zhí)行單一操作,而且不會將結(jié)果返回給調(diào)用方。例如,它可能通過網(wǎng)絡(luò)下載或上傳文件。 操作完成后,服務(wù)會自行停止運(yùn)行。
通過上面的生命周期圖也可以看出來首次啟動會創(chuàng)建一個(gè)Service的實(shí)例,然后依次調(diào)用onCreate()和onStartCommand(),此時(shí)Service進(jìn)入運(yùn)行狀態(tài),即使你多次調(diào)用StartService()啟動Service,也不會創(chuàng)建新的Service對象,而是直接復(fù)用前面創(chuàng)建的Service對象,調(diào)用它的startCommand()方法。下面介紹一下startService各個(gè)回調(diào)方法,我們該做什么
onCreate():首次創(chuàng)建服務(wù),系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序,如果服務(wù)已經(jīng)運(yùn)行,則不會調(diào)用此方法
onStartCommand():啟動服務(wù)的時(shí)候,系統(tǒng)將會調(diào)用此方法。如果實(shí)現(xiàn)了此方法,則在服務(wù)工作完成后,需要主動調(diào)用stopSelf()或者stopService()來停止服務(wù)。
onDestroy(),當(dāng)服務(wù)不再使用且將被銷毀時(shí),系統(tǒng)將會調(diào)用此方法,服務(wù)應(yīng)該實(shí)現(xiàn)此方法來清理所有資源。這是服務(wù)接受的最后一個(gè)調(diào)用。
下面我將通過一個(gè)實(shí)例展示這個(gè)service的實(shí)現(xiàn),以及方法調(diào)用的順序。注意,通過AS新建一個(gè)Service則不需要配置mainfest文件,否則需要加入service聲明,大概如下面這樣。且注意始終通過顯示Intent啟動或者綁定Service,不要為他設(shè)置intent-filter
<application ... >
<service android:name=".ExampleService" />
</application>
Service代碼如下:
public class TestService extends Service {
public TestService() {
}
@Override
public void onCreate() {
super.onCreate();
//處理一次性設(shè)置
Log.i("TestService","onCreate被調(diào)用");
}
//必須要有這個(gè)方法
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
Log.i("TestService","onDestroy被調(diào)用");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//處理Service邏輯
Log.i("TestService","onStartCommand被調(diào)用");
return super.onStartCommand(intent, flags, startId);
}
}
Activity中布局文件放置了一個(gè)啟動按鈕,一個(gè)停止按鈕,在onCreate()方法,實(shí)現(xiàn)startService,代碼如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stop = (Button)findViewById(R.id.stop);
start = (Button) findViewById(R.id.start);
final Intent intent = new Intent(this, TestService.class);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(intent);
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(intent);
}
});
}
調(diào)用的結(jié)果如下:
點(diǎn)擊啟動service按鈕后
I/TestService: onCreate被調(diào)用
I/TestService: onStartCommand被調(diào)用
點(diǎn)擊停止service按鈕后
I/TestService: onDestroy被調(diào)用
bindService()
當(dāng)應(yīng)用組件通過調(diào)用 bindService() 綁定到服務(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ù)才會運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù),但全部取消綁定后,該服務(wù)即會被銷毀。
和startService()一樣,首次使用bindService時(shí)綁定一個(gè)Service時(shí),系統(tǒng)會實(shí)例化一個(gè)Service實(shí)例,并調(diào)用onCreate()和onBind()方法,此后如果再次使用bindService綁定Service,系統(tǒng)不會創(chuàng)建新的Service實(shí)例,也不會再調(diào)用onBInd()方法,只是會直接把IBinder對象傳遞給其他后來增加的客戶端。 如果我們解除與服務(wù)的綁定,只需調(diào)用unbindService(),此時(shí)onUnbind和onDestory方法將會被調(diào)用!這是一個(gè)客戶端的情況,假如是多個(gè)客戶端綁定同一個(gè)Service的話,情況如下 當(dāng)一個(gè)客戶完成和service之間的互動后,它調(diào)用 unbindService() 方法來解除綁定。當(dāng)所有的客戶端都和service解除綁定后,系統(tǒng)會銷毀service。(除非service也被startService()方法開啟)。但一旦調(diào)用者銷毀,Service也立即終止。下面主要說一下onBInd這個(gè)回調(diào)方法。
第一種啟動方式并沒有直接提供Service與Activity的交互方式。第二種啟動方式即用bindService提供了Service與Activity交互方式,可以很方便的利用Ibinder實(shí)現(xiàn)Service與Activity的通信。關(guān)鍵在onBind方法
onBind()方法返回值是一個(gè)IBinder 對象,這個(gè)方法必須實(shí)現(xiàn),通過startService返回為null,表示不調(diào)用,bindService則返回一個(gè)擴(kuò)展Binder類的對象。
步驟如下:
- 定義一個(gè)繼承Binder類的類,在類里面編寫想要Activity調(diào)用的方法.
- 生成一個(gè)該類的對象,放到onBInd()方法中返回。
代碼例子如下:
public class TestService2 extends Service {
public TestService2() {
}
public Mybinder mybinder = new Mybinder();
public int getInfo() {
return 123;
}
public class Mybinder extends Binder {
public TestService2 getService() {
return TestService2.this;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.i("TS","onbind被調(diào)用");
return mybinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("TS","onDestory被調(diào)用");
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.i("TS","onRebind被調(diào)用");
}
}
這個(gè)例子Mybinder對象中g(shù)etService方法,得到這個(gè)Service對象,從而調(diào)用Service中所有方法。
在Activity中的代碼,要實(shí)現(xiàn)ServiceConnection類中的onServiceDisconnected和onServiceConnected方法,并且通過bindService來綁定服務(wù),unbindService來解綁服務(wù)。
主要代碼如下:
TestService2.Mybinder binder;
private ServiceConnection conn = new ServiceConnection() {
//Activity與Service斷開連接時(shí)回調(diào)該方法
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("------Service DisConnected-------");
}
//Activity與Service連接成功時(shí)回調(diào)該方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//將Service強(qiáng)制轉(zhuǎn)換類型為Binder,這種用法在回調(diào)中很常見。
System.out.println("------Service Connected-------");
binder = (TestService2.Mybinder) service;
}
};
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
}
});
//通過binder對象就可以對我們綁定的Service進(jìn)行各種操作
status.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,binder.getService().getInfo() + "",Toast.LENGTH_SHORT).show();
}
});
}
至于調(diào)用順序,還是和流程圖一樣,就不一一詳說了。這里已經(jīng)說明了2種啟動Service的方式和區(qū)別。下面將介紹一個(gè)很流行的Service應(yīng)用,前臺服務(wù)。
Service 前臺服務(wù)與Notification
我們在用很多應(yīng)用的時(shí)候,發(fā)現(xiàn)他們啟動的時(shí)候,會在通知欄生成一個(gè)和該App的通知,來繼續(xù)執(zhí)行Service,比如墨跡天氣,很多音樂App.這種叫前臺服務(wù),其實(shí)這種Service有一個(gè)很好的一點(diǎn),就是不會因?yàn)镾ervice自身的優(yōu)先級低,而被系統(tǒng)KILL,而前臺服務(wù)就不會。
前臺服務(wù)的寫法很容易,只需要在onCreate()中,建立一個(gè)通知,然后用startForeground()設(shè)置為前臺服務(wù)即可。
下面直接放出代碼,結(jié)合代碼注釋看看就好了,關(guān)于通知更多的內(nèi)容可以看看Notification詳解
這里只列出Service的onCreate()部分代碼
@Override
public void onCreate() {
super.onCreate();
//設(shè)定一個(gè)PendingIntent,來表示點(diǎn)擊通知欄時(shí)跳轉(zhuǎn)到哪里
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
Notification.Builder builder = new Notification.Builder(this);
//建立一個(gè)notificationManager來管理通知的出現(xiàn)
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//構(gòu)造通知的樣式,包括圖片,標(biāo)題,內(nèi)容,時(shí)間。
builder.setSmallIcon(R.mipmap.ic_launcher).
setWhen(System.currentTimeMillis()).
setContentTitle("我是標(biāo)題").
setContentText("我是內(nèi)容").
setTicker("在啟動時(shí)彈出一個(gè)消息").//這個(gè)Android5.0以上可能會失效
setWhen(System.currentTimeMillis()).
setContentIntent(contentIntent);
//最后通過build建立好通知
Notification notification = builder.build();
//通過manager來顯示通知,這個(gè)1為notification的id
notificationManager.notify(1,notification);
//啟動為前臺服務(wù),這個(gè)1為notification的id
startForeground(1,notification);
}
后臺定時(shí)服務(wù)
后臺定時(shí)服務(wù)其實(shí)并不是特殊的Service,只是Service的常見的一種應(yīng)用,放到后臺做定時(shí)更新,輪詢等。這次的Service要配合Alarm以及簡單的廣播機(jī)制來實(shí)現(xiàn)。
步驟主要如下:
- 獲得Service
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); - 通過set方法設(shè)置定時(shí)任務(wù)
int time = 1000;
long triggerAtTime = SystemClock.elapsedRealtime() + time;
Intent i = new Intent(this,AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
- 定義一個(gè)Service,在onStartCommand中開辟一個(gè)事務(wù)線程,用于處理一些定時(shí)邏輯
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//執(zhí)行想做的操作,比如輸出時(shí)間
}
}).start();
//步驟二里面的代碼
return super.onStartCommand(intent, flags, startId);
}
- 定義一個(gè)Broadcast,用于啟動Service。
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context,LongRunningService.class);
context.startService(i);
}
}
這樣我們就能執(zhí)行后臺定時(shí)服務(wù)
IntentService
這是
Service的子類,它使用工作線程逐一處理所有啟動請求。如果您不要求服務(wù)同時(shí)處理多個(gè)請求,這是最好的選擇。 您只需實(shí)現(xiàn)onHandleIntent()方法即可,該方法會接收每個(gè)啟動請求的 Intent,使您能夠執(zhí)行后臺工作。
由于大多數(shù)啟動服務(wù)都不必須同時(shí)處理多個(gè)請求,因此使用IntentService類實(shí)現(xiàn)服務(wù)也許是最好的選擇。
IntentService執(zhí)行以下操作:
- 創(chuàng)建默認(rèn)的工作線程,用于在應(yīng)用的主線程外執(zhí)行傳遞給
onStartCommand()的所有Intent - 創(chuàng)建工作隊(duì)列,用于講一個(gè)Intent逐一傳遞給
onHandleIntent()實(shí)現(xiàn),不要擔(dān)心多線程問題 - 在處理完所有啟動后停止服務(wù),永遠(yuǎn)不用調(diào)用
stopSelf() - 提供onBind()的默認(rèn)實(shí)現(xiàn)
- 提供
onstartCommand()的默認(rèn)實(shí)現(xiàn),可將Intent依次發(fā)送給工作隊(duì)列和onHandleIntent()
下面給一個(gè)IntentService的例子
public class MyIntentService extends IntentService {
private final String TAG = MyIntentService.class.getName();
public MyIntentService() {
//此構(gòu)造函數(shù)一定要實(shí)現(xiàn) super(MyIntentService.class.getName());
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getExtras().getString("param");
if("s1".equals(action)) Log.i(TAG, "啟動s1");
if("s2".equals(action)) Log.i(TAG, "啟動s2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
Log.i(TAG,"onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void setIntentRedelivery(boolean enabled) {
super.setIntentRedelivery(enabled);
Log.i(TAG,"setIntentRedelivery");
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
}
在Activity中的onCreate()方法中加入如下代碼
Intent it1 = new Intent(this,MyIntentService.class);
Bundle b1 = new Bundle();
b1.putString("param", "s1");
it1.putExtras(b1);
Intent it2 = new Intent(this,MyIntentService.class);
Bundle b2 = new Bundle();
b2.putString("param", "s2");
it2.putExtras(b2);
startService(it1);
startService(it2);
運(yùn)行結(jié)果如下
I/com.shiqifeng.servicetest.MyIntentService: onCreate
I/com.shiqifeng.servicetest.MyIntentService: onStartCommand
I/com.shiqifeng.servicetest.MyIntentService: 啟動s1
I/com.shiqifeng.servicetest.MyIntentService: onStartCommand
I/com.shiqifeng.servicetest.MyIntentService: 啟動s2
I/com.shiqifeng.servicetest.MyIntentService: onDestroy
至此Service的基本使用全部結(jié)束