今天咱們來說一下Android中Service的相關內容。
java
Service在Android中和Activity是屬於同一級別上的組件,咱們能夠將他們認爲是兩個好哥們,Activity儀表不凡,迷倒萬千少女,常常作一些公衆人物角色,而Service一副彪悍的長相,但卻身強力壯,經常在後臺作一些搬運工的力氣活,雖然有些累,但你們都不能失去他。android
下面咱們就圍繞Service對其進行全面講解:app
1.Service生命週期異步
Service生命週期能夠從兩種啓動Service的模式開始講起,分別是context.startService()和context.bindService()。ide
(1).startService的啓動模式下的生命週期:當咱們首次使用startService啓動一個服務時,系統會實例化一個Service實例,依次調用其onCreate和onStartCommand方法,而後進入運行狀態,此後,若是再使用startService啓動服務時,再也不建立新的服務對象,系統會自動找到剛纔建立的Service實例,調用其onStart方法;若是咱們想要停掉一個服務,可以使用stopService方法,此時onDestroy方法會被調用,須要注意的是,無論前面使用了多個次startService,只需一次stopService,便可停掉服務。this
(2).bindService啓動模式下的生命週期:在這種模式下,當調用者首次使用bindService綁定一個服務時,系統會實例化一個Service實例,並一次調用其onCreate方法和onBind方法,而後調用者就能夠和服務進行交互了,此後,若是再次使用bindService綁定服務,系統不會建立新的Service實例,也不會再調用onBind方法;若是咱們須要解除與這個服務的綁定,可以使用unbindService方法,此時onUnbind方法和onDestroy方法會被調用。spa
兩種模式有如下幾點不一樣之處:startService模式下調用者與服務無必然聯繫,即便調用者結束了本身的生命週期,只要沒有使用stopService方法中止這個服務,服務仍會運行;一般狀況下,bindService模式下服務是與調用者生死與共的,在綁定結束以後,一旦調用者被銷燬,服務也就當即終止,就像江湖上的一句話:不求同生,希望同死。指針
值得一提的是,之前咱們在使用startService啓動服務時都是習慣重寫onStart方法,在Android2.0時系統引進了onStartCommand方法取代onStart方法,爲了兼容之前的程序,在onStartCommand方法中其實調用了onStart方法,不過咱們最好是重寫onStartCommand方法。日誌
以上兩種模式的流程以下圖所示:code
下面咱們就結合實例來演示一下這兩種模式的生命週期過程。咱們新建一個名爲service的項目,而後建立一個MyService的服務類,代碼以下:
package com.scott.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class MyService extends Service { private static final String TAG = "MyService"; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate called."); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand called."); return super.onStartCommand(intent, flags, startId); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.i(TAG, "onStart called."); } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind called."); return null; } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "onUnbind called."); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy called."); } }
而後再AndroidManifest.xml中配置服務信息,否則這個服務就不會生效,配置以下:
<service android:name=".MyService"> <intent-filter> <action android:name="android.intent.action.MyService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
若是服務只是在本應用中使用,大能夠去掉<intent-filter>屬性。
服務搭建完成以後,咱們就來關注一下調用者MainActivity,它很簡單,只有兩個按鈕,一個是啓動服務,另外一個是中止服務,咱們來看一下他們的點擊事件:
/** * 啓動服務 * @param view */ public void start(View view) { Intent intent = new Intent(this, MyService.class); startService(intent); } /** * 中止服務 * @param view */ public void stop(View view) { Intent intent = new Intent(this, MyService.class); stopService(intent); }
接下來咱們就先點擊一次啓動按鈕,看看都發生了些什麼。日誌打印結果以下:
固然咱們以爲還不過癮,再點擊一次,咱們會發現結果略有不一樣:
咱們看到第二次點擊時onCreate方法就再也不被調用了,而是直接調用了onStartCommand方法(onStartCommand中又調用了onStart方法)。咱們選擇「Settings->Application s->Running services」就會發現咱們剛剛啓動的服務:
而後咱們點擊中止按鈕,試圖中止服務,咱們發現以下現象:
咱們會發現onDestroy方法被調用了,此時服務就中止運行了。咱們再次查看「Running services」,就會發現MyService這個服務已全無蹤影。
在這個過程當中,onBind方法和onUnbind方法始終沒被調用,咱們下面就讓這兩位show一下本身。
咱們修改一下MainActivity的代碼,使其能夠能夠以bindService的方式啓動一個服務,代碼以下:
private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //connected Log.i(TAG, "onServiceConnected called."); } /** * Called when a connection to the Service has been lost. * This typically happens when the process hosting the service has crashed or been killed. * This does not remove the ServiceConnection itself. * this binding to the service will remain active, * and you will receive a call to onServiceConnected when the Service is next running. */ @Override public void onServiceDisconnected(ComponentName name) { } }; /** * 綁定服務 * @param view */ public void bind(View view) { Intent intent = new Intent(this, MyService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } /** * 解除綁定 * @param view */ public void unbind(View view) { unbindService(conn); }
在使用bindService綁定服務時,咱們須要一個ServiceConnection表明與服務的鏈接,它只有兩個方法,onServiceConnected和onServiceDisconnected,前者是在操做者在鏈接一個服務成功時被調用,然後者是在服務崩潰或被殺死致使的鏈接中斷時被調用,而若是咱們本身解除綁定時則不會被調用,因此咱們這裏只研究onServiceConnected這個方法。
看樣子是能夠去綁定一個服務了,其實還不行,由於咱們前面服務中的onBind方法返回值爲null,這樣是不行的,要想實現綁定操做,必須返回一個實現了IBinder接口類型的實例,該接口描述了與遠程對象進行交互的抽象協議,有了它咱們才能與服務進行交互。咱們因而有了這樣的代碼:
@Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind called."); return new Binder() {}; }
咱們返回了一個Binder的實例,而這個Binder偏偏是實現了IBinder接口,因此這樣就能夠實現綁定服務的操做了,一塊兒來演示一下。
先點擊一下綁定按鈕,咱們會發如今MainActivity中打印日誌以下:
似的,onServiceConnected方法被調用了,看來綁定鏈接已經成功了,看看MyService如何:
onCreate方法和onBind方法被調用了,此時服務已進入運行階段,若是再次點擊綁定按鈕,onCreate和onBinder並不會再次被調用,這個過程當中它們僅被調用一次。
而後點擊解除綁定按鈕,咱們會發現MyService打印以下:
能夠看到onUnbind方法和onDestroy方法被調用了,此時MyService已被銷燬,整個生命週期結束。
另外一方面,當咱們退出MainActivity時,服務也會隨之而結束,從這一點上看,MyService能夠說是誓死追隨着MainActivity。
須要注意的是,在鏈接中斷狀態再去作解除綁定操做會引發一個異常,在MainActivity銷燬以前沒有進行解除綁定也會致使後臺出現異常信息,此時咱們就要想辦法確保不會出現此類狀況,能夠這樣作:
private boolean binded; @Override public void onServiceConnected(ComponentName name, IBinder service) { binded = true; } /** * 解除綁定 * @param view */ public void unbind(View view) { unbindService(); } @Override protected void onDestroy() { super.onDestroy(); unbindService(); } /** * 解除服務綁定 */ private void unbindService() { if (binded) { unbindService(conn); binded = false; } }
以上就是bindService的生命週期,正如咱們上面講的同樣,使用bindService啓動服務後調用者和服務綁定到了一塊兒,當調用者被銷燬,服務也當即結終止。
一般狀況下是這樣的,不過也有特殊狀況。當startService和bindService在同一場合下使用時,就會出現稍微不一樣的現象。
若是咱們先以startService方式啓動服務,而後再用bindService綁定到這個服務,以後使用unbindService解除綁定,此時服務並不會所以而終止,而是繼續運行,直到咱們使用stopService來中止這個服務。下面咱們再修改一下代碼以驗證這個過程。MyService保持不變,咱們只需修改一下MainActivity。MainActivity最新代碼以下:
package com.scott.service; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.View; public class MainActivity extends Activity { private static final String TAG = "MainActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "onServiceConnected called."); } @Override public void onServiceDisconnected(ComponentName name) { } }; /** * 啓動服務 * @param view */ public void start(View view) { Intent intent = new Intent(this, MyService.class); startService(intent); } /** * 綁定服務 * @param view */ public void bind(View view) { Intent intent = new Intent(this, MyService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } /** * 解除綁定 * @param view */ public void unbind(View view) { unbindService(conn); } /** * 中止服務 * @param view */ public void stop(View view) { Intent intent = new Intent(this, MyService.class); stopService(intent); } }
在MainActivity中包含了四個按鈕事件,分別是startService、bindService、unbindService和stopService,咱們逐一地按下,看看都發生了什麼。
首先按下啓動服務的按鈕,MyService打印以下:
恩,意料之中。而後咱們再按下綁定服務的按鈕,MyService打印以下:
此時,只有onBind被調用,以後二者就綁定成功。咱們再按下解除綁定的按鈕,MyService打印以下:
此時,onUnbind方法方法被調用,注意,此時MyService並無因解除綁定而終止,而是繼續運行。也許咱們內心會問,若是屢次按下綁定服務的按鈕或重複以上兩個步驟,結果如何呢?答案是onBind和onUnbind都不會再被調用了。看不到onBind被調用,是否是沒有綁定成功啊,咱們來看一下MainActivity打印信息:
重複按下綁定按鈕,幾回都綁定成功了。最後咱們按下中止服務的按鈕,MyService打印以下:
此時,onDestroy被調用了,此時MyService中止了運行,整個生命週期結束。
以上就是關於MyService生命週期的講解,下面咱們來介紹一下如何與服務進行通訊。與服務之間的通訊能夠分爲兩種,進程內的通訊和進程間的通訊,前者調用者和服務在同一應用進程內,然後者是分佈在不一樣應用進程中的。
2.進程內與服務通訊
進程內與服務通訊實際上就是經過bindService的方式與服務綁定,獲取到通訊中介Binder實例,而後經過調用這個實例的方法,完成對服務的各類操做。咱們上面也介紹了很多關於bindService的內容,下面咱們就針對實際需求對代碼作改動。首先是MyService,代碼以下:
package com.scott.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class MyService extends Service { private static final String TAG = "MyService"; @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind called."); return new MyBinder(); } /** * 綁定對象 * @author user * */ public class MyBinder extends Binder { /** * 問候 * @param name */ public void greet(String name) { Log.i(TAG, "hello, " + name); } } }
咱們建立了一個MyBinder的內部類,定義了一個greet方法,在onBind方法中就將這個MyBinder的實例返回,只要調用者獲取到這個實例,就能夠像拿着遊戲手柄同樣對服務進行操做。咱們來看一下調用者的代碼吧,MainActivity代碼以下:
package com.scott.service; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.View; public class MainActivity extends Activity { /** * 綁定對象實例 */ private MyService.MyBinder binder; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { binder = (MyService.MyBinder) service; //獲取其實例 binder.greet("scott"); //調用其方法 } @Override public void onServiceDisconnected(ComponentName name) { } }; /** * 綁定服務 * @param view */ public void bind(View view) { Intent intent = new Intent(this, MyService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } /** * 解除綁定 * @param view */ public void unbind(View view) { unbindService(conn); } }
在上面的代碼中,咱們是在綁定服務成功時將IBinder類型的service參數強轉爲MyService.MyBinder類型,獲取綁定中介實例,而後調用其greet方法。
操做一下,看看效果如何。先點擊綁定服務的按鈕,MyService打印以下:
須要注意的是,與服務綁定是一個異步的過程,也就是說,在這一刻咱們綁定服務,下一刻咱們去操做binder對象,也許它還爲null,這就容易引發空指針異常,正確的作法是把這些操做放到綁定成功以後,確保萬無一失。
以上就是進程內通訊的內容。
關於進程間通訊的內容,限於篇幅緣由,這裏就再也不陳述了