官方定義:Service(服務)是一個沒有用戶界面的在后台運行執行耗時操作的應用組件。其他應用組件能夠啟動Service,並且當用戶切換到另外的應用場景,Service將持續在后台運行。另外,一個組件能夠綁定到一個service與之交互(IPC機制),例如,一個service可能會處理網絡操作,播放音樂,操作文件I/O或者與內容提供者(content provider)交互,所有這些活動都是在后台進行。
Service有兩種狀態,“啟動的”和“綁定”
其中涉及到了Service的兩種狀態:(Started)、(Bound)
-
Started:
通過startService()啟動的服務處於“啟動的”狀態,一旦啟動,service就在后台運行,即使啟動它的應用組件已經被銷毀了。通常started狀態的service執行單任務並且不返回任何結果給啟動者。比如當下載或上傳一個文件,當這項操作完成時,service應該停止它本身。
-
Bound:
“綁定”狀態的service,通過調用bindService()來啟動,一個綁定的service提供一個允許組件與service交互的接口,可以發送請求、獲取返回結果,還可以通過誇進程通信來交互(IPC)。綁定的service只有當應用組件綁定后才能運行,多個組件可以綁定一個service,當調用unbind()方法時,這個service就會被銷毀了。
注意:
a.一個服務在進程中的主線程運行——一個服務不會創建自己的線程,也不會在另外的進程運行。
b.還有就是不要把Service理解成線程,你可以把 Service 想象成一種消息服務,而你可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 里注冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,當然這些都是 Thread 做不到的。舉個簡單的小例子:如果你的 Thread 需要不停地隔一段時間就要連接服務器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在運行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 里面控制之前創建的 Thread。因此你便需要創建並啟動一個 Service ,在 Service 里面創建、運行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會創建一個對應 Service 的實例)。
具體例子:在布局文件中添加兩個按鈕(啟動服務,停止服務),添加點擊事件(具體findById獲取,再添加監聽事件)
創建一個Service類
package com.example.test_myservice_demo01;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("創建服務");
}
@Override
public int onStartCommand(Intent intent,int flags, int startId) {
System.out.println("啟動服務..."); //這里實現服務的核心業務
for (int i=0;i<50;i++){
System.out.println("i="+i);
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
System.out.println("停止服務");
super.onDestroy();
}
}
MainActivity文件:
...
public void onClick(View v) {
switch (v.getId()){
case R.id.button_start:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent); //啟動服務
break;
case R.id.button_stop:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent); //停止服務
break;
default:
break;
}
}
在manifest清單文件中可以看到:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
實驗結果:如果不點擊停止服務,就會一直在運行狀態,有什么辦法可以能讓服務停止下來,只需要在MyService的任何位置調用stopSelf()就能停止服務。
04-17 06:12:36.755 2909-2909/com.example.test_myservice_demo01 I/System.out: 創建服務
04-17 06:12:36.755 2909-2909/com.example.test_myservice_demo01 I/System.out: 啟動服務...
04-17 06:12:36.755 2909-2909/com.example.test_myservice_demo01 I/System.out: i=0
04-17 06:12:36.756 2909-2909/com.example.test_myservice_demo01 I/System.out: i=1
04-17 06:12:36.756 2909-2909/com.example.test_myservice_demo01 I/System.out: i=2
04-17 06:12:36.756 2909-2909/com.example.test_myservice_demo01 I/System.out: i=3
...
04-17 06:12:36.757 2909-2909/com.example.test_myservice_demo01 I/System.out: i=47
04-17 06:12:36.757 2909-2909/com.example.test_myservice_demo01 I/System.out: i=48
04-17 06:12:36.757 2909-2909/com.example.test_myservice_demo01 I/System.out: i=49
點擊停止按鈕后之后
04-17 06:12:43.282 2909-2909/com.example.test_myservice_demo01 I/System.out: 停止服務
...
IntentService服務
這是一個Service的子類,IntentService使用隊列的方式將請求的Intent加入隊列,然后開啟一個worker thread(線程)來處理隊列中的Intent,對於異步的startService請求,IntentService會處理完成一個之后再處理第二個,每一個請求都會在一個單獨的worker thread中處理,不會阻塞應用程序的主線程,這里就給我們提供了一個思路,如果有耗時的操作與其在Service里面開啟新線程還不如使用IntentService來處理耗時操作。而在一般的繼承Service里面如果要進行耗時操作就必須另開線程,但是使用IntentService就可以直接在里面進行耗時操作,因為默認實現了一個worker thread。對於異步的startService請求,IntentService會處理完成一個之后再處理第二個。
例子:
Service類
package com.example.test_myservice_demo01;
import android.app.IntentService;
import android.content.Intent;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p>
* TODO: Customize class - update intent actions and extra parameters.
* 內部類只有一個工作線程來完成耗時操作,只需要實現HandleIntent方法就行
*/
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
System.out.println(intent.getStringExtra("info"));
for (int i =0;i<15;i++){
System.out.println("onHandleIntent-"+i+"-"+Thread.currentThread().getName());
try {
Thread.sleep(500); //模擬延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
// if (i==10){
// stopSelf(); //如果添加,便會停止服務
// break;
// }
}
}
}
MainActivity同上加上一個點擊按鈕
case R.id.button_3:
Intent startIntentService = new Intent(this,MyIntentService.class);
startIntentService.putExtra("info","這個是 我的第一個IntentService");
startService(startIntentService);
break;
-----------------分割線---------------------------
Bound:還有就是綁定服務、解綁
不同於以上服務,在MainActivity中加按鈕點擊事件(bind,unbind)
case R.id.button_bind:
Intent intent = new Intent(this,MyBoundService.class);
//異步
bindService(intent,connection, Context.BIND_AUTO_CREATE); //綁定服務
break;
case R.id.button_unbind:
unbindService(connection); //解除服務
break;
還需要:
//綁定服務的連接回調方法
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//綁定成功后回調方法
downloadBinder = (MyBoundService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//服務異常調用
}
};
Service類:
package com.example.test_myservice_demo01;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyBoundService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder{
public void startDownload(){
Log.d("MyService","startDownload executed");
}
public int getProgress(){
Log.d("MyService","getProgress executed");
return 0;
}
}
public MyBoundService() {
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
// throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("已經解除綁定");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
實驗結果:
綁定:
04-17 06:50:08.346 23840-23840/com.example.test_myservice_demo01 D/MyService: startDownload executed
04-17 06:50:08.346 23840-23840/com.example.test_myservice_demo01 D/MyService: getProgress executed
解綁:
04-17 06:50:58.931 23840-23840/com.example.test_myservice_demo01 I/System.out: 已經解除綁定