【Android】Service前台服務的使用


1.什么是前台服務

前台服務是那些被認為用戶知道(用戶所認可的)且在系統內存不足的時候不允許系統殺死的服務。前台服務必須給狀態欄提供一個通知,它被放到正在運行(Ongoing)標題之下——這就意味着通知只有在這個服務被終止或從前台主動移除通知后才能被解除。
官方描述:
A foreground service(前台服務) is a service that's considered to be(被用戶所認可的) something the user is actively aware of and thus not a candidate for(而不是一個候選的,可以在內存不足時,被系統殺死的) the system to kill when low on memory. A foreground service must provide a notification for the status bar(前台服務必須提供一個顯示通知), which is placed under the "Ongoing" heading(它是不可以忽略的), which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.(意思是通知信息不能被忽略,除非服務停止或主動移除,否則將一直顯示。)


2.為什么要使用前台服務

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

例:
App中的音樂播放服務應被設置在前台運行(前台服務)——在App后台運行時,便於用戶明確知道它的當前操作、在狀態欄中指明當前歌曲信息、提供對應操作。


3.如何創建一個前台服務

  • 新建一個服務



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");
  }
}
    • 構建通知消息
      在Service的onStartCommand中添加如下代碼構建Notification:
@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構建方式:
Notification(int icon, CharSequence tickerText, long when)
  This constructor was deprecated in API level 11. Use Notification.Builder instead.

// 已過時--在API11之后就被淘汰了,之后需要調用Notification.Builder()來構建Notification. Notification notification = new Notification(R.mipmap.ic_launcher, "前台服務測試",System.currentTimeMillis()); 

getNotification()
  This method was deprecated in API level 16. Use build() instead.

// 在API16之后,可以使用build()來進行Notification的構建 Notification notification = new Notification.Builder(this.getApplicationContext())   .setContentText("這是一個前台服務")   .setSmallIcon(R.mipmap.ic_launcher)   .setWhen(System.currentTimeMillis())   .build(); 
  • 啟動前台服務
    在完成Notification通知消息的構建后,在Service的onStartCommand中可以使用startForeground方法來讓Android服務運行在前台。
// 參數一:唯一的通知標識;參數二:通知消息。 startForeground(110, notification);// 開始前台服務 

注:當使用的通知ID一致時,只會更新當前Notification。

  • 停止前台服務
    在Service的onDestory中使用stopForeground方法來停止正在運行的前台服務。
