日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

如何正确的使用 Service?

發(fā)布時(shí)間:2024/3/13 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何正确的使用 Service? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

簡(jiǎn)介

Service(服務(wù))是 Android 四大組件之一,它的主要作用是執(zhí)行后臺(tái)操作,Activity 提供了 UI 界面來跟用戶交互,而 Service 則沒有 UI 界面,所有的操作都是在后臺(tái)完成。

Service 跟 Activity 一樣也可以由其它應(yīng)用程序啟動(dòng),即使用戶切換到了其它應(yīng)用,Service 仍然保持在后臺(tái)運(yùn)行。

此外,一個(gè)組件可以與 Service 進(jìn)行綁定(bind)來跟 Service 進(jìn)行交互,甚至是進(jìn)行進(jìn)程間通信(IPC)。

通常情況下可以使用 Service 進(jìn)行網(wǎng)絡(luò)請(qǐng)求、播放音樂、文件 I/O 等操作。

創(chuàng)建服務(wù)

要?jiǎng)?chuàng)建一個(gè) Service 首先需要繼承 Service 來實(shí)現(xiàn)一個(gè)子類。

public class TestService extends Service {@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags,int startId) {return super.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic boolean onUnbind(Intent intent) {return super.onUnbind(intent);}@Overridepublic void onDestroy() {super.onDestroy();} }

類似于 Activity,所有的 Service 都要在 Manifest 里面進(jìn)行聲明,如下:

<manifest ... >...<application ... ><service android:name="xxx.xxxs.TestService" />...</application> </manifest>

通過在 <service> 標(biāo)簽里將 android:exported 設(shè)置為 false。可以防止其他的程序來啟動(dòng)你的 Service。

啟動(dòng)服務(wù)

通常情況下有兩種方式來啟動(dòng) Service,startService() 和 bindService()。

startService()

Intent intent = new Intent(this, TestService.class); startService(intent); // 開啟服務(wù) stopService(intent); // 停止服務(wù)

當(dāng)組件通過調(diào)用 startService() 啟動(dòng) Service 后,Service 就可以在后臺(tái)無限期的運(yùn)行,即使啟動(dòng) Service 的組件被銷毀也不受影響。

一般情況下 startService() 是執(zhí)行單一操作,并且不會(huì)將執(zhí)行結(jié)果返回給調(diào)用者。例如,它可能是下載文件或者上傳文件,通常操作完成后會(huì)自動(dòng)停止。

該方式允許多個(gè)組件同時(shí)對(duì)相同的 Service 進(jìn)行 startService() 操作,但是如果只要有其中有一個(gè)組件調(diào)用了 stopSelf() 或 stopService(), 該 Service 就會(huì)被銷毀。

bindService()

Intent intent = new Intent(this, TestService.class); ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {}@Overridepublic void onServiceDisconnected(ComponentName name) {} }; // 綁定服務(wù) bindService(intent, connection, Context.BIND_AUTO_CREATE); // 解綁服務(wù) unbindService(aidlConnection);

當(dāng)組件通過調(diào)用 bindService() 啟動(dòng) Service 后,Service 就處于綁定狀態(tài)了。這種方式提供了 client - service 的接口,可以讓調(diào)用者與 Service 進(jìn)行發(fā)送請(qǐng)求和返回結(jié)果的操作,甚至可以進(jìn)行進(jìn)程間的通信(IPC)。

只要有一個(gè)組件對(duì)該 Service 進(jìn)行了綁定,那該 Service 就不會(huì)銷毀。如果多個(gè)組件可以同時(shí)對(duì)一個(gè) Service 進(jìn)行綁定,只有所有綁定的該 Service 的組件都解綁后,該 Service 才會(huì)銷毀。

盡管兩種方式是分開討論的,但是并不是互斥的關(guān)系,使用 startService() 啟動(dòng)了 Service 后,也是可以進(jìn)行綁定的。

注意:雖然 Service 是在后臺(tái)運(yùn)行的,但其實(shí)還是在主線程中進(jìn)行所有的操作。Service 啟動(dòng)時(shí)除非單獨(dú)進(jìn)行了定義,否則沒有單獨(dú)開啟線程或者進(jìn)程都是運(yùn)行在主線程中。

所以任何能阻塞主線程的操作(例如:播放音樂或者網(wǎng)絡(luò)請(qǐng)求),都應(yīng)該在 Service 中單獨(dú)開啟新的線程來進(jìn)行操作,否則很容易出現(xiàn) ANR。

系統(tǒng)方法

在創(chuàng)建一個(gè) Service 時(shí),必須要去繼承 Service,并且需要重寫父類的一些方法來實(shí)現(xiàn)功能。以下是主要方法的介紹。

onStartCommand()

當(dāng)另一個(gè)組件(如:Activity)通過調(diào)用 startService() 來啟動(dòng) Service 時(shí),系統(tǒng)會(huì)調(diào)用該方法。一旦執(zhí)行該方法,Service 就會(huì)啟動(dòng)并在后臺(tái)無限期執(zhí)行。

