一闯割、如何保活后臺(tái)服務(wù)
在Android Services (后臺(tái)服務(wù)) 里面优炬,我們了解了Android四大組件之一的Service,知道如何使用后臺(tái)服務(wù)進(jìn)行來完成一些特定的任務(wù)怨喘。但是后臺(tái)服務(wù)在系統(tǒng)內(nèi)存不足的時(shí)候,可能會(huì)被系統(tǒng)殺死振定。那么如何讓后臺(tái)服務(wù)盡量不被殺死呢必怜?基本的解決思路主要有以下幾種:
1. 提高Service的優(yōu)先級(jí):
<!-- 為防止Service被系統(tǒng)回收,可以嘗試通過提高服務(wù)的優(yōu)先級(jí)解決后频,1000是最高優(yōu)先級(jí)梳庆,數(shù)字越小,優(yōu)先級(jí)越低 -->
android:priority="1000"
2.把service寫成系統(tǒng)服務(wù)徘郭,將不會(huì)被回收:
在Manifest.xml文件中設(shè)置persistent屬性為true靠益,則可使該服務(wù)免受out-of-memory killer的影響。但是這種做法一定要謹(jǐn)慎残揉,系統(tǒng)服務(wù)太多將嚴(yán)重影響系統(tǒng)的整體運(yùn)行效率胧后。
3.將服務(wù)改成前臺(tái)服務(wù)foreground service:
重寫onStartCommand方法,使用StartForeground(int,Notification)方法來啟動(dòng)service抱环。
注:一般前臺(tái)服務(wù)會(huì)在狀態(tài)欄顯示一個(gè)通知壳快,最典型的應(yīng)用就是音樂播放器,只要在播放狀態(tài)下镇草,就算休眠也不會(huì)被殺眶痰,如果不想顯示通知,只要把參數(shù)里的int設(shè)為0即可梯啤。
同時(shí)竖伯,對于通過startForeground啟動(dòng)的service,onDestory方法中需要通過stopForeground(true)來取消前臺(tái)運(yùn)行狀態(tài)因宇。
這個(gè)方案也是本文目前準(zhǔn)備詳細(xì)介紹的七婴。
4.利用Android的系統(tǒng)廣播
利用ANDROID的系統(tǒng)廣播檢查Service的運(yùn)行狀態(tài),如果被殺掉察滑,就再起來打厘,系統(tǒng)廣播是Intent.ACTION_TIME_TICK,這個(gè)廣播每分鐘發(fā)送一次贺辰,我們可以每分鐘檢查一次Service的運(yùn)行狀態(tài)户盯,如果已經(jīng)被結(jié)束了,就重新啟動(dòng)Service饲化。
二莽鸭、分析為何后臺(tái)服務(wù)會(huì)被回收
當(dāng)后臺(tái)服務(wù)被回收的時(shí)候,我們查看Logcat里面的日志的時(shí)候吃靠,我們可能會(huì)看到如下的日志:
06-19 08:01:32.755 W/ActivityManager( 2081): Killing ProcessRecord{43a96570 6437:com.example.helloandroid/u0a187}: background ANR 06-19
08:01:32.910 I/ActivityManager( 2081): Process com.example.helloandroid (pid 6437) (adj 0) has died.
這里我們就知道了蒋川,ANR導(dǎo)致的,如何避免ANR撩笆,主要注意以下幾點(diǎn)會(huì)導(dǎo)致ANR的發(fā)生:
- 主線程 (“事件處理線程” / “UI線程”) 在5秒內(nèi)沒有響應(yīng)輸入事件
- BroadcastReceiver 沒有在10秒內(nèi)完成返回
- 在主線程內(nèi)進(jìn)行網(wǎng)絡(luò)操作
- 在主線程內(nèi)進(jìn)行一些緩慢的磁盤操作(I/O操作或數(shù)據(jù)庫操作)
至于如何避免ANR的發(fā)生捺球,各位盡量避免上面這幾種情況出現(xiàn)缸浦,基本上就能避開大部分ANR了。
三氮兵、前臺(tái)服務(wù)
1.what
前臺(tái)服務(wù)是那些被認(rèn)為用戶知道(用戶所認(rèn)可的)且在系統(tǒng)內(nèi)存不足的時(shí)候不允許系統(tǒng)殺死的服務(wù)裂逐。前臺(tái)服務(wù)必須給狀態(tài)欄提供一個(gè)通知,它被放到正在運(yùn)行(Ongoing)標(biāo)題之下——這就意味著通知只有在這個(gè)服務(wù)被終止或從前臺(tái)主動(dòng)移除通知后才能被解除泣栈。
2.why
在一般情況下卜高,Service幾乎都是在后臺(tái)運(yùn)行,一直默默地做著辛苦的工作南片。但這種情況下掺涛,后臺(tái)運(yùn)行的Service系統(tǒng)優(yōu)先級(jí)相對較低,當(dāng)系統(tǒng)內(nèi)存不足時(shí)疼进,在后臺(tái)運(yùn)行的Service就有可能被回收薪缆。
那么,如果我們希望Service可以一直保持運(yùn)行狀態(tài)且不會(huì)在內(nèi)存不足的情況下被回收時(shí)伞广,可以選擇將需要保持運(yùn)行的Service設(shè)置為前臺(tái)服務(wù)拣帽。
3.how
本文按照做音樂播放器的思路,做一下相關(guān)的說明嚼锄,如何使用前臺(tái)服務(wù)减拭。
首先要?jiǎng)?chuàng)建一個(gè)服務(wù):
public class MusicPlayerService extends Service {private static final String TAG = MusicPlayerService.class.getSimpleName();
@Override
public void onCreate() { super.onCreate();
Log.d(TAG, "onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind()");
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
然后創(chuàng)建Notification:
在Service的onStartCommand中添加如下代碼:
@Override public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
// 在API11之后構(gòu)建Notification的方式
Notification.Builder builder = new Notification.Builder
(this.getApplicationContext()); //獲取一個(gè)Notification構(gòu)造器
Intent nfIntent = new Intent(this, MainActivity.class);
builder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 設(shè)置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_large)) // 設(shè)置下拉列表中的圖標(biāo)(大圖標(biāo))
.setContentTitle("下拉列表中的Title") // 設(shè)置下拉列表里的標(biāo)題
.setSmallIcon(R.mipmap.ic_launcher) // 設(shè)置狀態(tài)欄內(nèi)的小圖標(biāo)
.setContentText("要顯示的內(nèi)容") // 設(shè)置上下文內(nèi)容
.setWhen(System.currentTimeMillis()); // 設(shè)置該通知發(fā)生的時(shí)間
Notification notification = builder.build(); // 獲取構(gòu)建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //設(shè)置為默認(rèn)的聲音
}
在完成Notification通知消息的構(gòu)建后,在Service的onStartCommand中可以使用startForeground方法來讓Android服務(wù)運(yùn)行在前臺(tái)区丑。
#參數(shù)一:唯一的通知標(biāo)識(shí)拧粪;參數(shù)二:通知消息。
startForeground(110, notification);// 開始前臺(tái)服務(wù)
如果需要停止前臺(tái)服務(wù)沧侥,可以使用stopForeground來停止正在運(yùn)行的前臺(tái)服務(wù)可霎。
@Override public void onDestroy() {
Log.d(TAG, "onDestroy()");
stopForeground(true);// 停止前臺(tái)服務(wù)--參數(shù):表示是否移除之前的通知
super.onDestroy();
}
到此為止,我們基本上就學(xué)會(huì)了如何使用前臺(tái)服務(wù)了正什。
四啥纸、總結(jié)
1. 前臺(tái)服務(wù)與普通服務(wù)的區(qū)別
- 前臺(tái)Service的系統(tǒng)優(yōu)先級(jí)更高号杏、不易被回收婴氮;
- 前臺(tái)Service會(huì)一直有一個(gè)正在運(yùn)行的圖標(biāo)在系統(tǒng)的狀態(tài)欄顯示,下拉狀態(tài)欄后可以看到更加詳細(xì)的信息盾致,非常類似于通知的效果主经。