安卓服務service全解,生命週期,前臺服務、後臺服務,啟動登出、繫結解綁,註冊
全棧工程師開發手冊 (作者:欒鵬)
定義服務(服務的生命週期)
呼叫context.startService()時依次執行 ->onCreate()- >onStartCommand()->Service running
呼叫context.stopService()時依次執行 ->onDestroy()
呼叫context.bindService()時依次執行->onCreate()->onBind()->Service running
呼叫context.onUnbind()時依次執行 -> onDestroy()
當繫結service和所有客戶端解除繫結之後,Android系統將會銷燬它,(除非它同時被onStartCommand()方法開啟)。
因此,如果你的service是一個純粹的繫結service,那麼你不需要管理它的生命週期。
然而,如果你選擇實現onStartCommand()回撥方法,那麼你必須顯式地停止service,因為service此時被看做是開啟的。
這種情況下,service會一直執行到它自己呼叫 stopSelf()或另一個元件呼叫stopService(),不論它是否和客戶端繫結。
另外,如果你的service被開啟並且接受繫結,那麼當系統呼叫你的 onUnbind()方法時,如果你想要在下次客戶端繫結的時候接受一個onRebind()的呼叫(而不是呼叫 onBind()),你可以選擇在 onUnbind()中返回true。
onRebind()的返回值為void,但是客戶端仍然在它的 onServiceConnected()回撥方法中得到 IBinder 物件。
下圖展示了這種service(被開啟,還允許繫結)的生命週期:
程式碼示例:
程式碼中設計服務的生命週期,服務設定為前臺服務和後臺服務。
package com.lp.app.service;
import com.lp.app.Activity1;
import com.lp.app.R;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
//服務的生命週期
public class Service1 extends Service{
public final static String action_name = "com.lp.action.service1";
public final static String key1 = "key1";
public final static String key2 = "key2";
//當服務被建立時,執行oncreat函式
@Override
public void onCreate() {
Log.v("服務生命週期", "服務第一次被啟動");
pausePlayback(); //將服務放置在後臺。預設服務就是後臺服務。前臺服務是一個通知欄
startPlayback("顯示標題","顯示文字"); //開啟一個通知欄,點選通知,可以將服務移動至前臺
}
//onStartCommand為service的重新啟動函式
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//startBackgroundTask(intent, startId); //將服務設定到後臺執行
//START_STICKY:如果service程序被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立service,重新啟動後引數Intent將為null。
//START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務。
//START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將原來Intent的值傳入。
//START_STICKY_COMPATIBILITY:START_STICKY的相容版本,但不保證服務被kill後一定能重啟。
//引數flags可以用來確定service的啟動方式。START_FLAG_REDELIVERY表示Intent引數是由系統執行時在通過stopSelf顯式停止service之前終止它而重新傳遞的。
//FLAF_RETRY表示service已經在異常終止後重新啟動,如果service之前被設為START_STICKY,則會傳入這個標誌
Log.v("服務生命週期", "服務被其他視窗通過startService()啟動");
return Service.START_STICKY;
}
public class MyBinder extends Binder {
Service1 getService() {
return Service1.this;
}
}
private final IBinder binder = new MyBinder();
@Override
public IBinder onBind(Intent intent) {
// TODO 自動生成的方法存根
Log.v("服務生命週期", "一個客戶端正在通過bindService()函式繫結到本服務");
return binder;
}
@Override
public void onRebind(Intent intent)
{
//在onUnbind()函式已經被呼叫過後執行
Log.v("服務生命週期", "一個客戶端正在通過bindService()函式繫結到當前服務");
}
@Override
public boolean onUnbind(Intent intent)
{
Log.v("服務生命週期", "所有客戶端已經通過unbindService()函式脫離繫結");
return true; //返回允許繫結
}
@Override
public void onStart(Intent intent,int startId){
Log.v("服務生命週期", "服務啟動");
super.onStart(intent, startId);
}
@Override
public void onDestroy(){
Log.v("服務生命週期", "服務銷燬");
super.onDestroy();
}
//將一個service移動至前臺
//(前臺服務:會在通知一欄顯示 ONGOING 的 Notification。當服務被終止的時候,通知一欄的 Notification 也會消失,這樣對於使用者有一定的通知作用)
//前臺服務具有較高的優先順序,能在記憶體不足時,不被殺死
private void startPlayback(String contenttitle, String contenttext) {
int NOTIFICATION_ID = 1;
//建立一個當單擊通知時將開啟主activity的intent
Intent intent = new Intent(this, Activity1.class);
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
//設定Notification UI引數
Notification notification = new Notification(R.drawable.icon,"啟動app視窗", System.currentTimeMillis());
notification.setLatestEventInfo(this, contenttitle, contenttext, pi);
//設定notification為持續顯示
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
//將service移到前臺。
startForeground(NOTIFICATION_ID, notification);
}
//將service移動到後臺
public void pausePlayback() {
//移動到後臺並移除Notification
stopForeground(true);
}
}
在manifest檔案中註冊服務
這裡我們hi演示顯式啟動服務和隱式啟動服務。所有這裡處理設定服務的名稱,還設定了觸發條件
<!-- service註冊服務,其中permission表示要想外部應用程式使用這個服務,必須要包含的自定義許可權(只是個名稱) -->
<service
android:name="com.lp.app.service.Service1"
android:permission="com.lp.my_service1_permission">
<intent-filter>
<action android:name="com.lp.action.service1"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
顯式的啟動和終止服務
Intent serviceIntent=null;
//顯示啟動一個服務
private void explicitStart() {
serviceIntent = new Intent(this, Service1.class);
startService(serviceIntent);
}
//顯式終止一個服務
private void explicitStop() {
if(serviceIntent!=null)
stopService(serviceIntent);
}
隱式的啟動和終止服務
隱式啟動,相當於把自定義服務註冊為系統服務,再以啟動系統服務的方式啟動自定義服務。
這種方式的和顯式的啟動和停止服務不同,而是通過intent觸發指定名稱的事件。而這個事件又觸發了註冊在manifest檔案中的service,所以需要在manifest檔案中註冊服務時,設定觸發源
//隱式的啟動一個Service。相當於把自定義服務註冊為系統服務,再以啟動系統服務的方式啟動自定義服務
private void implicitStart() {
Intent intent = new Intent(Service1.action_name); //在註冊服務是設定了intent-filter,所以啟動這個,可以啟動對應的服務
intent.putExtra(Service1.key1, "value1");
intent.putExtra(Service1.key2, "value2");
startService(intent);
}
//隱式終止一個服務
private void implicitStop() {
Intent intent = new Intent(Service1.action_name);
stopService(intent);
}
繫結和解除繫結
//為Service繫結建立一個service連線
//service的引用
private Service1 serviceRef;
//處理service和 activity之間的連線
private ServiceConnection mConnection = new ServiceConnection() {
//當建立連線時呼叫此函式
public void onServiceConnected(ComponentName className, IBinder service) {
serviceRef = ((Service1.MyBinder)service).getService();
Log.v("服務繫結客戶端", "服務繫結建立連線");
}
//當service意外斷開時執行
public void onServiceDisconnected(ComponentName className) {
serviceRef = null;
Log.v("服務繫結客戶端", "服務繫結斷開連線");
}
};
Intent bindIntent=null;
//繫結一個service和activity
private void bindToService() {
bindIntent = new Intent(this, Service1.class);
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
//BIND_AUTO_CREATE表示收到繫結請求的時候,如果服務尚未建立,則即刻建立,在系統記憶體不足需要先摧毀優先順序元件來釋放記憶體,且只有駐留該服務的程序成為被摧毀物件時,服務才被摧毀
//BIND_DEBUG_UNBIND通常用於除錯場景中判斷繫結的服務是否正確,但容易引起記憶體洩漏,因此非除錯目的的時候不建議使用
//BIND_NOT_FOREGROUND表示系統將阻止駐留該服務的程序具有前臺優先順序,僅在後臺執行,該標誌位位於Froyo中引入
}
//解除一個繫結
private void unbindToService() {
if (bindIntent!=null) {
unbindService(mConnection);
}
}