如果實(shí)現(xiàn)該方法,在 Service 執(zhí)行完后,需要調(diào)用 stopSelf() 或 stopService() 來停結(jié)束Service。

如果只是會(huì)通過綁定的方式(bind)的方式來啟動(dòng) Service 則不需要重寫該方法。

onBind()

系統(tǒng)會(huì)調(diào)用這個(gè)函數(shù)當(dāng)某個(gè)組件(例如:activity,fragment)通過調(diào)用 bindService() 綁定的方式來啟動(dòng) Service 的時(shí)候。在實(shí)現(xiàn)這個(gè)函數(shù)的時(shí)候,必須要返回一個(gè) IBinder 的繼承類,來與 Service 進(jìn)行通信。

這個(gè)函數(shù)是默認(rèn)必須要重寫的,但是如果不想通過綁定的方式來啟動(dòng) Service,則可以直接返回 null。

onCreate()

系統(tǒng)會(huì)調(diào)用此方法在第一次啟動(dòng) Service 的時(shí)候,用于初始化一些一次性的變量。如果 Service 已經(jīng)啟動(dòng)了,則此方法就不會(huì)再別調(diào)用。

onDestroy()

系統(tǒng)在 Service 已經(jīng)不需要準(zhǔn)備被銷毀的時(shí)候會(huì)調(diào)用此方法。Service 中如有用到 thread、listeners、receivers 等的時(shí)候,應(yīng)該將這些的清理方法寫在此方法內(nèi)。

生命周期

與 Activity 類似,Service 也有生命周期回調(diào)方法,可以實(shí)現(xiàn)這些方法來監(jiān)控 Service 狀態(tài)的變化來執(zhí)行相關(guān)操作。

startService()

onCreate() -> onStartCommand() -> onDestroy()

bindService()

onCreate() -> onBind() -> onUnbind() -> onDestroy()

系統(tǒng)資源回收

當(dāng)系統(tǒng)內(nèi)存不足的時(shí)候,系統(tǒng)會(huì)強(qiáng)制回收一些 Activity 和 Service 來獲取更多的資源給那些用戶正在交互的程序或頁面。當(dāng)資源充足的時(shí)候可以通過 onStartCommand() 的返回值,來實(shí)現(xiàn) Service 自動(dòng)重啟。

public int onStartCommand(Intent intent, int flags, int startId) {return START_NOT_STICKY | START_STICKY | START_REDELIVER_INTENT; }

START_NOT_STICKY

當(dāng)系統(tǒng)因回收資源而銷毀了 Service,當(dāng)資源再次充足時(shí)不再自動(dòng)啟動(dòng) Service,除非有未處理的 Intent 準(zhǔn)備發(fā)送。

START_STICKY

當(dāng)系統(tǒng)因回收資源而銷毀了 Service,當(dāng)資源再次充足時(shí)自動(dòng)啟動(dòng) Service。而且再次調(diào)用 onStartCommand() 方法,但是不會(huì)傳遞最后一次的 Intent,相反系統(tǒng)在回調(diào) onStartCommand() 的時(shí)候會(huì)傳一個(gè)空 Intent,除非有未處理的 Intent 準(zhǔn)備發(fā)送。

START_REDELIVER_INTENT

當(dāng)系統(tǒng)因回收資源而銷毀了 Service,當(dāng)資源再次充足時(shí)自動(dòng)啟動(dòng) Service,并且再次調(diào)用 onStartCommand() 方法,并會(huì)把最后一次 Intent 再次傳遞給 onStartCommand(),相應(yīng)的在隊(duì)列里的 Intent 也會(huì)按次序一次傳遞。此模式適用于下載等服務(wù)。

IntentService

Service 本身默認(rèn)是運(yùn)行在主線程里的,所以如果在 Service 要進(jìn)行一些會(huì)堵塞線程的操作,一定要將這些操作放在一個(gè)新的線程里。

為了滿足后臺(tái)運(yùn)行異步線程的需求,Android 的框架提供了 IntentService。

IntentService 是 Service 的子類,并且所有的請(qǐng)求操作都是在異步線程里。如果不需要 Service 來同時(shí)處理多個(gè)請(qǐng)求的話,IntentService 將會(huì)是最佳的選擇。

使用該服務(wù)只需要繼承并重寫 IntentService 中的 onHandleIntent() 方法,就可以對(duì)接受到的 Intent 做后臺(tái)的異步線程操作了。

