四大組件中奈附,我們最熟悉的而且最常用的應(yīng)該是Activity了,它的主要作用是提供用戶交互的界面煮剧,那么如果有一些任務(wù)只需要在后臺執(zhí)行而不需要界面斥滤,那應(yīng)該怎么辦呢。今天總結(jié)歸納的是Service勉盅。
目錄
一佑颇、Service概述
二、Service的基本用法
1.啟動Service
2.綁定Service
三草娜、Service生命周期
四挑胸、Service的幾點說明
五、IntentService使用
六宰闰、相關(guān)參考
一茬贵、概述
首先我們看下官方對Service的介紹:
Service是一個可以在后臺執(zhí)行需要長時間運行的操作的應(yīng)用組件,它沒有用戶界面移袍。其它組件可以啟動一個Service解藻,之后即使用戶切換到別的應(yīng)用里,這個Service也將繼續(xù)在后臺運行葡盗。此外舆逃,一個組件可以與一個Service綁定來與之交互,甚至可以執(zhí)行IPC進程間通信。服務(wù)可以在后臺執(zhí)行很多任務(wù)路狮,比如處理網(wǎng)絡(luò)事務(wù)虫啥,播放音樂,文件讀寫或者與一個內(nèi)容提供者交互奄妨,等等涂籽。
Service主要有兩種形式:啟動狀態(tài)和綁定狀態(tài)。
啟動狀態(tài)(Started)
當一個應(yīng)用組件比如activity通過調(diào)用startService()來啟動一個服務(wù)的時候砸抛,服務(wù)便處于啟動狀態(tài)评雌。一旦啟動,服務(wù)可以在后臺無限期地運行下去直焙,即使當啟動它的組件已經(jīng)銷毀景东。通常情況下,一個啟動的service執(zhí)行一個單一的操作并且不會返回任何結(jié)果給調(diào)用者奔誓。例如斤吐,服務(wù)可能通過網(wǎng)絡(luò)下載或者上傳一個文件,當操作完成厨喂,服務(wù)會自己停止和措。
綁定狀態(tài)(Bound)
當一個應(yīng)用組件通過調(diào)用bindService()來與一個服務(wù)綁定時,服務(wù)便處于綁定狀態(tài)蜕煌。一個綁定的服務(wù)提供了一個客戶端-服務(wù)器端接口來允許組件與服務(wù)進行交互派阱,發(fā)送請求,得到結(jié)果甚至通過IPC進程間通信來完成操作斜纪。只有當其它組件與服務(wù)綁定時贫母,服務(wù)才會處于綁定狀態(tài)。多個組件可以同時與服務(wù)綁定盒刚,但是當他們?nèi)慷冀獬壎〞r颁独,服務(wù)就會銷毀。
二伪冰、Service的基本用法
對于Activity的使用誓酒,我們應(yīng)該都比較熟悉了,一般是先新建一個繼承Activity的自己的Activity贮聂,然后在里面寫自己的邏輯靠柑,如果要啟動一個Activity的話可以通過調(diào)用StartActivity()傳入一個Intent對象來完成,在清單文件里進行Activity注冊也是必不可少的一步吓懈。與之類似歼冰,Service基本用法也很簡單,下面簡單介紹啟動Service的步驟耻警,之后會有詳細的介紹隔嫡。
啟動Service
- 新建Service
public class TestService extends Service {
private final String TAG = "TestService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "---onCreate()---");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "---onStartCommand()---");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "---onDestroy()---");
}
}
-
注冊Service
3.啟動Service
從上面這張圖可以看到甸怕,新建一個Intent對象并把它傳給startService,這樣就可以啟動一個Service了腮恩,啟動Service后如果想要停止它梢杭,可以使用stopService方法。
綁定Service
下面是組件與Service綁定的步驟秸滴。
- 首先我們修改一些之前的Service.
public class TestService extends Service {
private final String TAG = "TestService";
//實例化"粘合劑" 可返回給與Service綁定的組件
private MyBinder mMyBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "---onCreate()---");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "---onStartCommand()---");
return super.onStartCommand(intent, flags, startId);
}
//綁定后調(diào)用此方法
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mMyBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "---onDestroy()---");
}
//這里執(zhí)行任務(wù)
public class MyBinder extends Binder {
public void startTask() {
Log.i(TAG, "Task starts...");
}
}
}
2.假設(shè)我們要在一個Activity里通過點擊一個按鈕來綁定Service武契,那么我們要先在Activity里新建一個ServiceConnection的實例。下面代碼是在Activity里抽取出來的荡含。
private TestService.MyBinder mMyBinder;
/**
* 服務(wù)連接
*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* 當服務(wù)連接后 返回binder
* @param name
* @param binder
*/
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mMyBinder = (TestService.MyBinder) binder;
mMyBinder.startTask();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
3.這樣我們在Activity里點擊一個按鈕來綁定Service的時候咒唆,可以調(diào)用BindService來完成,詳細如下圖释液。
啟動后的Service可以用stopService來停止服務(wù)全释,綁定后的Service可以用unbindService來取消綁定。
Service用法小結(jié)
上面的基本用法其實展示了Service最基本的用法误债,包括啟動Service浸船,停止Service,綁定Service以及取消綁定Service找前,Service里的邏輯很簡單,實際上在平時的開發(fā)過程中判族,在后臺執(zhí)行時應(yīng)該會比較復(fù)雜躺盛,考慮的東西也比較多。
啟動Service的邏輯其實比較簡單形帮,就是新建一個Intent對象槽惫,然后調(diào)用startService方法把對象傳進去,之后就可以啟動了辩撑,停止的時候也是傳一個Intent對象給stopService方法界斜。
綁定Service的話邏輯較為復(fù)雜,這里我們是將Activity與Service建立綁定合冀。我們可以看到幾點注意事項:
(1) 首先是在Service里各薇,我們定義了一個繼承Binder的子類,用來執(zhí)行任務(wù)君躺,Binder我覺得可以理解為"粘合劑"峭判,我們可以理解為它是其它組件和Service的之間的結(jié)合物,之后實例化這個Binder并在回調(diào)方法里返回這個Binder給綁定Service的組件棕叫,就可以通過它讓組件控制Service了林螃。
(2) 然后是在Activity里實例化了一個ServiceConnection,并重寫了onServiceConnected方法俺泣,這個方法會在建立連接后返回Service的Binder疗认,然后通過Binder執(zhí)行Service里的任務(wù)完残。
(3) 綁定Service的時候,調(diào)用bindService方法横漏,這里傳入三個參數(shù)谨设,一個是Intent對象,說明要與哪個Service綁定绊茧,一個是剛才的ServiceConnection實例铝宵,第三個參數(shù)是常量,我們這里傳入BIND_AUTO_CREATE华畏,表示綁定時是否自動創(chuàng)建Service鹏秋。銷毀綁定的Service的時候只需要在使用unbindService方法時傳入?yún)?shù)ServiceConnection實例即可。
如果既點擊了Start Service按鈕亡笑,又點擊了Bind Service按鈕侣夷,這樣的話不管是單獨點擊Stop Service按鈕還是Unbind Service按鈕,Service都不會被銷毀仑乌,必要將兩個按鈕都點擊一下百拓,Service才會被銷毀。也就是說晰甚,點擊Stop Service按鈕只會讓Service停止衙传,點擊Unbind Service按鈕只會讓Service和Activity解除關(guān)聯(lián),一個Service必須要在既沒有和任何Activity關(guān)聯(lián)又處理停止狀態(tài)的時候才會被銷毀厕九。
三蓖捶、Service生命周期
Service的生命周期比Activity的簡單很多,但是更加要引起重視扁远,畢竟Service可能是在用戶不知情的情況下在后臺運行的俊鱼。下面這張圖很清楚地為我們展示了兩種不同的方式,Service的生命周期畅买。
看這張圖并闲,我們可以初步對Service之前的幾個回調(diào)方法有所了解,啟動和綁定Service都會調(diào)用onCreate()方法來創(chuàng)建Service谷羞,如果是啟動的話會調(diào)用onStartCommand()方法帝火,如果是綁定的話會調(diào)用onBind()方法,當啟動的Service停止后會調(diào)用onDestroy()方法銷毀湃缎,綁定的Service在全部都解除綁定后調(diào)用onUnbind()方法购公,最后調(diào)用onDestroy()方法銷毀。
四雁歌、Service的幾點說明
A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.Service不是一個單獨的進程宏浩,Service與它所在應(yīng)用位于同一進程中。
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).Service不是一個線程靠瞎,所以為了避免ANR錯誤(用戶無響應(yīng))比庄,我們應(yīng)該在Service里開啟新的線程來處理耗時任務(wù)求妹。
我們可以看到Service只是一個在后臺運行的沒有用戶界面的組件,使用它做些耗時的任務(wù)時佳窑,是需要開啟新線程的制恍。這里如果有需要的話,可以詳細去了解下IntentService這個類神凑,它可以很方便地處理一些多線程問題净神。
五、IntentService
Service這個組件默認情況下仍然運行在應(yīng)用的主線程中溉委,所以如果我們要執(zhí)行耗時或者阻塞操作鹃唯,那么為了避免ANR,應(yīng)該要在Service內(nèi)創(chuàng)建一個新的線程瓣喊。IntentService是Service類的一個子類坡慌,用來處理異步請求。
下面是一個IntentService使用示例:
//實現(xiàn)倒計時5秒的服務(wù)
public class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public MyIntentService() {
super("count down 5");
}
@Override
public void onCreate() {
super.onCreate();
Log.e("ssssss", "倒計時服務(wù)創(chuàng)建...");
}
@Override
protected void onHandleIntent(Intent intent) {
long endTime = System.currentTimeMillis() + 5 * 1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
} finally {
Log.e("ssssss", "倒計時結(jié)束...");
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("ssssss", "倒計時服務(wù)銷毀...");
}
}
與普通Service一樣藻三,我們可以通過startService(Intent)方法傳遞請求給IntentService洪橘。通過查看源碼可知,IntentService在onCreate()函數(shù)中通過HandlerThread單獨開啟一個線程來處理所有Intent請求對象所對應(yīng)的任務(wù)棵帽,這樣以免事務(wù)處理阻塞主線程熄求。執(zhí)行完所一個Intent請求對象所對應(yīng)的工作之后,如果沒有新的Intent請求達到逗概,則自動停止Service弟晚;否則執(zhí)行下一個Intent請求所對應(yīng)的任務(wù)。
IntentService詳解
官方建議我們使用IntentService來完成異步請求的處理仗谆,那么IntentService內(nèi)部具體都做了哪些東西呢指巡?大概如下:
創(chuàng)建一個獨立于應(yīng)用主線程的新的線程來執(zhí)行所有傳遞給onStartCommand()方法中的請求淑履。
創(chuàng)建一個工作隊列隶垮,一次傳遞一個請求到onHandleIntent()方法中。
當所有請求處理完成后秘噪,停止服務(wù)狸吞,不需要我們手動調(diào)用stopSelf()。
提供onBind()方法的默認實現(xiàn)指煎,并且返回空蹋偏。
幫我們實現(xiàn)了將請求從onStartCommand()傳遞到工作隊列,然后傳遞到onHandleIntent()至壤。
為了對比地看出Service和IntentService在使用上的區(qū)別威始,下面展示一個使用Service的例子:
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
六、相關(guān)參考
Service基本使用
Android Service完全解析像街,關(guān)于服務(wù)你所需知道的一切(上)Service高級用法
Android Service完全解析黎棠,關(guān)于服務(wù)你所需知道的一切(下)