轉載http://blog.csdn.net/liuhe688/article/details/6874378
富貴必從勤苦得,男兒須讀五車書。唐.杜甫《柏學士茅屋》
作爲程序員的我們,須知富貴是要通過勤苦努力才能得到的,要想在行業內有所建樹,就必須刻苦學習和鑽研。
今天我們來講一下Android中Service的相關內容。
Service在Android中和Activity是屬於同一級別上的組件,我們可以將他們認爲是兩個好哥們,Activity儀表不凡,迷倒萬千少女,經常做一些公衆人物角色,而Service一副彪悍的長相,但卻身強力壯,常常在後臺做一些搬運工的力氣活,雖然有些累,但大家都不能失去他。
下面我們就圍繞Service對其進行全面講解:
1.Service生命週期
Service生命週期可以從兩種啓動Service的模式開始講起,分別是context.startService()和context.bindService()。
(1).startService的啓動模式下的生命週期:當我們首次使用startService啓動一個服務時,系統會實例化一個Service實例,依次調用其onCreate和onStartCommand方法,然後進入運行狀態,此後,如果再使用startService啓動服務時,不再創建新的服務對象,系統會自動找到剛纔創建的Service實例,調用其onStart方法;如果我們想要停掉一個服務,可使用stopService方法,此時onDestroy方法會被調用,需要注意的是,不管前面使用了多個次startService,只需一次stopService,即可停掉服務。
(2).bindService啓動模式下的生命週期:在這種模式下,當調用者首次使用bindService綁定一個服務時,系統會實例化一個Service實例,並一次調用其onCreate方法和onBind方法,然後調用者就可以和服務進行交互了,此後,如果再次使用bindService綁定服務,系統不會創建新的Service實例,也不會再調用onBind方法;如果我們需要解除與這個服務的綁定,可使用unbindService方法,此時onUnbind方法和onDestroy方法會被調用。
兩種模式有以下幾點不同之處:startService模式下調用者與服務無必然聯繫,即使調用者結束了自己的生命週期,只要沒有使用stopService方法停止這個服務,服務仍會運行;通常情況下,bindService模式下服務是與調用者生死與共的,在綁定結束之後,一旦調用者被銷燬,服務也就立即終止,就像江湖上的一句話:不求同生,但願同死。
值得一提的是,以前我們在使用startService啓動服務時都是習慣重寫onStart方法,在Android2.0時系統引進了onStartCommand方法取代onStart方法,爲了兼容以前的程序,在onStartCommand方法中其實調用了onStart方法,不過我們最好是重寫onStartCommand方法。
以上兩種模式的流程如下圖所示:
下面我們就結合實例來演示一下這兩種模式的生命週期過程。我們新建一個名爲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,它很簡單,只有兩個按鈕,一個是啓動服務,另一個是停止服務,我們來看一下他們的點擊事件:
-
-
-
-
-
public void start(View view) {
-
Intent intent = new Intent(this, MyService.class);
-
startService(intent);
-
}
-
-
-
-
-
-
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) {
-
-
Log.i(TAG, "onServiceConnected called.");
-
}
-
-
-
-
-
-
-
-
-
@Override
-
public void onServiceDisconnected(ComponentName name) {
-
}
-
};
-
-
-
-
-
-
public void bind(View view) {
-
Intent intent = new Intent(this, MyService.class);
-
bindService(intent, conn, Context.BIND_AUTO_CREATE);
-
}
-
-
-
-
-
-
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;
-
}
-
-
-
-
-
-
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) {
-
}
-
};
-
-
-
-
-
-
public void start(View view) {
-
Intent intent = new Intent(this, MyService.class);
-
startService(intent);
-
}
-
-
-
-
-
-
public void bind(View view) {
-
Intent intent = new Intent(this, MyService.class);
-
bindService(intent, conn, Context.BIND_AUTO_CREATE);
-
}
-
-
-
-
-
-
public void unbind(View view) {
-
unbindService(conn);
-
}
-
-
-
-
-
-
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();
-
}
-
-
-
-
-
-
-
public class MyBinder extends Binder {
-
-
-
-
-
-
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) {
-
}
-
};
-
-
-
-
-
-
public void bind(View view) {
-
Intent intent = new Intent(this, MyService.class);
-
bindService(intent, conn, Context.BIND_AUTO_CREATE);
-
}
-
-
-
-
-
-
public void unbind(View view) {
-
unbindService(conn);
-
}
-
}
在上面的代碼中,我們是在綁定服務成功時將IBinder類型的service參數強轉爲MyService.MyBinder類型,獲取綁定中介實例,然後調用其greet方法。
操作一下,看看效果如何。先點擊綁定服務的按鈕,MyService打印如下:
需要注意的是,與服務綁定是一個異步的過程,也就是說,在這一刻我們綁定服務,下一刻我們去操作binder對象,也許它還爲null,這就容易引起空指針異常,正確的做法是把這些操作放到綁定成功之後,確保萬無一失。
以上就是進程內通信的內容。
關於進程間通信的內容,限於篇幅原因,這裏就不再陳述了,大家可以看我之前的兩篇文章,做一下了解:
使用AIDL實現進程間的通信
使用AIDL實現進程間的通信之複雜類型傳遞