Service作為安卓的四大元件之一,适合長期在背景運作而不需要使用者界面的操作,Service預設運作在UI線程中,是以Service中同樣不适合做耗時操作,如果需要做耗時操作需要開啟子線程。
常用Service的開啟包括start和bind兩種啟動方式。
- 建立自定義Service
public class ServiceTest extends Service {
/**
* 繼承Service時必須要實作的方法
*bind調用時,擷取Intent傳遞的參數,或執行綁定後的操作
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Service首次運作時會執行該方法
* 如果Service已經啟動則不會調用(在onStartCommand()和onBind()之前調用)
*/
@Override
public void onCreate() {
super.onCreate();
}
/**
* 通過start調用時執行該方法,每調用一次,執行一次該方法
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/**
* Service方法銷毀時,執行該方法
*/
@Override
public void onDestroy() {
super.onDestroy();
}
}
以上示範代碼的注釋已很詳細的講解Service每個方法的含義,自定義Service其實很簡單,隻需要繼承Service方法即可,然後根據業務需要重寫在onStartCommand()或onBind()執行相應的操作。下面簡述每個方法的含義。
- onCreate():Service啟動前調用的方法,在在onStartCommand()和onBind()之前調用,處理一些初始化的操作;
- onBind():由于該方法在Service設定為abstract,是以繼承Service必須實作的方法,同時也是通過bind調用時的回調方法;
- onStartCommand():如果通過start方式啟動,必須重寫該方法,在該回調方法中處理相應的業務邏輯,每次調用都會執行該方法;
- onDestroy():Service銷毀時執行該方法,可處理一些資源關閉或釋放的操作。
2 注冊Service
作為四大元件之一,同樣在使用Service的使用,需要繼承Service類,重寫onBind方法,并在AndroidManifest.xml中注冊新建立的Servcie對象。
<service android:name=".ServiceTest"
android:enabled="true"
android:exported="true"
android:isolatedProcess="true"
android:process=":procressName"/>
- name:表示新建立的Service的類名稱;
- enabled:是否被執行個體化,預設為true;
- exported:表示該Service是否支援隐式調用,其預設值是由Service中是否包含intent-filter決定,如果包含intent-filter則預設為true,否則為false,即使設定為true也無法隐式調用,因為如果不添加intent-filter,當通過Intent調用時,安卓就無法知道具體那個控件(Service/Activity/Broadcast Receiver)來響應該請求;
- isolatedProcess:如果設定為true,服務将會在特殊的程序中運作(獨立應用的程序),通過start或bind與其進行資料通信;
- process:程序設定,是否需要在單獨的程序中運作,在設定該參數時需注意,如果設定為“:procressName”和“procressName”,前者表示“App-package:procressName”而後者表示“procressName”程序,兩者表示不同的程序。
3 Service啟動
Service的啟動包括兩種方式,一種是通過start方式,還有一種是bind方式,下面具體介紹該兩種方式的不同含義。
3.1 Bind方式
使用流程如下
- 建立自定義Servcie類,重寫onBind()對象,傳回自定義的Binder類并傳入自定義的Service對象;
- 建立自定義Binder類,編寫針對Service的處理方法;
- 在bindServcie時,傳入ServiceConnection對象,擷取是否連接配接狀态。
建立自定義的Service類,編寫綁定成功後的方法類。
public class ServiceTest extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(this); //傳回自定義的Binder類
}
/**
* Service的處理方法
* @param str
*/
public void serviceMethod(String str){
Log.i(TAG, "setMethod: str "+str);
}
建立自定義Binder類,編寫針對Service的處理方發
public class MyBinder extends Binder {
private static final String TAG = "MyBinder";
private ServiceTest mServiceTest;
public MyBinder(ServiceTest serviceTest) {
mServiceTest = serviceTest;
}
//Binder操作後的方法
public void testMethod(String str){
Log.i(TAG, "testMethod: "+"XIAOHAN "+str);
mServiceTest.serviceMethod(str);
mMyBinderInterface.binderCall(str+"recall");
}
private MyBinderInterface mMyBinderInterface;
public void setMyBinderInterface(MyBinderInterface myBinderInterface) {
mMyBinderInterface = myBinderInterface;
}
public interface MyBinderInterface {
void binderCall(String msg);
}
}
實際使用時,bindService時,需要傳入一個ServiceConnection參數,在onServiceConnected可以擷取到自定義的Binder類對象,通過該對象可以擷取自定義Binder類中的回調處理方法,拿到自定義的Binder類後,說明bind成功,可通過binder對象,傳入參數,間接調用自定義的Service中的處理方法。
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyBinder = (MyBinder) service;
mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
@Override
public void binderCall(String msg) {
Log.i(TAG, "XIAOHAN binderCall: "+msg);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent(MainActivity.this,ServiceTest.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "XIAOHAN onClick: "+mMyBinder);
mMyBinder.testMethod("hangzhou");
}
});
執行流程列印日志如下:
02-24 02:13:53.165 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN serviceConnection
02-24 02:13:53.175 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN bindService
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN onBind:
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/MyBinder: XIAOHAN MyBinder:
執行順序:建立serviceConnection對象–bindService—調用onBind方法----建立MyBinder對象
當點選onclick時,即執行mMyBinder.testMethod(“hangzhou”)時的執行日志如下
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN onClick: [email protected]
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MyBinder: testMethod: XIAOHAN hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN setMethod: hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN binderCall: hangzhourecall
執行順序:自定義Biner的執行方法–Service的綁定處理方法—ServiceConnection的onServiceConnected()回調
注意:如果在使用bind調用Service時,如果在AndroidManifest.xml中對綁定的元件(activity或servcie)設定了程序名,但未設定自定義的Service的相同的程序,則會報如下錯誤:
java.lang.ClassCastException:android.os.BinderProxy
處理的方法見該文章。
3.2 Start方式
可通過其他元件(如Activity)調用startServce方法來開啟Service,每次調用一次start方法,都會回調一次 onStartCommand()方法,但隻要執行一次stopService方法,就會關閉service方法,是以該執行的順序是:
onCreate()—onStartCommand() (n次)—onDestroy()
對于該流程的操作相信大家都已熟悉,但實際使用中我們更多的是關注onStartCommand()方法的使用。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
在檢視該方法前,我們先通過檢視源代碼,擷取該方法的參數類型和傳回值類型,點選源碼,可以檢視如下代碼塊,通過該代碼塊可以看到我們需要的參數類型。
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
方法參數的設定:
- Intent:啟動Service傳遞過來的Intent對象,也可通過Intent傳遞數值;
- flags:包括兩個參數:START_FLAG_REDELIVERY和START_FLAG_RETRY,其中START_FLAG_REDELIVERY:表示之前已經傳遞過Intent資訊,但是由于Service被kill,再次重傳時保留之前Intent的值,表示之前的傳遞還未結束,START_FLAG_RETRY:如果onStartCommand()回調一直沒有傳回值會重新調用onStartCommand()方法;
- startId:指明目前Service的唯一對象,與stopSelfResult (int startId)方法配合使用,可更安全的停止服務。
傳回值的類型:
- START_STICKY_COMPATIBILITY:與START_STICKY類似,主要是相容低版本的作用;
- START_STICKY:線程被殺死,會嘗試再次建立該服務,并會回調onStartCommand()方法,但Intent參數可能為空,是以回調該方法時需要做非空判斷;
- START_NOT_STICKY:線程被殺死時,不會嘗試重新開機該服務,除非程式檢測被殺後,重新開啟,但已經不是被殺調之前的狀态了(建立新的服務對象);
- START_REDELIVER_INTENT:如果線程被殺,重新建立,并傳遞最後一個服務傳遞的Intent的值調用onStartCommand()方法,與START_STICKY不同,該傳遞的Intent值是非空的。
startService啟動Service的方式包括兩種,一種是通過顯示啟動,一種是通過隐式啟動,兩種啟動方式分析如下
1.顯示啟動:
在需要的地方啟動該服務。
Intent intent = new Intent(MainActivity.this, ServiceTest.class);
// intent.putExtra("xiaohan","杭州");//添加資料
Bundle bundle = new Bundle(); //通過Bundle方式傳遞
bundle.putString("xiaohan","杭州");
intent.putExtras(bundle);
startService(intent);
在自定義的Service的onStartCommand方法中處理回調。
//回調中處理該資料
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* String xiaohan = intent.getStringExtra("xiaohan");
Log.i(TAG, "onStartCommand: "+xiaohan);*/
Bundle extras = intent.getExtras();
String xiaohan = extras.getString("xiaohan");
Log.i(TAG, "onStartCommand: "+xiaohan);
return START_STICKY;
}
該方式是我們比較常見的啟動方式,啟動時可通過Intent傳遞參數(支援基本資料類型(及其對應的數組)、String和序列化後的對象),如果包含多個參數,可通過Bundle傳遞,但需要注意通過該種方式傳遞的資料不能太大(不超過1M)。
2.隐式啟動:
在AndroidManifest.xml中聲明自定義的Service,添加action和category。
<service android:name=".ServiceTest">
<intent-filter >
<action android:name="com.example.xiaohan.service"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
采用Bind的方式
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyBinder = (MyBinder) service;
mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
@Override
public void binderCall(String msg) {
Log.i(TAG, "XIAOHAN binderCall: "+msg);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent service = new Intent();
service.setAction("com.example.xiaohan.service"); //service 的 action 值
service.setPackage("com.example.xiaohan.test22"); //遠端服務所在包名
//綁定服務
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
//啟動服務
// startService(service);