上一篇Android之Service詳解(一)講解了Service的定義、開啟和關閉以及生命周期,接下來講解Service與Activity間的通信,以及IntentService等内容。
一、Service與Activity間的通信
在上一篇中我們學習了啟動和停止服務的方法,不知道你有沒有發現,雖然服務是在活動裡啟動的,但在啟動了服務之後,活動與服務基本就沒有什麼關系了。确實如此,我們在活動裡調用了startService()方法來啟動MyService 這個服務,然後MyService 的onCreate()和onStartCommand()方法就會得到執行。之後服務會一直處于運作狀态,但具體運作的是什麼邏輯,活動就控制不了了。這就類似于活動通知了服務一下:“你可以啟動了!”然後服務就去忙自己的事情了,但活動并不知道服務到底去做了什麼事情,以及完成的如何。
那麼有沒有什麼辦法能讓活動和服務的關系更緊密一些呢?例如在活動中指揮服務去幹什麼,服務就去幹什麼。當然可以,這就需要借助我們剛剛忽略的onBind()方法了。
比如說目前我們希望在MyService 裡提供一個下載下傳功能,然後在活動中可以決定何時開始下載下傳,以及随時檢視下載下傳進度。實作這個功能的思路是建立一個專門的Binder 對象來對下載下傳功能進行管理,修改MyService 中的代碼,如下所示:
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.d("MyService", "startDownload executed");
}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return ;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
……
}
可以看到,這裡我們建立了一個DownloadBinder 類,并讓它繼承自Binder,然後在它的内部提供了開始下載下傳以及檢視下載下傳進度的方法。當然這隻是兩個模拟方法,并沒有實作真正的功能,我們在這兩個方法中分别列印了一行日志。
接着,在MyService 中建立了DownloadBinder 的執行個體,然後在onBind()方法裡傳回了這個執行個體,這樣MyService 中的工作就全部完成了。
public class MainActivity extends Activity implements OnClickListener {
private Button bindService;
private Button unbindService;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
可以看到,這裡我們首先建立了一個ServiceConnection 的匿名類,在裡面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分别會在活動與服務成功綁定以及解除綁定的時候調用。在onServiceConnected()方法中,我們又通過向下轉型得到了DownloadBinder 的執行個體,有了這個執行個體,活動和服務之間的關系就變得非常緊密了。
現在我們可以在活動中根據具體的場景來調用DownloadBinder 中的任何public 方法,即實作了指揮服務幹什麼,服務就去幹什麼的功能。這裡仍然隻是做了個簡單的測試,在onServiceConnected()方法中調用了DownloadBinder 的startDownload()和getProgress()方法。
當然,現在活動和服務其實還沒進行綁定呢,這個功能是在Bind Service 按鈕的點選事件裡完成的。可以看到,這裡我們仍然是建構出了一個Intent 對象,然後調用bindService()方法将MainActivity 和MyService 進行綁定。bindService()方法接收三個參數,第一個參數就是剛剛建構出的Intent 對象,第二個參數是前面建立出的ServiceConnection 的執行個體,第三個參數則是一個标志位,這裡傳入BIND_AUTO_CREATE 表示在活動和服務進行綁定後自動建立服務。這會使得MyService 中的onCreate()方法得到執行,但onStartCommand()方法不會執行。
然後如果我們想解除活動和服務之間的綁定該怎麼辦呢?調用一下unbindService()方法就可以了,這也是Unbind Service 按鈕的點選事件裡實作的功能。
二、Service生命周期(二)
上一篇講解了基本的Service(Bound Service)生命周期,這裡講結合Unbound Service一起講解。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICO2gDM1AzM2ETNyATM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
這兩條路徑并不是毫不相幹的:當調用startService()start一個Service後,您仍可以bind該Service。比如,當播放音樂時,需調用startService()啟動指定播放的音樂,當需要擷取該音樂的播放進度時,有需要調用bindService(),在這種情況下,直到Service被unbind ,調用stopService() 或stopSelf()都不能停止該Service。
實作Service的生命周期回調:
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
這些生命周期方法在使用時無需調用各自的父類方法。
在兩條生命周期路徑中,都包含了兩個嵌套的生命周期:
1、完整生命周期( entire lifetime ):從onCreate()被調用到onDestroy()傳回。與Activity類似,一般在onCreate()中做一些初始化工作,而在onDestroy()做一些資源釋放工作。如,若Service在背景播放一個音樂,就需要在onCreate()方法中開啟一個線程啟動音樂,并在onDestroy()中結束線程。
無論是startService() 還是 bindService()啟動Service,onCreate() 和 onDestroy()均會被回調。
2、活動生命周期(active lifetime):從onStartCommand() 或 onBind()回調開始。由相應的startService() 或 bindService()調用。
若是Start Service,那麼Service的活動生命周期結束就意味着其完整生命周期結束 (the active lifetime ends the same time that the entire lifetime ends),即便onStartCommand()傳回後,Service仍處于活動狀态;若是bound Service,那麼當onUnbind()傳回時,Service的活動生命周期結束。
注意:針對Start Service,由于Service中沒有類似onStop()的回調,是以在調用stopSelf() 或 stopService()後,隻有onDestroy()被回調标志着Service已停止。
三、Service更多技巧
1、向使用者發送通知
運作中的Service可以通過Toast Notifications 或 Status Bar Notifications 向使用者發送通知。Toast是一個可以短時間彈出的提醒框。二Status Bar是頂部狀态欄中出現的太有圖示的資訊,使用者可以通過下拉狀态欄獲得具體資訊并執行某些操作(如啟動Activity)。
通常,Status Bar用于通知某些操作已經完成,如下載下傳檔案完成。當使用者下拉狀态欄後,點選該通知,可擷取詳細内容,如檢視該下載下傳的檔案。
2、運作前台Service
前台Service用于動态通知消息,如天氣預報。該Service不易被kill。前台Service必須提供status bar,隻有前台Service被destroy後,status bar才能消失。
舉例來說,一個播放音樂的Service必須是前台Service,隻有這樣使用者才能确知其運作狀态。為前台Service提供的status bar可以顯示目前音樂的播放狀态,并可以啟動播放音樂的Activity。
調用startForeground()可以啟動前台Service。該方法接收兩個參數,參數一是一個int型變量,使用者指定該通知的唯一性辨別,而參數而是一個Notification用于配置status bar,示例如下:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, , notificationIntent, );
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
調用stopForeground()來移除(remove)前台Service。該方法需傳入一個boolean型變量,表示是否也一并清除status bar上的notification。該方法并不停止Service,如果停止正在前台運作的Service,那麼notification 也一并被清除。
參考:
http://www.cnblogs.com/lwbqqyumidi/p/4181185.html
http://blog.csdn.net/vanpersie_9987/article/details/51360245