在Android王國中,Service是一個勞動模範,總是默默的在後臺運行,無怨無悔,且總是乾最臟最累的活,比如下載文件,傾聽音樂,網路操作等這些耗時的操作,所以我們請尊重的叫他一聲:"勞模,您辛苦了". 帶著這份好尊重,我又重新研讀了API的文檔,發現老外寫東西還是很靠譜的,人家在文檔中告訴你Se ...
在Android王國中,Service是一個勞動模範,總是默默的在後臺運行,無怨無悔,且總是乾最臟最累的活,比如下載文件,傾聽音樂,網路操作等這些耗時的操作,所以我們請尊重的叫他一聲:"勞模,您辛苦了".
帶著這份好尊重,我又重新研讀了API的文檔,發現老外寫東西還是很靠譜的,人家在文檔中告訴你Service是什麼,又告訴你Service又不是什麼,我覺得這種思路不錯,那就從這兩個個方面開始談起吧:- Service是什麼?
- A Service is an application component. ☆ Service 是一個應用程式組件
- that can perform long-running operations in the background. ☆ 它能在後臺執行一些耗時較長的操作.
- and does not provide a user interface. ☆ 並且不提供用戶界面
- Another application component can start a service and it will continue to run in the background event if the user switches to another application. ☆ 服務能被其它的應用程式組件啟動,即使用戶切換到其他的應用程式時還能保持在後臺運行.
- Additionally,a component can bind to a service to interact with it and even perform interprocess communcation(IPC). ☆ 此外,組件還能綁定服務,並與服務交互,甚至執行進程間通信(IPC).
- For example,a service might handle network transactions,play music,perform file I/O,or interact with a content provider,all from the background. ☆ 比如,一個服務可以處理網路傳輸,聽音樂,執行文件操作,或者與內容提供者進行交互,所有這些都在後臺進行.
- A service can essentially tack two forms.☆ 服務有以下兩種基本類型
- Started --> startService()
- Bound --> bindService()
- Service又不是什麼?
- A service is not a separate process.☆ 服務不是一個單獨的進程.
- A service is not a thread.it runs in the main thread of its hosting process. ☆ 服務不是一個線程,它運行在主線程.
- the service does not create its own thread and does not run in a separate process(unless you specify otherwise). ☆ 服務不能自己創建並且不能運行在單獨的進程中(除非你明確指定).
- This means that, if your service is going to do any CPU intensive work ot blocking operations(such as MP3 playback or network). ☆ 這意味著如果你的服務要執行一些很耗CUP的工作或者阻塞的操作(比如播放mp3或網路操作),you should create a new thread within the service to do that work. ☆ 你應該在服務中創建一個新的線程來執行這些工作.
- By using a separate thread, you will reduce the risk of Application Not Responding(ANR) errors and the application's main thread can remain dedicated to user interaction with your activities. ☆ 利用一個分離的進程,將減少你的activities發生應用程式停止響應(ANR)錯誤的風險.
- 如何創建一個Started服務
- 繼承service
publicclassFirstServiceextendsService{
privatestaticfinalString TAG ="--FirstService-->";
publicFirstService(){
Log.i(TAG,"Service is running.");
}
@Override
publicvoid onCreate(){
Log.i(TAG,"onCreate is running.");
super.onCreate();
}
@Override
publicint onStartCommand(Intent intent,int flags,int startId){
Log.i(TAG,"onStartCommand is running.");
returnsuper.onStartCommand(intent, flags, startId);
}
@Override
publicIBinder onBind(Intent intent){
Log.i(TAG,"IBinder is running.");
returnnull;
}
}
- 四大組件都需要在manifests.xml中註冊,這個也不例外.
- 如何啟動它
Intent intent =newIntent(ServiceActivity.this,FirstService.class);
startService(intent);
- 生命周期onCreate(), onStartCommand(), onDestory()就這三個生命周期
--FirstService-->:Service is running.
--FirstService-->: onCreate is running.
--FirstService-->: onStartCommand is running.
-
我們在onStartCommand方法中列印下當前線程
@Override
publicint onStartCommand(Intent intent,int flags,int startId){
Log.i(TAG,"onStartCommand is running.Thread:"+Thread.currentThread());
returnsuper.onStartCommand(intent, flags, startId);
}
onStartCommand is running.Thread:Thread[main,5,main]
-
如何結束服務:①調用stopService()方法 ,會回調service的onDestory()方法;
Intent intent2 =newIntent(ServiceActivity.this,FirstService.class);
stopService(intent2);
- onStartCommand的返回值:
- START_STICKY:粘性的,被意外中止後自動重啟,重新調用onStartCommand(),但會丟失原來激活它的Intent,會用一個null intent來調用onStartCommand(),可以用於播放器.值為1
- START_NOT_STICKY:非粘性的,被意外中止後不會重啟,除非還存在未發送的Intent,這是避免服務運行的最安全選項; 值為2
- START_REDELIVER_INTENT:粘性的且重新發送Intent,被意外中止後重新啟動,且該service組件將得到用於激活它的Intent對象,這中服務適用於需要立即恢復工作的活躍服務,比如下載文件; 值為3
-
onStartCommand的參數:
@Override //第一個參數:為我們傳入的intent;第二個flags:啟動服務的方式,與返回值有關;第三個為我們啟動service的次數.
publicint onStartCommand(Intent intent,int flags,int startId){
Log.i(TAG,"onStartCommand is running.Thread:"+Thread.currentThread());
Log.i(TAG,"flags:"+flags);
Log.i(TAG,"startId:"+startId);
returnsuper.onStartCommand(intent, flags, startId);
}
11-1602:56:28.3859039-9039/com.wanghx.androidstudy I/--FirstService-->: onStartCommand is running.Thread:Thread[main,5,main]
11-1602:56:28.3859039-9039/com.wanghx.androidstudy I/--FirstService-->: flags:0
11-1602:56:28.3859039-9039/com.wanghx.androidstudy I/--FirstService-->: startId:2
11-1602:56:33.9859039-9039/com.wanghx.androidstudy I/--FirstService-->: onStartCommand is running.Thread:Thread[main,5,main]
11-1602:56:33.9859039-9039/com.wanghx.androidstudy I/--FirstService-->: flags:0
11-1602:56:33.9859039-9039/com.wanghx.androidstudy I/--FirstService-->: startId:3
11-1602:56:35.5359039-9039/com.wanghx.androidstudy I/--FirstService-->: onStartCommand is running.Thread:Thread[main,5,main]
11-1602:56:35.5359039-9039/com.wanghx.androidstudy I/--FirstService-->: flags:0
11-1602:56:35.5359039-9039/com.wanghx.androidstudy I/--FirstService-->: startId:4
- 如何啟動一個綁定服務
- 在activity中創建一個內部類,繼承ServiceConnection.
classMyServiceConnectionimplementsServiceConnection{
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
Log.i(TAG,"onServiceConnected");
}
@Override
publicvoid onServiceDisconnected(ComponentName name){
Log.i(TAG,"onServiceDisconnected");
}
}
- 在activity中定義一個成員connection
privateMyServiceConnection connection =newMyServiceConnection();
- 在綁定服務按鈕中加入綁定代碼
Intent intent3 =newIntent(ServiceActivity.this,FirstService.class);
bindService(intent3, connection, BIND_AUTO_CREATE);
I/--FirstService-->:Service is running.
I/--FirstService-->: onCreate is running.
I/--FirstService-->:IBinder is running.
- 在解綁服務中加入代碼,這裡的connection必須和上邊的綁定服務的connection實例一致.
unbindService(connection);
- Service和Thread的關係
- 其實他兩個沒有一毛錢關係.只是因為service需要做耗時操作,需要重新建立一線程來處理工作,而不阻塞主線程;
- service是運行在主線程的;
- activity啟動service後,即使activity被銷毀了,如果沒有主動關閉服務,服務還是會在後臺默默運行的;
- 如何連接遠程的service,只需要在manifests.xml中這樣寫即可
<service
android:name="com.example.servicetest.MyService"
android:process=":remote">
</service>
- AIDL:Android Interface Definition Language:Android介面定義語言,它可以用於讓多個service與多個應用程式組件之間進行跨進程通信;
- 這些都不是重點,我們還是弄一下在我們自己的程式中service與activity之間的通信吧;
- activity-->service 通過intent傳遞數據給service;
- activity調用onServiceConnected()中的IBind對象來訪問service中的方法;
- IBinder通信的關鍵是利用activity中的IBinder對象獲得service對象,然後調用方法;
publicclassFirstServiceextendsService{
privatestaticfinalString TAG ="FirstService-->";
privateMyBinder myBinder =newMyBinder();
publicFirstService(){
Log.i(TAG,"Service is running.");
}
@Override
publicvoid onCreate(){
Log.i(TAG,"onCreate is running.");
super.onCreate();
}
@Override
publicint onStartCommand(Intent intent,int flags,int startId){
String name = intent.getStringExtra("name");
Log.i(TAG,"onStartCommand is running.Thread:"+Thread.currentThread());
Log.i(TAG,"flags:"+flags);
Log.i(TAG,"startId:"+startId);
Log.i(TAG,"name:"+name);
return START_STICKY;
}
@Override
publicvoid onDestroy(){
Log.i(TAG,"onDestroy is running.");
super.onDestroy();
}
@Override
publicIBinder onBind(Intent intent){
Log.i(TAG,"IBinder is running.");
return myBinder;
}
publicclassMyBinderextendsBinder{
publicFirstService getService(){
returnFirstService.this;
}
}
publicint getRandomNumber(){
returnnewRandom().nextInt(10)+1;
}
}
publicclassServiceActivityextendsAppCompatActivityimplementsView.OnClickListener{
privateMyServiceConnection connection =newMyServiceConnection();
privatestaticfinalString TAG ="ServiceActivity-->";
privateFirstService mFirstService;
privateboolean isBinder;// 服務是否綁定
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
findViewById(R.id.btn_start_service).setOnClickListener(this);
findViewById(R.id.btn_stop_service).setOnClickListener(this);
findViewById(R.id.btn_bound_service).setOnClickListener(this);
findViewById(R.id.btn_unbound_service).setOnClickListener(this);
findViewById(R.id.btn_get_number).setOnClickListener(this);
}
@Override
publicvoid onClick(View v){
switch(v.getId()){
case R.id.btn_start_service:
Intent intent =newIntent(ServiceActivity.this,FirstService.class);
intent.putExtra("name","Zhangsan");
startService(intent);
break;
case R.id.btn_stop_service:
Intent intent2 =newIntent(ServiceActivity.this,FirstService.class);
stopService(intent2);
break;
case R.id.btn_bound_service:
if(!isBinder){
Intent intent3 =newIntent(ServiceActivity.this,FirstService.class);
bindService(intent3, connection, BIND_AUTO_CREATE);
isBinder =true;
}
break;
case R.id.btn_unbound_service:
if(isBinder){
unbindService(connection);
isBinder =false;
}
break;
case R.id.btn_get_number:
if(mFirstService ==null){
Toast.makeText(getApplicationContext(),"請先綁定服務",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(),"得到的隨機數為:"+ mFirstService.getRandomNumber(),Toast
.LENGTH_SHORT).show();
}
break;
}
}
classMyServiceConnectionimplementsServiceConnection{
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
Log.i(TAG,"onServiceConnected");
FirstService.MyBinder myBinder =(FirstService.MyBinder) service;
mFirstService = myBinder.getService();
}
@Override
publicvoid onServiceDisconnected(ComponentName name){
Log.i(TAG,"onServiceDisconnected");
}
}
}