Hi,你們好,上一期咱們講了如何使用BroadcastReceiver,這一期咱們講解Android四大組件之Service相關知識。天天一篇技術乾貨,天天咱們一塊兒進步。java
耐心專一不只僅是美德,更是一筆財富。android
Service
是一個能夠在後臺執行長時間運行操做而不提供用戶界面的應用組件。Service可由其餘應用組件啓動,並且即便用戶切換到其餘應用,Service仍將在後臺繼續運行。 此外,組件能夠綁定到Service,以與之進行交互,甚至是執行進程間通訊 (IPC)。 例如,Service能夠處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序交互,而全部這一切都可在後臺進行。網絡
Service
是一個專門在後臺處理長時間任務的Android
組件。ide
1.Service
不是一個單獨的進程;函數
2.Service
也不是一個單獨的線程;this
3.Service
是一個單獨的Android組件,Service運行在主線程上,若是想在Service中處理很佔時間的操做時,必須在Service中開線程,以下降Activity沒有響應的風險;spa
4.Service
不提供用戶界面;操作系統
它有兩種啓動方式:startService
和bindService
。線程
Service有三個常見用途。代理
1.功能調度:Service接收指定的廣播信息,從而進一步分析和處理事件,最後修改數據、更新界面或者進行其餘相關的操做,調度整個應用使其保持正確的狀態。
2.功能提供:Service並不會接收任何的廣播,只接收指定的廣播提供狀態數據,這時須要綁定Service,綁定Service時要管理好Service,通常在Activity的onStop函數裏進行解綁unBindService操做。
3.遠程調用:定義AIDL服務,跨進程調用Service,先定義一個遠程調用接口,而後爲該接口提供一個IBinder實現類,客戶端獲取了遠程的Service的IBinder對象的代理後,經過該IBinder對象去回調遠程Service的屬性或方法。
若是某個程序組件須要在運行時向用戶呈現界面,或者程序須要與用戶交互,就須要用Activity,不然就應該考慮使用Service。
類似點:
1.都是單獨的Android組件;
2.都擁有獨立的生命週期;
3.都是Context的派生類,因此能夠調用Context類定義的如getResources()
、getContentResolver()
等方法;
4.都擁有本身生命週期回調方法;
不一樣點:
1.Activity運行於前臺有圖形用戶界面,負責與用戶交互;Service一般位於後臺運行,不須要與用戶交互,也沒有圖形用戶界面。
隨着應用程序啓動Service方式不一樣,Service的生命週期也略有差別,以下圖:
若是應用程序經過startService()方法來啓動Service,Service的生命週期如上圖左半部分所示。
經過調用startService() 方法啓動Service: 當其餘組件調用startService()方法時,Service被建立,而且無限期運行,其自身必須調用stopSelf()方法或者其餘組件調用stopService() 方法來中止Service,當Service中止時,系統將其銷燬。
若是應用程序經過bindService()方法來啓動Service,Service的生命週期如上圖右半部分所示。
經過bindService() 方法啓動Service: 當其餘組件調用bindService()方法時,Service被建立。接着客戶端經過IBinder接口與Service通訊。客戶端經過unbindService() 方法關閉鏈接。多個客戶端能綁定到同一個Service,而且當他們都解除綁定時,系統將銷燬Service(Service不須要被中止)
特別說明:當Activity調用bindService()綁定一個已經過startService()啓動的Service時,系統只是把Service內部的IBinder對象傳給Activity,並不會把該Service生命週期徹底綁定到該Activity,於是當Activity調用unBindService()方法取消與該Service的綁定時,也只是切斷該Activity與Service之間的關聯,並不能中止該Service組件。要中止該Service組件,還需調用stopService()方法。
在Service
的生命週期裏,經常使用的有:
4個手動調用的方法
手動調用方法 | 做用 |
---|---|
startService() | 啓動服務 |
stopService() | 關閉服務 |
bindService() | 綁定服務 |
unbindService() | 解綁服務 |
5個自動調用的方法
內部自動調用的方法 | 做用 |
---|---|
onCreate() | 建立服務 |
onStartCommand() | 開始服務 |
onDestroy() | 銷燬服務 |
onBind() | 綁定服務 |
onUnbind() | 解綁服務 |
當咱們開始使用Service
的時候固然是啓動一個Service了,啓動Service的方法和啓動Activity很相似,都須要藉助Intent來實現,下面咱們就經過一個具體的例子來看一下。
public class MyService extends Service {
public static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
}
}
複製代碼
要建立一個這樣的Service,你須要讓該類繼承Service類,而後重寫如下方法:
這幾個方法都是回調方法,且在主線程中執行,由Android操做系統在合適的時機調用。
注意:每一個Service
必須在manifest
中 經過來聲明。
<service android:name="com.demo.service.MyService" >
...
</service>
複製代碼
如今咱們經過繼承Service
的方式定義了咱們本身的MyService
類,而且在manifest
中聲明瞭咱們的MyService
,接下來咱們應該啓動咱們本身的服務。
第一種方式:咱們是經過一個Intent
對象,並調用startService()
方法來啓動MyService
。
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
複製代碼
注意:假如咱們是經過點擊Button
執行上面的代碼,那麼第一次點擊的時候回執行其中的onCreate()
跟onStartCommand()
方法,可是當咱們第二次點擊的時候就只會執行onStartCommand()
方法。
爲何會這樣呢? 這是因爲onCreate()
方法只會在Service
第一次被建立的時候調用,若是當前Service
已經被建立過了(第一次點擊建立了MyService
),無論怎樣調用startService()
方法,onCreate()
方法都不會再執行。
第二種方式:經過bindService
啓動Service
。
bindService
啓動服務特色: 1.bindService
啓動的服務和調用者之間是典型的client-server
模式。調用者是client
,service
則是server
端。service
只有一個,但綁定到service
上面的client
能夠有一個或不少個。這裏所提到的client
指的是組件,好比某個Activity
。
2.client
能夠經過IBinder
接口獲取Service
實例,從而實如今client
端直接調用Service
中的方法以實現靈活交互,這在經過startService()
方法啓動中是沒法實現的。
3.bindService
啓動服務的生命週期與其綁定的client
息息相關。當client
銷燬時,client
會自動與Service
解除綁定(client
會有ServiceConnectionLeaked
異常,但程序不會崩潰)。固然,client
也能夠明確調用Context
的unbindService()
方法與Service
解除綁定。當沒有任何client
與Service
綁定時,Service
會自行銷燬。
啓動了以後,當咱們想中止服務的時候該怎麼作呢?
第一種方式:咱們也是經過一個Intent
對象,並調用stopService()
方法來中止MyService
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
複製代碼
第二種方式:調用unbindService(conn)
方法來中止MyService
unbindService(ServiceConnection conn)
複製代碼
在上面咱們高高興興的啓動了Service
了,可是細心的你可能發現了,貌似咱們僅僅只是啓動了而已,Activity
跟Service
並無多少"交流",下面咱們就讓Activity
跟Service
交流一下。
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class MyBinder extends Binder {
public void startDownload() {
Log.d("TAG", "startDownload() executed");
// 執行具體的下載任務
}
}
}
複製代碼
接下來咱們在MainActivity
中經過Button
來綁定Service
和解除綁定
public class MainActivity extends Activity implements OnClickListener {
private Button bindService;
private Button unbindService;
private MyService.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
@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()
方法,這兩個方法分別會在Activity
與Service
創建關聯和解除關聯的時候調用。在onServiceConnected()
方法中,咱們又經過 向下轉型 獲得了MyBinder
的實例,有了這個實例,Activity
和Service
之間的關係就變得很是緊密了。如今咱們能夠在Activity
中根據具體的場景來調用MyBinder
中的任何public
方法,即實現了Activity
指揮Service
幹什麼Service
就去幹什麼的功能。
固然,如今Activity
和Service
其實還沒關聯起來了呢,這個功能是在Bind Service按鈕的點擊事件裏完成的。能夠看到,這裏咱們仍然是構建出了一個Intent
對象,而後調用bindService()
方法將Activity
和Service
進行綁定。bindService()
方法接收三個參數,第一個參數就是剛剛構建出的Intent
對象,第二個參數是前面建立出的ServiceConnection
的實例,第三個參數是一個標誌位,這裏傳入BIND_AUTO_CREATE
表示在Activity
和Service
創建關聯後自動建立Service
,這會使得MyService
中的onCreate()
方法獲得執行,但onStartCommand()
方法不會執行(只有當咱們經過 startService()
方法請求啓動服務時,調用此方法)。
解除Activity和Service之間的關聯,調用
unbindService(connection);
複製代碼
在MyService
的內部經過stopSelf()
方法來銷燬的;
一個Service必需要在既沒有和任何Activity關聯又處理中止狀態的時候纔會被銷燬;
在Service的onDestroy()方法裏去清理掉那些再也不使用的資源,防止在Service被銷燬後還會有一些再也不使用的對象仍佔用着內存;
IntentService
是Service的子類,在介紹IntentService以前,先來了解使用Service時須要注意的兩個問題
Service
不會專門啓動一個線程執行耗時操做,全部的操做都是在主線程中進行的,以致於容易出現ANR,因此須要手動開啓一個子線程;Service
不會自動中止,須要調用stopSelf()
方法 或者 是stopService()
方法中止;使用IntentService
不會出現這兩個問題,由於IntentService
在開啓Service
時,會自動開啓一個新的線程來執行它,另外,當Service
運行結束後,會自動中止。
第一種方式,返回 START_STICKY
或 START_REDELIVER_INTENT
當Service
因內存不足而被系統kill後,一段時間後內存再次空閒時,系統將會嘗試從新建立此Service
,一旦建立成功後將回調onStartCommand
方法,但其中的Intent
將是null
,除非有掛起的Intent
,如pendingintent
,這個狀態下比較適用於不執行命令、但無限期運行並等待做業的媒體播放器或相似的服務。
/** * 返回 START_STICKY 或 START_REDELIVER_INTENT * @param intent * @param flags * @param startId * @return */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//return super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
複製代碼
第二種方式,提升service的優先權
<service
android:name="com.demo.UploadService"
android:enabled="true" >
<intent-filter android:priority="1000" >
<action android:name="com.demo.MyService" />
</intent-filter>
</service>
複製代碼
Service做爲Android的四大組件之一,而且項目開發過程當中一些場景下常常被使用到,小夥伴們趕忙上手實操,把它靈活的運用到項目中,結合上兩期的Activity和BroadcastReceiver實現有趣的交互吧。
PS:若是還有未看懂的小夥伴,歡迎加入咱們的QQ技術交流羣:892271582,裏面有各類大神回答小夥伴們遇到的問題哦~