Service 學(xué)習(xí)要點
1.Service概述
Service的主要作用是讓系統(tǒng)在后臺在后臺做一些不與用戶交互的操作(例如耗時操作:下載網(wǎng)絡(luò)資源,長期運行的操作:播放音樂)
Service與Thread的區(qū)別:(1)Service不是在一個獨立的進程中,它與我們的應(yīng)用程序在同一進程(process)中 (2)Service也不是一個線程,相反,它是運行在主線程的(即UI線程),因此若我們要在Service中進行耗時操作時,需要開啟一個子線程,在其中進行耗時操作,否則很容易出現(xiàn)ANR錯誤(Application Not Responding 程序無響應(yīng))
2.Service用法
ServiceTest.java如下:
public class ServiceTest extends Service {
private MyLocalBinder myLocalBinder=new
MyLocalBinder();
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG","onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("TAG","onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG","onBind");
return myLocalBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("TAG","onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG","onDestroy");
}
//對外提供的訪問方法
public void downLoad(){
Log.e("downLoad","正在下載...");
}
public void undownLoad(){
Log.e("downLoad","取消下載...");
}
class MyLocalBinder extends Binder{
public ServiceTest getServiceTestInstance(){
return ServiceTest.this;
}
//...這里也可以繼續(xù)寫方法對外提供
}
}
與Activity相似,使用Service也需要通過intent,不能忘記的是在使用Service前,需要在AndroidManifest.xml中進行聲明
啟動方式一:startService()
通過打印Service的生命周期,我們發(fā)現(xiàn)第一次啟動Service的時候,會執(zhí)行onCreate()和onStartCommand(),**
再次啟動時,只會執(zhí)行onStartCommand(),也就是說onCreate()只會在第一次啟動的時候進行初始化
**點擊“stopService”后,Service被銷毀,進入onDestroy()方法。不管我們啟動了多少次Service,只要我們在外部調(diào)用一次Context.stopService()或者在Service內(nèi)部調(diào)用stopSelf(),Service就會被銷毀
上面這種啟動方式的缺點:啟動完Service后,這個Service就在后臺運行了,同時也與啟動它的Activity失去了聯(lián)系,因為不能通過ServiceTest service = new ServiceTest()的方式啟動Service,因而我們的Activity中不能獲取到ServiceTest的實例。
為了解決與啟動Service的組件的通信能力,還有一個解決方案就是通過廣播的形式。我們在Activity中發(fā)出一些想用操作廣播,在Service中注冊該廣播,Service接收到該廣播信息后,完成相應(yīng)的功能。但是頻繁發(fā)送廣播比較消耗性能,同時,由于廣播接受者中的onReceive()中,不能執(zhí)行長時間的工作,時間超過后,可能就直接跳出了方法。因此,這種方案不是首選。
啟動方式二:bindService() Bound機制
通過bindService()方式第一次啟動后,會執(zhí)行onCreate()和onBind()方法,當(dāng)我們點擊“unBindService“時,走的是onUnbind()和onDestroy()方法。如果有另一個組件對同一個Service進行bindService()操作(也就是在bindService()中傳入不同的ServiceConnection,此時只會進入onBind()方法,即onCreate()只會在第一次啟動的時候進行初始化
總結(jié):可以看到,不管是通過哪種方式啟動Service,同一個Service在整個應(yīng)用程序中只有一個實例存在。區(qū)別:(1)兩種方式所走的生命周期是不一樣的(2)何時被銷毀:當(dāng)我們通過startService()啟動時,不管我們啟動了多少次Service,只要我們在外部調(diào)用一次Context.stopService()或者在Service內(nèi)部調(diào)用stopSelf(),Service就會被銷毀;而當(dāng)我們通過bindService()啟動時,前面我們多次啟動service后,當(dāng)所有客戶端發(fā)出unBindService(),這個Service將被系統(tǒng)銷毀。(3)當(dāng)Service即被startService()啟動也被bindService()啟動時,這種情況下,Service必須在既沒有任何activity關(guān)聯(lián)又停止的情況下,Service才會被銷毀。
3.IntentService
我們在第一部分談到,有時需要在service中進行耗時操作,此時就需要開啟一個子線程,而對于這種需求,Android提供了IntentService給用戶,intentservice內(nèi)部已經(jīng)幫我們開啟了線程,我們只需要實現(xiàn)它的onHandleIntent方法,在里面實現(xiàn)我們的功能即可,注:intentservice不能處理多個線程的請求,但是可以處理多個service的請求(此處求解?)
IntentService提供的功能:(1)所有請求處理完成后自動停止服務(wù)(2)提供了默認onBind()的實現(xiàn),直接返回null,意味著我們只能通過startService()的方式啟動IntentService
public class MyIntentService extends IntentService
{
public MyIntentService()
{
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.e("MyIntentService","Thread is"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("OnDestroy","OnDestroy");
}
}
使用時需注意兩點:首先要提供一個無參的構(gòu)造方法,里面調(diào)用父類的有參構(gòu)造方法,第二是實現(xiàn)onHandleIntent這個抽象方法。
4.前臺Service
Service默認都是在后臺默默運行的,用戶基本察覺不到有Service在運行。此時,Service的優(yōu)先級是比較低的,當(dāng)系統(tǒng)資源不足的時候,易被銷毀。因此,如果我們想讓用戶知道有Service在后臺運行,如音樂播放器,或者想讓Service一直保持運行狀態(tài),不容易被系統(tǒng)回收,此時,就可以考慮使用前臺Service。前臺Service是被認為是用戶已知的正在運行的服務(wù),當(dāng)系統(tǒng)需要釋放內(nèi)存時不會優(yōu)先殺掉該進程。
用法:
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG","onCreate");
Notification notification=new Notification(R.mipmap.ic_launcher,"前臺通知",System.currentTimeMillis());
Intent intent=new Intent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,0);
notification.setLatestEventInfo(this, "通知標(biāo)題", "前臺Service內(nèi)容", pendingIntent);
//設(shè)置到前臺運行,第一個參數(shù)為通知notification的唯一ID
startForeground(1,notification);
}
(關(guān)于Notification在SDK23以后和SDK22之前用法不一樣,上面是SDK22以前的,下面是SDK23以后的)
Notification.Builder builder=new Notification.Builder(getApplication());
builder.setContentInfo("補充內(nèi)容");
builder.setContentText("主內(nèi)容區(qū)");
builder.setContentTitle("通知標(biāo)題");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("新消息");
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
Intent intent = new Intent(getApplication(), MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplication(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
如果我們要移除這個前臺Service,只需要調(diào)用stopService()即可