目錄
- 服務元件及應用
-
- 1. 服務概念
- 2. 服務類型
-
- 2.1 本地服務
- 2.2 遠端服務
- 3. service啟動方式與生命周期
- 4. 兩種方法的簡單執行個體
-
- 4.1 非綁定式的服務
-
- 4.1.1 效果
- 4.1.2 代碼
- 4.2 綁定式的服務
-
- 4.2.1 效果
- 4.2.2 代碼
服務元件及應用
1. 服務概念
Service也是Android系統四大元件1之一,它是一種長生命周期、沒有可視化界面、運作于背景的一種服務程式。
比如:播放音樂的時候,你有可能想邊聽音樂邊幹些其他事情,當退出播放音樂的應用,如果不用Service,就聽不到歌了——這就是Service的用武之地了。
Android背景運作的很多Service是在系統啟動時被開啟的,以支援系統的正常工作。
比如:MountService監聽是否有SD卡安裝及移除,PackageManagerService提供軟體包的安裝、移除及檢視等。
激活和停止Service通常是由其他元件完成的。例如,元件Activity的超類Context提供了激活和停止Service的方法。
如同Activity元件一樣,Service元件必須在清單檔案裡使用相應的标簽。使用菜單File→New→Service建立的服務元件,将自動在清單檔案裡注冊。
2. 服務類型
服務類型可劃分為本地服務和遠端服務,也可劃分為綁定式服務與非綁定服務。
2.1 本地服務
本地服務用于應用程式内部(同一個apk内被調用),使用startService()啟動服務,使用stopService()停止服務。
在服務内部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。無論調用了多少次startService(),都隻需調用一次stopService()來停止。
2.2 遠端服務
遠端服務用于應用程式之間(被另一個apk調用)。
可以定義接口并把接口暴露出來,以便其他應用進行操作,比如一個天氣預報服務。用戶端建立到服務對象的連接配接,并通過那個連接配接來調用服務。
通過調用bindService()方法建立連接配接并啟動服務,調用 unbindService()關閉連接配接。多個用戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
遠端服務提供給可被其他應用複用。
3. service啟動方式與生命周期
-
非綁定式服務:
使用
啟動service,調用startService()
和onCreate()
2兩個方法。該service隻能建立一次,也就是說onStartCommand()
隻會執行一次。(但每次調用onCreate()
時,startService()
onStartCommand()
方法都會被調用。)
使用
來結束服務,調用stopService()
方法即可終止服務。onDestroy()
-
綁定式服務:
第一次執行
時,會調用bindService()
和onCreate()
方法建立服務并将service與activity綁定。(但是多次執行onBind()
bindService()
時,并不會多次建立服務和綁定服務,也就不會再次調用這兩個方法。)
當調用
方法或者調用者Context不存在時,比如綁定的activity調用unbindService()
方法,就會調用finish()
和onUnbind()
來結束服務。onDestroy()
- 非綁定式服務和綁定式服務的差別
- 1、startService啟動的服務預設無限期執行,bindService啟動的服務的生命周期與其綁定者相關。
- 2、若使用startService啟動服務,則建立後将無法再與其傳遞消息;若使用bindService啟動服務,則可以通過IBinder接口實作靈活的互動。
4. 兩種方法的簡單執行個體
4.1 非綁定式的服務
4.1.1 效果
界面如下圖所示:
logcat中輸出:
2020-04-17 16:10:52.756 24674-24674/com.example.myapplication_service V/MyService_unbind: service is created!
2020-04-17 16:10:52.756 24674-24674/com.example.myapplication_service V/MyService_unbind: service is started!
2020-04-17 16:11:01.823 24674-24674/com.example.myapplication_service V/MyService_unbind: service is destroyed!
4.1.2 代碼
首先在項目中new一個Service(注意,Service中又兩個選項,new的是Service而不是Service(IntentService))
建立初始會自帶
onBind()
方法,由于此處寫的是非綁定式的方法,直接在裡面
return null;
即可。
MyService_unbind.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService_unbind extends Service {
public MyService_unbind() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
//建立服務
@Override
public void onCreate() {
super.onCreate();
Log.v("MyService_unbind","service is created!");
}
//開始服務
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("MyService_unbind","service is started!");
return START_STICKY;
}
//結束服務
@Override
public void onDestroy() {
super.onDestroy();
Log.v("MyService_unbind","service is destroyed!");
}
}
由于服務是不與使用者互動的,此處為了使代碼更簡單且示範效果更明顯,就在每個方法中用logcat列印出一句話表明服務的生命周期,并在布局檔案中寫兩個用于啟動服務和終止服務的button。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.403" />
<Button
android:id="@+id/btn_ubs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="啟動非綁定式服務"
app:layout_constraintBottom_toTopOf="@+id/btn_ubd"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintVertical_bias="0.676" />
<Button
android:id="@+id/btn_ubd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="204dp"
android:text="關閉非綁定式服務"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
在MainActivity中寫兩個button的觸發事件,在啟動服務按鈕中使用
startService()
啟動service,在終止按鈕中使用
stopService()
來結束服務。
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Button btn_ubs;
Button btn_ubd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//擷取程式界面的兩個按鈕
btn_ubs = (Button)findViewById(R.id.btn_ubs);
btn_ubd = (Button)findViewById(R.id.btn_ubd);
btn_ubs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MyService_unbind.class);
startService(intent);
}
});
btn_ubd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MyService_unbind.class);
stopService(intent);
}
});
}
}
需要注意的問題:
1、若沒有直接new一個service,則要手動在
AndroidManifest.xml
檔案裡面注冊自己寫的service。若是new的service,則會在清單檔案中自動注冊,如下所示。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
2、布局檔案中的button的text最好寫在strings.xml中,可以根據布局檔案中的警告進一步修改。
4.2 綁定式的服務
4.2.1 效果
界面如下圖所示:
每點一次按鈕,textview就會重新整理成自第一次點選按鈕後到這次點選按鈕所經曆的秒數。
4.2.2 代碼
new一個service,命名為BinderService來寫綁定式服務。
綁定式服務需要重寫
onBind()
方法,傳回一個IBinder的對象。IBinder是一個接口,一般我們會繼承實作了IBinder類的Binder類,在其中實作自己的方法。
BinderService.java
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class BinderService extends Service {
public BinderService() {
}
private int count = 0;
class Mybinder extends Binder{
public int getcount(){return count;}
}
Mybinder binder = new Mybinder();
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try{
//線程啟動後每過1s,count+1
Thread.sleep(1000);
count++;
}catch (Exception e){e.printStackTrace();}
}
}
}).start();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return binder;
}
}
在布局檔案中定義一個button來啟動綁定式服務,textview顯示目前秒數。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.179" />
<Button
android:id="@+id/btn_bs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_bs_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
在MainActivity中寫button的觸發事件,在啟動服務按鈕中用
bindService()
來綁定服務。
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
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;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
Button btn_bs;
// 建立綁定服務下的binder的執行個體
BinderService.Mybinder mybinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_bs = (Button)findViewById(R.id.btn_bs);
final TextView textView = (TextView)findViewById(R.id.textView);
// 給出activity要調的服務BinderService,是bindService的第一個參數
Intent binderintent = new Intent(MainActivity.this,BinderService.class);
// bindService的第二個參數
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 這裡的service就是onBind()方法傳回的binder對象
mybinder = (BinderService.Mybinder)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 服務連接配接結束時,設定其為null
mybinder = null;
}
};
// 綁定服務,有三個參數:intent、ServiceConnection和int
// 其中的第三個參數是指明綁定效果
bindService(binderintent,connection, Context.BIND_AUTO_CREATE);
btn_bs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText(mybinder.getcount()+"");
}
});
}
}
- Android四大元件分别為:Activity/Service/BroadCast Recevicer/Content provider ↩︎
- Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。 ↩︎