前言 Hi,大家好,上一期我們講瞭如何使用BroadcastReceiver,這一期我們講解Android四大組件之Service相關知識。每天一篇技術乾貨,每天我們一起進步。 耐心專註不僅僅是美德,更是一筆財富。 1.簡介與定義 Service是一個可以在後臺執行長時間運行操作而不提供用戶界面的應 ...
Hi,大家好,上一期我們講瞭如何使用BroadcastReceiver,這一期我們講解Android四大組件之Service相關知識。每天一篇技術乾貨,每天我們一起進步。
耐心專註不僅僅是美德,更是一筆財富。
1.簡介與定義
簡介
Service
是一個可以在後臺執行長時間運行操作而不提供用戶界面的應用組件。Service可由其他應用組件啟動,而且即使用戶切換到其他應用,Service仍將在後臺繼續運行。 此外,組件可以綁定到Service,以與之進行交互,甚至是執行進程間通信 (IPC)。 例如,Service可以處理網路事務、播放音樂,執行文件 I/O 或與內容提供程式交互,而所有這一切均可在後臺進行。
定義
Service
1.Service
不是一個單獨的進程;
2.Service
也不是一個單獨的線程;
3.Service
是一個單獨的Android組件,Service運行在主線程上,如果想在Service中處理很占時間的操作時,必須在Service中開線程,以降低Activity沒有響應的風險;
4.Service
不提供用戶界面;
它有兩種啟動方式:startService
和bindService
。
2.用途
Service有三個常見用途。
1.功能調度:Service接收指定的廣播信息,從而進一步分析和處理事件,最後修改數據、更新界面或者進行其他相關的操作,調度整個應用使其保持正確的狀態。
2.功能提供:Service並不會接收任何的廣播,只接收指定的廣播提供狀態數據,這時需要綁定Service,綁定Service時要管理好Service,一般在Activity的onStop函數里進行解綁unBindService操作。
3.遠程調用:定義AIDL服務,跨進程調用Service,先定義一個遠程調用介面,然後為該介面提供一個IBinder實現類,客戶端獲取了遠程的Service的IBinder對象的代理後,通過該IBinder對象去回調遠程Service的屬性或方法。
3.應用場景
如果某個程式組件需要在運行時向用戶呈現界面,或者程式需要與用戶交互,就需要用Activity,否則就應該考慮使用Service。
4.Service與Activity對比
相似點:
1.都是單獨的Android組件;
2.都擁有獨立的生命周期;
3.都是Context的派生類,所以可以調用Context類定義的如getResources()
、getContentResolver()
等方法;
4.都擁有自己生命周期回調方法;
不同點:
1.Activity運行於前臺有圖形用戶界面,負責與用戶交互;Service通常位於後臺運行,不需要與用戶交互,也沒有圖形用戶界面。
5.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() | 解綁服務 |
6.Service的使用
當我們開始使用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類,然後重寫以下方法:
-
onCreate() 1.如果service沒被創建過,調用startService()後會執行onCreate()和onStartCommand()方法; 2.如果service已處於運行中,調用startService()不會執行onCreate()方法,只執行onStartCommand()方法。 也就是說,onCreate()只會在第一次創建service時候調用,多次執行startService()不會重覆調用onCreate(),此方法適合完成一些初始化工作。
-
onStartCommand() 如果多次執行了Context的startService()方法,那麼Service的onStartCommand()方法也會相應的多次調用。onStartCommand()方法很重要,我們在該方法中根據傳入的Intent參數進行實際的操作,比如會在此處創建一個線程用於下載數據或播放音樂等。
-
onBind() Service中的onBind()方法是抽象方法,Service類本身就是抽象類,所以onBind()方法是必須重寫的,即使我們用不到。
-
onDestroy() 在銷毀的時候會執行Service的該方法。
這幾個方法都是回調方法,且在主線程中執行,由Android操作系統在合適的時機調用。
註意:每個Service
必須在manifest
中 通過<service>來聲明。
<service android:name="com.demo.service.MyService" >
...
</service>
現在我們通過繼承Service
的方式定義了我們自己的MyService
類,並且在manifest
中聲明瞭我們的MyService
,接下來我們應該啟動我們自己的服務。
啟動Service
第一種方式:我們是通過一個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
會自行銷毀。
啟動了之後,當我們想停止服務的時候該怎麼做呢?
停止Service
第一種方式:我們也是通過一個Intent
對象,並調用stopService()
方法來停止MyService
Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent);
第二種方式:調用unbindService(conn)
方法來停止MyService
unbindService(ServiceConnection conn)
Service和Activity通信
在上面我們高高興興的啟動了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);
關於銷毀Service說明
-
在
MyService
的內部通過stopSelf()
方法來銷毀的; -
一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷毀;
-
在Service的onDestroy()方法里去清理掉那些不再使用的資源,防止在Service被銷毀後還會有一些不再使用的對象仍占用著記憶體;
7.IntentService
IntentService
是Service的子類,在介紹IntentService之前,先來瞭解使用Service時需要註意的兩個問題
-
Service
不會專門啟動一個線程執行耗時操作,所有的操作都是在主線程中進行的,以至於容易出現ANR,所以需要手動開啟一個子線程; -
Service
不會自動停止,需要調用stopSelf()
方法 或者 是stopService()
方法停止;
使用IntentService
不會出現這兩個問題,因為IntentService
在開啟Service
時,會自動開啟一個新的線程來執行它,另外,當Service
運行結束後,會自動停止。
8.如何保證服務不會被殺死
第一種方式,返回 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:如果還有未看懂的小伙伴,歡迎關註我們的WXGZH:下碼看花,裡面有各種大神回答小伙伴們遇到的問題哦~