Android Foreground Service (前台服務)


一、如何保活后台服務

Android Services (后台服務) 里面,我們了解了Android四大組件之一的Service,知道如何使用后台服務進行來完成一些特定的任務。但是后台服務在系統內存不足的時候,可能會被系統殺死。那么如何讓后台服務盡量不被殺死呢?基本的解決思路主要有以下幾種:

1. 提高Service的優先級:

<!-- 為防止Service被系統回收,可以嘗試通過提高服務的優先級解決,1000是最高優先級,數字越小,優先級越低 -->  
android:priority="1000" 

2.把service寫成系統服務,將不會被回收:

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

3.將服務改成前台服務foreground service:

重寫onStartCommand方法,使用StartForeground(int,Notification)方法來啟動service。  

注:一般前台服務會在狀態欄顯示一個通知,最典型的應用就是音樂播放器,只要在播放狀態下,就算休眠也不會被殺,如果不想顯示通知,只要把參數里的int設為0即可。 

同時,對於通過startForeground啟動的service,onDestory方法中需要通過stopForeground(true)來取消前台運行狀態。 

這個方案也是本文目前准備詳細介紹的。

4.利用Android的系統廣播

利用ANDROID的系統廣播檢查Service的運行狀態,如果被殺掉,就再起來,系統廣播是Intent.ACTION_TIME_TICK,這個廣播每分鍾發送一次,我們可以每分鍾檢查一次Service的運行狀態,如果已經被結束了,就重新啟動Service。 

二、分析為何后台服務會被回收

當后台服務被回收的時候,我們查看Logcat里面的日志的時候,我們可能會看到如下的日志:

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導致的,如何避免ANR,主要注意以下幾點會導致ANR的發生:

  • 主線程 (“事件處理線程” / “UI線程”) 在5秒內沒有響應輸入事件 
  •  BroadcastReceiver 沒有在10秒內完成返回 
  • 在主線程內進行網絡操作 
  • 在主線程內進行一些緩慢的磁盤操作(I/O操作或數據庫操作) 

至於如何避免ANR的發生,各位盡量避免上面這幾種情況出現,基本上就能避開大部分ANR了。

三、前台服務

1.what

前台服務是那些被認為用戶知道(用戶所認可的)且在系統內存不足的時候不允許系統殺死的服務。前台服務必須給狀態欄提供一個通知,它被放到正在運行(Ongoing)標題之下——這就意味着通知只有在這個服務被終止或從前台主動移除通知后才能被解除。

2.why

在一般情況下,Service幾乎都是在后台運行,一直默默地做着辛苦的工作。但這種情況下,后台運行的Service系統優先級相對較低,當系統內存不足時,在后台運行的Service就有可能被回收。
那么,如果我們希望Service可以一直保持運行狀態且不會在內存不足的情況下被回收時,可以選擇將需要保持運行的Service設置為前台服務。

3.how

本文按照做音樂播放器的思路,做一下相關的說明,如何使用前台服務。
 
首先要創建一個服務:
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");
  }
}

然后創建Notification:

在Service的onStartCommand中添加如下代碼:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  Log.d(TAG, "onStartCommand()");
  // 在API11之后構建Notification的方式
  Notification.Builder builder = new Notification.Builder
    (this.getApplicationContext()); //獲取一個Notification構造器
  Intent nfIntent = new Intent(this, MainActivity.class);
  
  builder.setContentIntent(PendingIntent.
    getActivity(this, 0, nfIntent, 0)) // 設置PendingIntent
    .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
      R.mipmap.ic_large)) // 設置下拉列表中的圖標(大圖標)
    .setContentTitle("下拉列表中的Title") // 設置下拉列表里的標題
    .setSmallIcon(R.mipmap.ic_launcher) // 設置狀態欄內的小圖標
    .setContentText("要顯示的內容") // 設置上下文內容
    .setWhen(System.currentTimeMillis()); // 設置該通知發生的時間
  
  Notification notification = builder.build(); // 獲取構建好的Notification
  notification.defaults = Notification.DEFAULT_SOUND; //設置為默認的聲音
}

在完成Notification通知消息的構建后,在Service的onStartCommand中可以使用startForeground方法來讓Android服務運行在前台。

// 參數一:唯一的通知標識;參數二:通知消息。
startForeground(110, notification);// 開始前台服務

如果需要停止前台服務,可以使用stopForeground來停止正在運行的前台服務。

@Override
public void onDestroy() {
  Log.d(TAG, "onDestroy()");
  stopForeground(true);// 停止前台服務--參數:表示是否移除之前的通知
  super.onDestroy();
}

到此為止,我們基本上就學會了如何使用前台服務了。

四、總結

1. 前台服務與普通服務的區別

  • 前台Service的系統優先級更高、不易被回收;
  • 前台Service會一直有一個正在運行的圖標在系統的狀態欄顯示,下拉狀態欄后可以看到更加詳細的信息,非常類似於通知的效果。

2.  拓展閱讀:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM