Service作為Android的服務組件,默默地在后台為整個程序服務,輔助應用與系統中的其他組件或系統服務進行溝通。它跟Activity的級別差不多,但不能自己運行只能后台運行。service可以在很多場合的應用中使用,比如播放多媒體的時候用戶啟動了其他Activity這個時候程序要在后台繼續播放,比如檢測SD卡上文件的變化,再或者在后台記錄你地理信息位置的改變等等, 總之服務總是藏在后台的。
ps:Service運行在主線程中的,所以用它做耗時工作時,同樣會阻塞進程,所以最好是在service里開個子線程。
一、注冊Service
service的注冊跟activity的注冊類似,同樣是要在AndroidManifest.xml的文件里注冊。
<service android:name=".MinaService"><!-- 你自定義的service文件 (在<application></application>里面加)--> <intent-filter> <action android:name="com.MinaService" /><!-- 用intent啟動時的快捷名(也可以用常規的方式啟動) --> <category android:name="android.intent.category.default" /> </intent-filter> </service>
二、Service的兩種模式
service有兩種模式,本地服務和遠程服務。我們一般開發應用都是用的本地服務,而遠程服務經常在做系統開發時被用到。所以今天我會主要講本地的服務,遠程服務放着以后再講吧(其實UP主也沒用過遠程服務就是了==)
類別 | 區別 | 優點 | 缺點 | 應用 |
本地服務(Local) | 該服務依附在主進程上, | 服務依附在主進程上而不是獨立的進程,這樣在一定程度上節約了資源,另外Local服務因為是在同一進程因此不需要IPC,也不需要AIDL。相應bindService會方便很多。 | 主進程被Kill后,服務便會終止。 | 非常常見的應用如:HTC的音樂播放服務,天天動聽音樂播放服務。 |
遠程服務(Remote) | 該服務是獨立的進程, | 服務為獨立的進程,對應進程名格式為所在包名加上你指定的android:process字符串。由於是獨立的進程,因此在Activity所在進程被Kill的時候,該服務依然在運行,不受其他進程影響,有利於為多個進程提供服務具有較高的靈活性。 | 該服務是獨立的進程,會占用一定資源,並且使用AIDL進行IPC稍微麻煩一點。 | 一些提供系統服務的Service,這種Service是常駐的。 |
三、Service的生命周期及兩種啟動方式
service的生命周期比activity簡單多了,原因是service是在后台運行的,它是一直運行的,所以不需要那么多的狀態判斷。它只繼承了onCreate()、onStart()或者說是onStartCommand()、
// 2.0 API level之后,實現onStart等同於重寫onStartCommand並返回 關於onStartCommon()的詳解:http://blog.csdn.net/lizzy115/article/details/7001731
onDestroy()三個方法。
服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。
- 使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運行。 如果打算采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。 采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
- 使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。
四、Service實例
接下來我會寫一個關於service的實例demo,希望看了之后會對大家有所幫助。
- 先新建一個項目,並新建一個class文件,命名為MinaService,繼承於service(不要忘記在AndroidManifest.xml文件里注冊),其中代碼如下:
1 public class MinaService extends Service{ 2 private String msg = "service bind success"; 3 4 private final IBinder mBinder = new LocalBinder(); 5 6 @Override 7 public IBinder onBind(Intent intent) { 8 Log.d("TEST", "onbind"); 9 //onBind service時會調用此方法,用於綁定Activity 10 return mBinder; 11 } 12 13 @Override 14 public void onCreate() { 15 //開啟服務時會首先調用該方法 16 Log.d("TEST", "onCreate"); 17 super.onCreate(); 18 } 19 20 @Override 21 public int onStartCommand(Intent intent, int flags, int startId) { 22 //start service時會在onCreate后調用該方法 23 Log.d("TEST", "start"); 24 return START_STICKY; 25 } 26 27 @Override 28 public void onDestroy() { 29 // 停止service后會調用此方法 30 Log.d("TEST", "destroy"); 31 super.onDestroy(); 32 } 33 34 @Override 35 public boolean onUnbind(Intent intent) { 36 //取消Activity與service綁定時要調用此方法 37 Log.d("TEST", "onunbind"); 38 return super.onUnbind(intent); 39 } 40 41 @Override 42 public void onRebind(Intent intent) { 43 // 重新綁定時調用此方法 44 Log.d("TEST", "onrebind"); 45 super.onRebind(intent); 46 } 47 48 49 /** 50 * @author cpacm 51 * 通過Binder返回service的引用到Activity中 52 */ 53 public class LocalBinder extends Binder { 54 MinaService getService() { 55 return MinaService.this; 56 } 57 } 58 59 //普通方法,證明service在后台運行 60 public String getMsg(){ 61 return msg; 62 } 63 public void setMsg(String m){ 64 this.msg = m; 65 } 66 67 }
- 回到MainActivity中,來看一下我們需要做的處理
1 public class MainActivity extends Activity implements OnClickListener{ 2 3 private MinaService ms = null; 4 private Button b1,b2,b3,b4,b5,b6; 5 private TextView textView; 6 private EditText editText; 7 @Override 8 protected void onCreate(Bundle savedInstanceState) { 9 super.onCreate(savedInstanceState); 10 setContentView(R.layout.activity_main); 11 //獲得界面的控件 12 textView = (TextView) findViewById(R.id.textView1); 13 editText = (EditText) findViewById(R.id.editText1); 14 b1 = (Button) findViewById(R.id.button1); 15 b1.setOnClickListener(this); 16 b2 = (Button) findViewById(R.id.button2); 17 b2.setOnClickListener(this); 18 b3 = (Button) findViewById(R.id.button3); 19 b3.setOnClickListener(this); 20 b4 = (Button) findViewById(R.id.button4); 21 b4.setOnClickListener(this); 22 b5 = (Button) findViewById(R.id.button5); 23 b5.setOnClickListener(this); 24 b6 = (Button) findViewById(R.id.button6); 25 b6.setOnClickListener(this); 26 Log.d("TEST","===初始化完成==="); 27 } 28 29 @Override 30 public void onClick(View v) { 31 // TODO Auto-generated method stub 32 switch(v.getId()){ 33 case R.id.button1:{//按鈕一:用startService開啟服務 34 Intent i = new Intent(); 35 i.setClass(MainActivity.this, MinaService.class); 36 startService(i); 37 break; 38 } 39 case R.id.button2:{//按鈕二:停止服務 40 Intent i = new Intent(); 41 i.setClass(MainActivity.this, MinaService.class); 42 stopService(i); 43 break; 44 } 45 case R.id.button3:{//按鈕三:用bindService來綁定Service和Activity 46 bindService(new Intent(MainActivity.this, 47 MinaService.class), mConnection, Context.BIND_AUTO_CREATE); 48 break; 49 } 50 case R.id.button4:{//取消綁定 51 unbindService(mConnection); 52 break; 53 } 54 case R.id.button5:{//跳轉到下一個Activity 55 Intent i = new Intent(); 56 i.setClass(MainActivity.this, SecondActivity.class); 57 startActivity(i); 58 59 break; 60 } 61 case R.id.button6:{//顯示Service里面的信息 62 textView.setText(ms.getMsg()); 63 ms.setMsg(editText.getText().toString()); 64 break; 65 } 66 } 67 } 68 69 70 public void show(String str){ 71 Toast.makeText(this, str, Toast.LENGTH_LONG).show(); 72 } 73 74 private ServiceConnection mConnection = new ServiceConnection(){ 75 76 77 /** 78 * 綁定Service和Activity時會用到這個函數,所以可以在這里獲取到Service的引用對象 79 * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName, android.os.IBinder) 80 **/ 81 @Override 82 public void onServiceConnected(ComponentName name, IBinder service) { 83 //獲取Service的引用對象 84 ms = ((MinaService.LocalBinder)service).getService(); 85 Toast.makeText(MainActivity.this, "connect", 86 Toast.LENGTH_SHORT).show(); 87 } 88 89 /** 90 * 這個函數是在綁定異常時調用,平時不會使用到這個函數 91 * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName) 92 **/ 93 @Override 94 public void onServiceDisconnected(ComponentName name) { 95 // TODO Auto-generated method stub 96 ms = null; 97 Toast.makeText(MainActivity.this, "cutdown", 98 Toast.LENGTH_SHORT).show(); 99 } 100 101 }; 102 103 }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="57dp" android:text="TextView" /> <EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="50dp" android:ems="10" > <requestFocus /> </EditText> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="start service" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="stop service" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="bind service" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="unbind service" /> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="next activity" /> <Button android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="set message" /> </LinearLayout> </LinearLayout>
case R.id.button5:{ Intent i = new Intent(); i.setClass(SecondActivity.this, MainActivity.class); startActivity(i); break; }
以上就是Demo里面的組成部分了。接下來我們來看一下運行結果。
- 首先我們先啟動項目,按下第一個按鈕(start service),我們可以看到在LogCat里打印出
可以看出項目先后調用了onCreate()和onStartCommand()。接下來按下第二個按鈕(stop service)
發現直接將service給destroy掉了。
接下來我們用BindService來啟動Service
依次按下第三個和第四個按鈕,結果如圖
可以發現BindService不會調用onStartCommand()方法,它會使用onbind來代替。unbind后,Service也是會摧毀。
public boolean bindService(Intent service, ServiceConnection conn, int flags)
該方法的第1個參數表示與服務類相關聯的Intent對象,第2個參數是一個ServiceConnection類型的變量,負責連接Intent對象指定的服務。通過ServiceConnection對象可以獲得連接成功或失敗的狀態,並可以獲得連接后的服務對象。第3個參數是一個標志位,一般設為 Context.BIND_AUTO_CREATE。 -
如果我們想保持和 Service 的通信,又不想讓 Service 隨着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。當你不需要綁定的時候就執行 unbindService() 方法,執行這個方法只會觸發 Service 的 onUnbind() 而不會把這個 Service 銷毀。這樣就可以既保持和 Service 的通信,也不會隨着 Activity 銷毀而銷毀了。(銷毀Service還是要調用StopService)
在應用中,我們先按下第一個按鈕,再按下第三個按鈕,這樣activity就獲得了service的對象。現在我們改變service里的值,在輸入框輸入“Activity與Service通信”,按下第六個按鈕。
然后按下第五個按鈕跳轉到下一個Activity。在跳轉到的Activity中,我們按下第三個按鈕(onBind),在按下第六個按鈕,你會發現文本框會出現“Activity與Service通信”的字符串。
從截圖上看,第二個activity成功獲得了service里面的值。(注意,跳轉到下一個Activity前一定要unbind,不然會報錯) - 關於service的一些技巧:
(1)可以在一個Activity啟動Service,再在另一個Activity綁定。
(2)可以多次start同一個Service(通過該方法,可以經由intent傳遞信息到Service中)
(3)start->bind->stop,此時service還是存在的不會被銷毀,但在調用unbind后,Service會被銷毀。
可能還會有更多的可能性組合,大家可以自己寫個demo來好好研究。
五、結語
Service不僅可以與前端界面組件建立雙向連接、提供數據和功能支持,也可以單向接受Intent對象的請求,進行數據的分析處理和功能調度。在不同的使用方式下,Service服務組件扮演的角色和開發模式完全不同。這種設計,也為理解Service帶來了一定的難度。所以理解和習慣Service的使用顯得非常重要。 參考文章:(1)Android Service學習之本地服務 http://android.blog.51cto.com/268543/527314/ DEMO下載(百度網盤)(過期的請CALL我) ======================================== 作者:cpacm出處:(http://www.cpacm.net/2015/03/22/Android開發日記(三)——Android四大組件之Service/)