通過這種方式啓動的服務屬於比較標準的一類。一般來說,從定義一個服務到使用一個服務,要遵循一下幾個步驟:
- 自己實現一個類,該類繼承service類
- 在清單文件中註冊自己的服務(實際上,四大組件的使用,都需要在清單文件中進行註冊)
- 調用startservice來初始化並且啓動自己的服務
- 調用stopservice來停止我們已經運行的服務。
通過上面的圖,我們也可以觀察到,這種服務的生命週期是怎樣的:
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service" />
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service" />
private Button startService;
private Button stopService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.start_service);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 啓動服務
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服務
break;
default:
break;
}
}
在之前實現service類的時候,我們可以看到有一個抽象方法被重新實現—onBind()。service和activity的通信大致是通過這樣的一種過程來實現的:
- 首先在service的類中,實現一個類,這個類繼承於Binder,這個類中你可以定義一些你自己的方法,這些方法就是以後要在服務中執行的方 法,這個類是服務類的子類。然後,創建一個這個binder類的實例,在Onbind函數中返回這個binder對象。這個對象就像是一個開着的接口,向 外提供了子類內部將要執行的操作。
- 服務對外通信的接口做好之後,想讓活動和服務建立連接的話,就要在兩者之間創建一個連接點。這個連接點的工作由 ServiceConnection類的對象來實現,我們要在activity中創建一個ServiceConnection的匿名類,類中要重寫兩個方 法: onServiceDisconnected,onServiceConnected。之後在創建一個這個類的對象,到此爲止,連接點也做好了,那麼如何 讓服務和活動通過這個連接點連接起來進行通訊工作呢?
- 因爲在實現服務的時候,給我們返回了一個Binder的對象作爲接口,可以看見在重寫onServiceConnected方法的時候,裏面有一 個參數就是Binder類型的參數,通過把這個Binder對象強制類型轉換成我們在服務中自己實現binder類的那個對象的類類型之後,就可以通過它 來調用我們服務內部的一些操作。這就爲活動來指揮服務內部的運行,提供了良好的接口和條件。
- 而對於acitivity來說的話,我們可以通過bindservice這個啓動服務的函數,在參數中把要啓動服務的intent和創建的連接點 ServiceConnection對象傳遞過去,如此一來,在綁定成功之後就可以創建服務並且連接在一起,然後自動調用連接點中 onServiceconnected方法,在這個方法的內部就可以通過參數中的binder對象來指揮服務。在服務和活動解除綁定的時候,自然也會調用 連接點處的onServiceDisconnected方法。
- 我們在活動中,現在通過Binder對象所能執行的是service類的子類所提供的方法,如果想執行service類內部的方法,可以在這個接口中再定義一個函數,返回服務類的一個對象,便可以執行服務類中的成員方法了。
那麼BinderService的生命週期就由以下幾個部分構成:
public static final int UPDATE_TEXT = 1;
private Button start_Service;
private Button stopService;
private Button bindService;
private Button unbindService;
private Button changeUI;
private TextView tvContent;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
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;
}
}
}
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.d("MyService", "startDownload executed");
}
public void stopDownload() {
Log.d("MyService", "stopDownload executed");
}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return 0;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate executed");
Notification notification = new Notification.Builder(this)
.setContentTitle("This is Title")
.setContentText("This is content")
.setSmallIcon(R.mipmap.ic_launcher)
.build();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
startForeground(1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand executed");
new Thread(new Runnable() {
@Override
public void run() {
//線程邏輯
Log.d("MyService Thread","Thread executed");
stopSelf();//終止自己的線程
}
}).start();//開啓線程
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService", "onDestory executed");
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Staet Service" />
<Button
android:id="@+id/stop_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Service" />
<Button
android:id="@+id/bind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bind Service" />
<Button
android:id="@+id/unbind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UnBind Service" />
<Button
android:id="@+id/changeUI"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change UI"/>
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="Hello World"/>
</LinearLayout>
服務的運行一般來說關心兩個問題:
- 如果處理服務中的耗時操作
- 如果在服務中的邏輯運行完畢之後,自動關閉該服務。
那麼能否有一種service可以不用我們自己手動單獨開闢線程,自動的來完成我們的耗時操作呢,並且在服務的邏輯處理完成之後,可以自動的幫我們結束服務,不用我們自己再調用stopself。這種服務就是intentservice。
intentservice的使用方法和普通的service是一樣的,但是在intentservice的實現中,我們需要繼承自intentservice的類,並且要爲這個類重載一個沒有參數的構造方法。具體的實現步驟有以下幾個比較重要的:
- 繼承Intentservice並提供一個無參數的構造函數,在方法內部調用父類的構造函數。
- 重寫父類當中onhandleintent這個方法,把我們要在服務當中處理的邏輯過程都放在這個方法內完成
- 啓動這個service。 這個服務的特殊之處就在於onhandleintent這個方法,這個方法能夠代替我們手動進行的兩個工作:這個方法內部的代碼都是在子線程中運行的,並 且在這個方法執行到末尾的時候,會自動的幫我們關閉service。這樣一來,就不用我們再手動的開子線程和關閉服務了。其實在 intentservice中也是通過開啓子線程來完成服務內的邏輯過程的執行的。
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d("MyIntentService","onHandleIntent executed ,Thread id is "+Thread.currentThread());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");
}
Log.d("MainActivity","MainActivity Thread id is "+Thread.currentThread());
Intent intentService = new Intent(this,MyIntentService.class);
startService(intentService);
- 如果是使用startservice的方式來啓動服務的話,即使啓動這個服務的activity可能處於destory狀態,但是服務本身還是在繼續運行的,除非這個服務所在進程被銷燬,那麼這個服務也會停止。
- 如果是使用bindservice來啓動服務的話,一旦啓動這個服務的activity變爲destory的狀態的時候,那麼這個服務也隨之會銷燬,因爲這個服務是和特定的activity進行綁定的。
- startservice一旦啓動之後,activity就不能再插手服務運行過程中的事情,比如我想了解服務運行的狀態等等,都不能夠實現,也就是說,activity只能是給服務發一個信號來啓動他,但是並不能和它進行交互。
- bindservice就不一樣了,它是依靠binder進程間通訊的機制,在服務本身內部向外提供接口,以便讓activity能夠和服務進行交互操作,這樣一來,activity就可以在服務運行的過程當中來查看服務運行的情況。
- bindservice雖然向外部提供了一個binder的對象,通過這個對象可以調用service內部的一些方法,但是在activity 中,通過binder調用的函數並不是直接一步到位調用了服務中的相關成員函數,而只是得到一個類似指針和映射的東西,通過這些再去找在服務內部真正存在 的方法。總的來說把,bindservice向外部提供的,只是內部信息的一個映射,並不是真正的把內部的函數地址等等信息提供出去。
- 如果有一個service和多個activity同時綁定的話,那麼即使一個activity解除了service的綁定,但是並不會回調 unbind和ondestory的方法,只不過在綁定多個actiity的時候要注意綁定的順序。> 其實bind和start兩種服務最本質的區別就是,Bind是一種服務器/客戶端模型,能夠讓服務的啓動者和服務的執行者交互,但是start不行。
Service到底是什麼?進程 ?線程?
這個問題也是自己剛剛遇到的,一開始有點模糊不清,包括不明白service爲什麼和調用他的activity是在同一個主線程中的。網上的資料, 大家還是最好有一點甄別能力,尤其是論壇上面的,有些明顯的錯誤來誤導提問者。當然,我寫的東西可能也是有錯的,如果有不同意見,大家可以一起討論。 我對這個問題的理解是這樣的:<br> 首先,service不是一線程,也不是一個進程,它是一個組件。它的生存運行,和線程的生命週期沒有一點關係。也就是說,線程的死活和它是沒有關係的。 但是,service的運行,畢竟需要一些條件,比如內存,CPU這些資源。然而,這些資源,操作系統只會分配給進程,這也就是爲什麼,不管我們用什麼種 類的service,只要我們把這個進程幹掉,服務神馬的也就都是浮雲了。所以,服務雖然是一個組件,但是它的運行還是要依賴於進程。
服務分爲遠程服務和本地服務,本地服務就是在我們這個應用進程之內啓動並運行的。一般來說,服務的運行並不會新開一個線程,無論是哪一種service他們都是運行在進程中的主線程中。這也就是爲什麼不能在服務中直接執行大量的耗時操作,要單獨新開一個子線程完成的原因。
還有一個問題:startservice在啓動之後,其實activity銷燬了,但是它還是繼續在運行,直到我們顯式的調用 stopservice或者stopself,他纔會停止。而且,即使我們在服務運行的過程中stopservice,但是服務還是會完成這一次的邏輯, 也就是說,我們在一個服務執行的過程中,不能中斷本次執行的這個過程。這一點對於Bindservice和startservice都是一樣。