@Override public void onDestroy() {   Log.d(TAG, "onDestroy()");   stopForeground(true);// 停止前台服務--參數:表示是否移除之前的通知   super.onDestroy(); } 

4.自定義Notification布局並設置點擊事件

  • 自定義Notification布局
     有時候我們需要個性化前台服務的通知內容,那么我么就需要通過自定義Notification布局的方式來達到想要的效果:
RemoteViews remoteViews = new RemoteViews(this.getPackageName(),   R.layout.notification_layout);// 獲取remoteViews(參數一:包名;參數二:布局資源) builder = new Notification.Builder(this.getApplicationContext())       .setContent(remoteViews);// 設置自定義的Notification內容 builder.setWhen(System.currentTimeMillis())     .setSmallIcon(R.mipmap.ic_launcher);    Notification notification = builder.getNotification();// 獲取構建好的通知--.build()最低要求在                   // API16及以上版本上使用,低版本上可以使用.getNotification()。 Notificationnotification.defaults = Notification.DEFAULT_SOUND;//設置為默認的聲音    startForeground(110, notification);// 開始前台服務 
 
自定義效果.png
  • 為自定義通知內容上的控件綁定點擊事件
      在通知(Notification)中為自定義布局上的控件綁定點擊事件監聽,我們需要通廣播的形式來實現效果。
  • 注冊廣播
private static final int REQUEST_CODE = 100;
private static final String ACTION_PLAY = "play";
  
Intent intentPlay = new Intent(ACTION_PLAY);// 指定操作意圖--設置對應的行為ACTION
PendingIntent pIntentPlay = PendingIntent.getBroadcast(this.getApplicationContext(),
REQUEST_CODE, intentPlay, PendingIntent.FLAG_UPDATE_CURRENT);// 取的一個PendingIntent,
                      // 它會發送一個廣播,如同Context.sendBroadcast.

 

  • 綁定點擊事件
remoteViews.setOnClickPendingIntent(R.id.iv_pause, pIntentPlay);// 綁定點擊事件(參數一:
  // 控件id;參數二:對應觸發的PendingIntent)

 

  • 注冊廣播監聽器,監聽對應廣播
  • 動態注冊
    • 在Service的onCreate中添加如下代碼注冊廣播監聽:
// 動態注冊廣播
@Override
public void onCreate() {
  super.onCreate();
  Log.d(TAG, "onCreate()");
  
  playerReceiver = new PlayerReceiver();
  IntentFilter mFilter = new IntentFilter();
  
  mFilter.addAction(ACTION_PLAY);
  mFilter.addAction(ACTION_PAUSE);
  mFilter.addAction(ACTION_LAST);
  mFilter.addAction(ACTION_NEXT);
  
  registerReceiver(playerReceiver, mFilter);
}

 

  • 在Service銷毀時(OnDestory中)解除廣播注冊:
@Override
public void onDestroy() {
  Log.d(TAG, "onDestroy()");
  
  stopForeground(true);// 停止前台服務
  if (playerReceiver != null)
    unregisterReceiver(playerReceiver);// 解除廣播注冊
  super.onDestroy();
}

 

  • 靜態注冊
    在AndroidManifest.xml的receiver標簽內添加需要過濾的內容,如:
<receiver  android:name=".PlayerReceiver"  android:exported="true">  <intent-filter>   <action android:name="play"/>   <action android:name="pause"/>   <action android:name="last"/>   <action android:name="next"/>  </intent-filter> </receiver> 
  • BroadcastReceiver代碼如下:
public class PlayerReceiver extends BroadcastReceiver {
  
  private static final String TAG = PlayerReceiver.class.getSimpleName();
  
  public PlayerReceiver() {
  }
  
  @Override
  public void onReceive(Context context, Intent intent) {
    // TODO: This method is called when the BroadcastReceiver is receiving
    String action = intent.getAction();// 獲取對應Action
    Log.d(TAG,"action:"+action);
  
    if(action.equals(MusicPlayerService.ACTION_PLAY)){
      // 進行對應操作
    } else if(action.equals(MusicPlayerService.ACTION_PAUSE)){
    } else if(action.equals(MusicPlayerService.ACTION_LAST)){
    } else if(action.equals(MusicPlayerService.ACTION_NEXT)){
    } 
  
  }
}

 

 
點擊后監聽到的動作.png

1.建立對應的Intent(意圖)--即指定對應操作的行為Action;
2.PendingIntent來處理意圖,(Pending譯為“待定的”、“延遲”,PendingIntent類提供了一種創建可由其它應用程序在稍晚時間觸發的Intent的機制--推薦閱讀
解析Android延遲消息(PendingIntent)處理

3.通過RemoteViews.setOnClickPendingIntent(int viewId,PendingIntent pendingIntent)方法來為指定的控件綁定對應的意圖;
4.在Service中注冊廣播,監聽對應操作。


5.修改自定義通知(Notification)上的顯示內容

在自定義通知布局后,我們在有些場景下需要修改一些控件的顯示內容(如修改TextView顯示文字、ImageView圖片、ProgressBar進度等),那么我們可以通過如Notification.contentViewsetTextViewText、setImageViewBitmap、setProgressBar等方法打成效果,示例代碼如下:

notification.contentView.setTextViewText(R.id.title_tv, "標題"); 

注:方法的差異不大,都需要傳遞控件的id,需要設置的內容、屬性等參數。


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

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

7.Service系列拓展閱讀

【Android】Service那點事兒
【Android】遠程服務(Remote Service)的使用

用到的圖片資源(看不清沒關系,右擊保存即可...):

 
last_img.png
 
play_img.png
 
pause_img.png
 
next_img.png


作者:紫豪
鏈接:https://www.jianshu.com/p/5505390503fa
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

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



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