【Android開發日記】之入門篇(五)——Android四大組件之Service


這幾天忙着駕校考試,連電腦都碰不到了,今天總算告一段落了~~
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,但是它們的使用場合有所不同。

  1. 使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運行。    如果打算采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。 采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
  2. 使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。

 四、Service實例

接下來我會寫一個關於service的實例demo,希望看了之后會對大家有所幫助。

  1. 先新建一個項目,並新建一個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 }
  2. 回到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>
    activity_main
    再新建一個SecondActivity(不要忘記在AndroidManifest.xml里注冊Activity),里面代碼基本和MainActivity里一樣,改一下這里
    case R.id.button5:{
        Intent i  = new Intent();  
         i.setClass(SecondActivity.this, MainActivity.class); 
        startActivity(i);
        break;
    }

    以上就是Demo里面的組成部分了。接下來我們來看一下運行結果。

  3. 首先我們先啟動項目,按下第一個按鈕(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。
  4. 如果我們想保持和 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,不然會報錯

  5. 關於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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM