Q1.我們使用service來做哪些事兒?
Service是一個專門在后臺處理長時間任務(比如網絡查詢,處理數據,更新Content Provider,firing Intents,和觸發Notification)的Android組件,它沒有UI。
Q1.1這里的后臺指的是什么?
Android的后臺就是指,它的運行是完全不依賴UI的,即使Activity被銷毀,或者程序被關閉,只要
進程還在,Service就可以繼續運行
Q1.2既然在Service里也要創建一個子線程,那為什么不直接在Activity里創建呢?
因為Activity很難對Thread進行控制,當Activity被銷毀之后,就沒有任何其它的辦法可以再重新獲
取到之前創建的子線程的實例。而且在一個Activity中創建的子線程,另一個Activity無法對其進行
操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中
的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中
Binder的實例。因此,使用Service來處理后臺任務,Activity就可以放心地finish,完全不需要擔心
無法對后臺任務進行控制的情況。
Q2.音樂播放器如何在后臺一直播放?
Service組件的優先級比不活躍的Activity高,這樣被系統殺死的概率就降低了。當然,實際上即便運行
中的Service被殺,但是當系統資源又足夠的時候,Service又會被重啟。
當必要的時候Service的優先級可能被提到與前臺Activity相同的優先級(通過組件的標簽)。這是極端
的例子,當結束掉Service會直接影響用戶體驗的時候,比如音樂的播放被打斷。
盡管Service跑起來不需要UI,但是它仍然在UI線程中執行。所以有些耗時的操作你需要放在其他的工作
線程中,比如Thread和AsynTask類等
Q3.怎樣使創建的Service只由自己的APP操縱
需要添加權限
<service android:enabled=”true”
android:name=”.MyService”
android:permission=”com.paad.MY_SERVICE_PERMISSION”/>
Q4. onStartCommand返回參數的作用?
返回的參數是用來決定當系統在Service運行時殺死了Service,資源足夠的時候又重啟這個Service,
系統該如何回應的。
START_STICKY:如果返回的是這個值,當Service任何時候的重啟,onStartCommand都會被調用。
但是注意的一點:傳來的intent參數會丟失,也就是null。
START_NOT_STICKY:當運行時被殺死的時候,如果之前還有start請求未處理(注意是未處理,而
不是處理中),Service才會被重啟,否則服務自動停止
START_REDELIVER_INTENT:如果Service在運行中被殺死,如果還有請求未被處理,或者在處理中。
在后者情況,onStartCommand會被調用,傳入之前傳入的初始狀態的Intent,因為它認為還沒有
完全處理好。
注意:以上行為只有在System kill event的情況下有效,stopSelf和stopService都不會過問
onStartCommand的返回狀態
下面2個參數是用來判斷你的Service是怎么啟動的:
1.START_FLAG_REDELIVERY 對應著START_REDELIVER_INTENT
2.START_FLAG_RETRY 對應著START_STICKY
Q5.如何進行綁定service?
調用bindService。傳入一個intent(隱式或者顯式)和ServiceConnection的實例
Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class);
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
你還需要去實現一個ServiceConnection,重寫它的onServiceConnected和
onServiceDisconneted方法去獲取一個Service實例的引用。
// Reference to the service
private MyMusicService serviceRef;
// Handles the connection between the service and activity
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Called when the connection is made.
serviceRef = ((MyMusicService.MyBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
// Received when the service unexpectedly disconnects.
serviceRef = null;
}
};
Q6.如何創建前臺Service?
Service幾乎都是在后臺運行的,一直以來它都是默默地做著辛苦的工作。但是Service的系統優先級
還是比較低的,當系統出現內存不足情況時,就有可能會回收掉正在后臺運行的Service。如果你希望
Service可以一直保持運行狀態,而不會由于系統內存不足的原因導致被回收,就可以考慮使用前臺
Service。前臺Service和普通Service最大的區別就在于,它會一直有一個正在運行的圖標在系統
的狀態欄顯示,下拉狀態欄后可以看到更加詳細的信息,非常類似于通知的效果。當然有時候你也可能
不僅僅是為了防止Service被回收才使用前臺Service,有些項目由于特殊的需求會要求必須使用前臺
Service,比如說墨跡天氣,它的Service在后臺更新天氣數據的同時,還會在系統狀態欄一直顯示
當前天氣的信息,如下圖所示:
foreground_service.png
在進程優先級中,正在運行的Service優先級是老二的位置,僅次與運行在前臺的Activity。
如果你的Service直接可以與用戶交互的,那么考慮將其放在前臺是必要的。通過startForeground.
startForeground通常必須指定一個不間斷的Notification。這個Notification存在的時間與
你的跑在前臺的Service相同
private void startPlayback(String album, String artist) {
int NOTIFICATION_ID = 1;
// Create an Intent that will open the main Activity
// if the notification is clicked.
Intent intent = new Intent(this, MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
// Set the Notification UI parameters
Notification notification = new Notification(R.drawable.icon,
“Starting Playback”, System.currentTimeMillis());
notification.setLatestEventInfo(this, album, artist, pi);
// Set the Notification as ongoing
notification.flags = notification.flags |
Notification.FLAG_ONGOING_EVENT;
// Move the Service to the Foreground
startForeground(NOTIFICATION_ID, notification);
}
結束前臺的Service通過stopForeground,Notification也會被取消:
public void pausePlayback() {
// Move to the background and remove the Notification
stopForeground(true);
}
Q7.關于遠程service。如果將普通Service轉換成一個遠程Service,在onCreate中執行,還會不會有ANR的情況呢?
將一個普通的Service轉換成遠程Service其實非常簡單,只需要在注冊Service的時候將
它的android:process屬性指定成:remote就可以了
<service
android:name="com.example.servicetest.MyService"
android:process=":remote" >
</service>
不會導致ANR,因為使用了遠程Service后,MyService已經在另外一個進程當中運行了,所以
只會阻塞該進程中的主線程,并不會影響到當前的應用程序
Q7.1那既然遠程Service這么好用,干脆以后我們把所有的Service都轉換成遠程Service吧,還省得再開啟線程了?
遠程Service非但不好用,甚至可以稱得上是較為難用。一般情況下如果可以不使用遠程Service,
就盡量不要使用它。在使用Start Service方式開啟遠程service不會崩潰,但是使用bindservice
方式會崩潰。這是由于用Bind Service我們會讓MainActivity和MyService建立關聯,但是目前MyService已經是一個遠程Service了,Activity和Service運行在兩個不同的進程當中,這時就不能再使用傳統的建立關聯的方式,程序也就崩潰了。
Q7.2如何才能讓Activity與一個遠程Service建立關聯呢?
有辦法,使用AIDL來進行跨進程通信