天天看點

Android之Service詳解(二)

上一篇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一起講解。

Android之Service詳解(二)

這兩條路徑并不是毫不相幹的:當調用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