public class TestIntentService extends IntentService {public TestIntentService() {super("TestIntentService");}public TestIntentService(String name) {super(name);}@Overridepublic void onCreate() {super.onCreate();}@Overrideprotected void onHandleIntent(@Nullable Intent intent) {//TODO: 耗時(shí)操作,運(yùn)行在子線程中}@Overridepublic void onDestroy() {super.onDestroy();} }

前臺(tái)服務(wù)

什么是前臺(tái)服務(wù)?

前臺(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)移除通知后才能被解除。

為什么要使用前臺(tái)服務(wù)?

在一般情況下,Service 幾乎都是在后臺(tái)運(yùn)行,一直默默地做著辛苦的工作。但這種情況下,后臺(tái)運(yùn)行的Service系統(tǒng)優(yōu)先級(jí)相對(duì)較低,當(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ù)。

例如:App中的音樂播放服務(wù)應(yīng)被設(shè)置在前臺(tái)運(yùn)行(前臺(tái)服務(wù))——在 App 后臺(tái)運(yùn)行時(shí),便于用戶明確知道它的當(dāng)前操作、在狀態(tài)欄中指明當(dāng)前歌曲信息、提供對(duì)應(yīng)操作。

如何創(chuàng)建一個(gè)前臺(tái)服務(wù)?

新建一個(gè)服務(wù)。

public class ForegroundService extends Service {private static final int RESULT_CODE = 0;private static final int ID = 1;public ForegroundService() { }@Overridepublic void onCreate() {super.onCreate();Intent intent = new Intent(this, MainActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, RESULT_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);NotificationCompat.Builder builder;// 兼容 Android 8.0if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {String channelId = "foreground_service";NotificationChannel channel = new NotificationChannel(channelId, "channel_1", NotificationManager.IMPORTANCE_HIGH);channel.enableLights(true);channel.setLightColor(Color.GREEN);channel.setShowBadge(true);NotificationManager notificationManager = getSystemService(NotificationManager.class);notificationManager.createNotificationChannel(channel);builder = new NotificationCompat.Builder(this, channelId);} else {builder = new NotificationCompat.Builder(this);}builder.setContentIntent(pendingIntent).setContentTitle("這是前臺(tái)通知標(biāo)題").setContentText("這是內(nèi)容").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher_round).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).setPriority(NotificationManager.IMPORTANCE_HIGH).setDefaults(Notification.DEFAULT_SOUND);startForeground(ID, builder.build());}@Overridepublic int onStartCommand(Intent intent, int flags,int startId) {return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent intent) {return super.onBind(intent);} }

啟動(dòng)與停止前臺(tái)服務(wù)

Intent foregroundIntent = new Intent(this, ForegroundService.class); startService(foregroundIntent); // 啟動(dòng)前臺(tái)服務(wù) stopService(foregroundIntent); // 停止前臺(tái)服務(wù)

前臺(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ì)的信息,非常類似于通知的效果。

服務(wù)保活

通過前面的介紹我們了解到 Service 是后臺(tái)服務(wù)來執(zhí)行一些特定的任務(wù),但是當(dāng)后臺(tái)服務(wù)在系統(tǒng)資源不足的時(shí)候可能會(huì)回收銷毀掉 Service。

那么如何讓后臺(tái)服務(wù)盡量不被殺死呢?基本解決思路如下:

提升 Service 的優(yōu)先級(jí)

為防止 Service 被系統(tǒng)回收,可以嘗試通過提高服務(wù)的優(yōu)先級(jí)解決。優(yōu)先級(jí)數(shù)值最高為 1000,數(shù)字越小,優(yōu)先級(jí)越低。

<service android:name=".ui.service.TestService" ><intent-filter android:priority="1000"/> </service>

persistent 屬性

在 Manifest.xml 文件中設(shè)置 persistent 屬性為 true,則可使該服務(wù)免受 out-of-memory killer 的影響。但是這種做法一定要謹(jǐn)慎,系統(tǒng)服務(wù)太多將嚴(yán)重影響系統(tǒng)的整體運(yùn)行效率。

<application android:persistent="true"> </application>

該屬性的特點(diǎn)如下:

  • 在系統(tǒng)啟動(dòng)的時(shí)候會(huì)被系統(tǒng)啟動(dòng)起來。
  • 在該 App 被強(qiáng)制殺掉后系統(tǒng)會(huì)重新啟動(dòng)該 App,這種情況只針對(duì)系統(tǒng)內(nèi)置App,第三方安裝的 App 不會(huì)被重啟。

將服務(wù)改成前臺(tái)服務(wù)

重寫 onStartCommand 方法,使用 startForeground(int, Notification) 方法來啟動(dòng) Service。利用 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)被銷毀了,就重新啟動(dòng) Service。

參考資料

  • 官方文檔 - 服務(wù)
  • Service 前臺(tái)服務(wù)的使用

我的 GitHub

github.com/jeanboydev

我的公眾號(hào)

歡迎關(guān)注我的公眾號(hào),分享各種技術(shù)干貨,各種學(xué)習(xí)資料,職業(yè)發(fā)展和行業(yè)動(dòng)態(tài)。

技術(shù)交流群

歡迎加入技術(shù)交流群,來一起交流學(xué)習(xí)。

總結(jié)

以上是生活随笔為你收集整理的如何正确的使用 Service?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。