---
title: 'Service'
disqus: kyleAlien
---
Service
===
## OverView of Content
如有引用參考請詳註出處,感謝 :smile:
> Service 屬於 Android 四大零組件之一
[TOC]
## Service 概述
* 服務是一種**後台運行**的概念,它不需要使用 UI 跟使用者互交,即使主界面被隱藏 or 切換到別的應用程序,服務仍然可以繼續工作
* **Service 並不是獨立運行在進程中,==服務是依賴於主進程中==,當主進程被結束,Service 也隨之結束**
:::warning
**==Service 默認是運行在主線程中,++Service 並不會新開線程++==,如果處理過於花費時間的任務就會出現 ANR,要特別注意**
:::
### 創建 Service
* File -> New -> Service -> Service,它會詢問是否 Export(讓其他應用訪問)、enable (使否啟用),當然也可以手動創建
> 
* 在 Manifest xml 中也會同時創建 service 節點
```xml=
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.oo.jnidemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
// 服務節點
<service
android:name=".MyMVP.MyService"
android:enabled="true"
android:exported="true"/>
<activity android:name=".MyMVP.MyMVP">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</application>
</manifest>
```
### Manifest Service 介紹
* Service 在 Manifest 只有 name 是必須的屬性,其他屬性可有可無
```java=
<service
android:name="string"
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:permission="string"
android:process="string" >
</service>
```
| 屬性 | 功能 |
| -------- | -------- |
| name | service **路徑名,必須要有此屬性** |
| enabled | 是否可被調用實例化 |
| exported | **是否其他應用程式可以調用,[AIDL](https://hackmd.io/yNGrVdN-RtelUqYQgn9avg#AIDL0) 就技術就可以使用** |
| icon | 該 service 的圖形 |
| label | 顯示給用戶看該 serice 的名稱 |
| process | 進程控制,**默認為主進程中**
| permission | 其他組件啟動此服務時需要的權限 |
| isolatedProcess | service 將運行在系統分出的特殊進程中,之後要調用只能透過 Service API ||
## Service 使用
* **Android 中繼承 Service 代表是一個服務**,目前先忽略 onBind 方法
* Service 啟動有兩種方式,如下
| 開啟 | 關閉 | 特色 | 生命週期 |
| -------- | -------- | -------- | - |
| startService | stopService / stopSelf | 簡單方便,但 **無法控制** 服務的內容細節 | 生命週期不按照 Service,關閉只依照 Function 調用 |
| bindService | unbindService | **可以控制服務的細節** | 當綁定的組件取消綁定時 Service 停止(有可能是 APP kill or unbindService) |
### startService / stopService
* 使用 startService 啟動服務後,該服務就與組件沒關係了,可在後台無限期的運行下去,只有 stopSelf / stopService 可以停止它
> onCreate & onStartCommand 的差別在於,**onCreate 並不是每一次都會被調用,而 onStartCommand 是固定每次都會被調用**
```java=
// Service
public class MyService extends Service {
public MyService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 服務啟動後立刻型的動作可以寫在這裡,每次服務啟動都會調用
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
// Service 創建時調用
}
@Override
public void onDestroy() {
// 回收資源
super.onDestroy();
}
// Service 中唯一抽象方法 onBind
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
```
* UI 界面啟動服務
```java=
// UI
public class TestService extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_service);
findViewById(R.id.serviceBtnStart).setOnClickListener(this);
findViewById(R.id.serviceBtnStop).setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent intent;
switch (view.getId()) {
case R.id.serviceBtnStart:
intent = new Intent(this, MyService.class);
startService(intent);
break;
case R.id.serviceBtnStop:
intent = new Intent(this, MyService.class);
stopService(intent);
break;
}
}
}
```
:::info
Service 可以**經由 stopSelf() 關閉自己**
:::
**--Log 觀察--**
> 分別開關了 2 次
> 
### bindService / unbindService
* 在繼承 Service 時必須顧及 **onBind 函數,該函數是 Service 中唯一的抽象函數**
* 執行 onBind 就不會執行 onStartCommand 方法
```java=
// Service
public class MyService extends Service {
public static final String TAG = "MyService";
public MyService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 服務啟動後立刻型的動作可以寫在這裡,每次服務啟動都會調用
Log.e(TAG, "Service onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
// Service 創建時調用
Log.e(TAG, "Service onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
// 回收資源
Log.e(TAG, "Service onDestroy");
}
// Service 中唯一抽象方法 onBind
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "Service onBind");
if(myBindClass == null) {
throw new UnsupportedOperationException("Not yet implemented");
} else {
return myBindClass;
}
}
private MyBindClass myBindClass = new MyBindClass();
class MyBindClass extends Binder { // Binder implements IBinder
public void startTask() {
Log.d(TAG, "Service start Task");
}
public void stopTask() {
Log.d(TAG, "Service stop Task");
}
}
}
```
* **bindService 要使用監聽,以下匿名 ++ServiceConnection++,監聽 Service 是否已經連上,連上後就可以取得內部類來控制 Service**
```java=
public class TestService extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_service);
findViewById(R.id.serviceBtnStart).setOnClickListener(this);
findViewById(R.id.serviceBtnStop).setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent intent;
switch (view.getId()) {
case R.id.serviceBtnStart:
intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE); // 監聽綁定
break;
case R.id.serviceBtnStop:
unbindService(serviceConnection);
break;
}
}
// 匿名類
private ServiceConnection serviceConnection = new ServiceConnection() {
// "1. "
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 獲取控制 Service 的方法
Log.e(MyService.TAG, "on Service Connected");
// 向下轉型 !
MyService.MyBindClass bindClass = (MyService.MyBindClass) iBinder;
bindClass.startTask();
bindClass.stopTask();
}
// "2. "
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e(MyService.TAG, "on Service Disconnected");
}
};
}
```
1. 系統調用 onServiceConnected 方法時會傳遞參數 IBinder,而 IBinder 就是 onBind() 返回的 IBinder
2. 當服務崩潰 or 中止時會調用該方法,**當客戶端 ++主動取消綁定時並不會調用該方法++**
**--Log 觀察--**
> 
## IntentService
* 會發現一般 Service 有幾個地方很危險,而 IntentService 可以很好的解決這個問題
1. **忘記調用 stopSelf 關閉服務**,IntentService 在服務結束後會自動關閉
2. **處理耗時操作要在服務中新開線程,否則會 ANR (因為服務仍是跑在主線程的),而 ==IntentService 本身就是跑在其他線程==**
* 創建方法 File -> New -> Service -> Service (IntentService),或是自己手動繼承 IntentService,**在 Manifest.xml 中一定要註冊 Service (export=false)**
```java=
// IntentService Test
public class ISTest extends IntentService {
public IntentServiceDemo(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
// 在這裡進行主要操作
}
}
```
### 測試 IntentService
* 為了要測試 IntentService 是否跑在其他的線程
```java=
// IntentService,記得要在 Manifest 註冊
public class IntentServiceTest extends IntentService {
public IntentServiceTest() {
super("My_IntentService"); // 給予線程名稱 1.
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e("tractThread", "Thread Name: " + Thread.currentThread().getName() +
", id : " + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy(); // 記得要呼叫超類
Log.e("tractThread", "IntentService onDestroy");
}
}
// UI
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.Sbtn).setOnClickListener(this);
}
@Override
public void onClick(View v) {
Log.e("tractThread", "Thread Name: " + Thread.currentThread().getName() +
", id : " + Thread.currentThread().getId());
Intent intent = new Intent(this, IntentServiceTest.class);
startService(intent);
}
}
```
1. 記得要給予名稱,否則會錯誤 (直接跳掉...),因為 startService 並不會給予建構函數名稱
2. onDestory 要覆寫時記得要用 super 呼叫超類,避免無法關閉,並且 **在任務執行完成後就會自動關閉服務**
**--Log 輸出--**
> 
## Appendix & FAQ
:::info
:::
###### tags: `Android 基礎`