Android 四大組件之Service(上)


1.Service簡介

  Service是Android四大組件中最與Activity相似的組件,他們都代表可執行的程序。Service一直運行於后台,不會與用戶交互,可用來處理一些耗時的任務(比如:后台播放音樂,I/O操作等)。它的創建、配置與Activity基本相似,下面將詳細介紹Android Service的開發。

2.創建、配置Service

  2.1 定義一個繼承Service類的子類

  2.2 在AndroidManifest.xml中配置該Service

  需要注意的是 Service和Activity都是從Context類派生出來的(建議可以了解下Context這個類,了解之后,學習后面的一些組件、類也是很輕松的),因此它們都可以調用Context中getResources()、getContentResolver()等方法;

  Service中定義的系列生命周期的方法:

    IBinder onBind(Intent intent):該方法是Service必須實現的一個方法,該方法返回一個IBinder對象,應用程序可以通過該對象與Service組件通訊

    void onCreate():當Service第一次被創建后,系統將立即回調該方法

    void onDestory():當Service被關閉之前會回調該方法

    void onStartCommand(Intent intent,int flags,int startId):該方法的早期版本(pre-2.0)是onStart(Intent intent,int startId)【onStartCommand(intent,flags,startId)里面已經調用了onStart()方法】,每次客戶端調用startService(Intent intent)啟動Service都會回調該方法。下面是Service類中該方法的源碼:

1 @Override
2     public int onStartCommand(Intent intent, int flags, int startId) {
3         onStart(intent, startId);
4         return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
5     }

    boolean onUnbind(Intent intent):當該Service上綁定的客戶端都斷開鏈接時,會回調該方法

    下面的類定義了一個Service組件:

 1 package com.example.administrator.servicedemo;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 
 7 /**
 8  * Created by Administrator on 2015/2/23.
 9  */
10 public class ServiceTest  extends Service{
11     //Service必須實現的方法
12     @Override
13     public IBinder onBind(Intent intent) {
14         return null;
15     }
16     //Service第一次被創建的時回調該方法
17     @Override
18     public void onCreate() {
19         super.onCreate();
20     }
21     //Service被啟動時回調該方法
22     @Override
23     public int onStartCommand(Intent intent, int flags, int startId) {
24         return super.onStartCommand(intent, flags, startId);
25     }
26     //Service被關閉之前回調該方法
27     @Override
28     public void onDestroy() {
29         super.onDestroy();
30     }
31     
32 }
View Code

    定義了Service之后,接下來需要在AndroidManifest.xml中配置該Service,配置Service使用<Service .../>元素

    

1 <service android:name=".ServiceTest" >
2             <intent-filter>
3                 <!-- 為Service組件的intent-filter配置action -->
4                 <action android:name="com.example.administrator.servicedemo.SERVICE_TEST"></action>
5             </intent-filter>
6         </service>

3.啟動和停止Service

  Android系統啟動Service有兩種方式:

    > 通過Context的startService(Intent intent)方法:這種方法啟動的Service,訪問者和Service之間沒有關聯,即使訪問者退出了,該Service仍然運行。(停止Service:stopService(Intent intent))

    > 通過Context的bindService(Intent intent)方法:這種方法啟動的Service,訪問者和Service綁定在了一起,一旦訪問者退出,該Service也會停止。(取消綁定Service:unBind(Intent intent))

4.綁定本地Service並與之通訊

  當程序通過startService()和stopService()啟動和停止Service時,訪問者和Service之間基本沒有什么聯系,因此它們之間也就無法進行通訊、數據交換。

  如果訪問者和Service之間需要進行方法調用或數據交換,那么這時應該使用bindService()和unbindService()進行啟動和關閉Service。

  Context 的bindService()方法的完整方法簽名為:bindService(Intent intent,ServiceConnection conn,int flags),該方法的三個參數解釋如下:

    Intent:該參數通過Intent啟動指定的Service

    conn:該參數是一個ServiceConnection對象,該對象用於監聽訪問者和Service之間的鏈接情況。當訪問者與Service之間鏈接成功時,將回調該對象的onServiceConnected(ComponentName name,IBinder service)方法,當Service所在的宿主線程因異常終止,或其它異常終止導致訪問者與Service之間斷開鏈接時回調該對象的onServiceDisconnected(ComponentName name)方法

    注意:當訪問者主動通過unBindService()方法斷開與Service的鏈接時ServiceConnection的onServiceDisconnected(ComponnentName name)並不會被調用。

    flags:指定綁定時是否自動創建Service,該參數可指定為0(不創建)或BIND_AUTO_CREATE(自動創建)

    該ServiceConnection的onServiceConnected方法中有一個IBinder對象,該對象可以實現與被綁定Service之間的通訊,下面程序示范如何在本地綁定Service,並和Service進行通訊

    MainActivity類主要用於啟動、關閉Service和顯示BindService類中的count

 1 package com.example.administrator.servicedemo;
 2 
 3 import android.content.ComponentName;
 4 import android.content.Intent;
 5 import android.content.ServiceConnection;
 6 import android.os.IBinder;
 7 import android.support.v7.app.ActionBarActivity;
 8 import android.os.Bundle;
 9 import android.util.Log;
10 import android.view.View;
11 import android.widget.Toast;
12 
13 
14 public class MainActivity extends ActionBarActivity {
15     private static final String SERVICE_BINDER = "com.example.administrator.servicedemo.SERVICE_BINDER";
16     public static final String TAG = "MainActivity";
17     private BindService.MyBind myBind;//聲明BindService類中內部類MyBind
18     @Override
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main);
22     }
23 
24     //訪問者與Service通信的橋梁
25     private ServiceConnection conn = new ServiceConnection() {
26         //連接成功
27         @Override
28         public void onServiceConnected(ComponentName name, IBinder service) {
29             Log.i(TAG,"connection is succeed");
30             myBind = (BindService.MyBind)service;
31         }
32         //因異常而斷開鏈接
33         @Override
34         public void onServiceDisconnected(ComponentName name) {
35             Log.i(TAG,"connection is failed");
36         }
37     };
38     //啟動service
39     public void startService(View view){
40         Intent intent = new Intent();
41         intent.setAction(SERVICE_BINDER);
42         bindService(intent,conn,BIND_AUTO_CREATE);
43     }
44     //解除綁定
45     public void stopService(View view){
46         Intent intent = new Intent();
47         intent.setAction(SERVICE_BINDER);
48         unbindService(conn);
49     }
50     //實時顯示count
51     public void showCount(View view){
52         Toast.makeText(MainActivity.this,"從BindService獲取到的count:"+myBind.getCount(),Toast.LENGTH_SHORT).show();
53     }
54 
55 }
View Code

    BindService類中啟動一個新線程對count自增  

 1 package com.example.administrator.servicedemo;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Binder;
 6 import android.os.IBinder;
 7 
 8 /**
 9  * Created by Administrator on 2015/2/23.
10  */
11 public class BindService extends Service {
12     private int count;
13     private boolean quit;
14     //自定義一個Binder
15     class MyBind extends Binder{
16         public int getCount(){
17             return count;//實時獲取count的值
18         }
19     }
20     @Override
21     public IBinder onBind(Intent intent) {
22         return new MyBind();//返回給訪問者的Binder
23     }
24 
25     @Override
26     public void onCreate() {
27         new Thread(new Runnable() {
28             @Override
29             public void run() {
30                 while (!quit){
31                     try {
32                         Thread.sleep(2000);
33                     } catch (InterruptedException e) {
34                         e.printStackTrace();
35                     }
36                     count++;
37                 }
38             }
39         }).start();
40     }
41 
42     @Override
43     public void onDestroy() {
44         super.onDestroy();
45         this.quit = true;
46     }
47 }
View Code

    layout布局文件中添加了啟動、關閉、顯示的按鈕

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 3     android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
 4     android:paddingRight="@dimen/activity_horizontal_margin"
 5     android:paddingTop="@dimen/activity_vertical_margin"
 6     android:orientation="vertical"
 7     android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
 8 
 9     <Button
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content"
12         android:text="start service"
13         android:onClick="startService"/>
14     <Button
15         android:layout_width="wrap_content"
16         android:layout_height="wrap_content"
17         android:text="stop service"
18         android:onClick="stopService"/>
19 
20     <Button
21         android:layout_width="wrap_content"
22         android:layout_height="wrap_content"
23         android:text="show count"
24         android:onClick="showCount"/>
25 </LinearLayout>
View Code

    程序運行后的結果:

      

    點擊 start service 后將啟動BindService,可通過BindService的MyBind內部類的getCount()方法獲取當前count的值

    點擊show count

         

    

    對於Service中onBind()方法所返回的Ibinder對象來說,他可被當成該Service組件所返回的代理對象,Service允許客戶端通過IBinder來訪問Service內部數據,這樣可以實現客戶端與Service之間的通信。

    當程序調用unBindService()方法解除對Service的綁定時,系統先回調onUnbind()方法,然后在回調onDestroy()方法.

    與多次調用startService()方法啟動Service不同的是,多次調用bindService()方法並不會執行重復綁定。而前一個程序,只要用戶點擊啟動Service,系統就會回調onStartCommand()方法一次.對於這個示例,不管用戶點擊多少次綁定Service,系統都只會回調一次onBind()方法。

5. Service的聲明周期:

  下圖分別是startService()、bindService()兩種方式啟動Service的生命周期

  

   這里還有一種特殊的情況:

    如果Service已由某個客戶端通過startService()啟動了,接下來其它客戶端再調用bindService()方法綁定到Service上,再調用unbindService()解除綁定,最后有調用bindService()方法再次綁定到這個Service,這個過程所出發的生命周期如下:

    onCreate()-->onStartCommand()-->onBind()-->onUnbind()[重寫該方法時返回true]-->onRebind()

    要想onRebind()方法被回調,除了已startService()方法啟動Service之外,還要在重寫Service的onUnbind()方法時返回true.

    從上面可以看到整個過程中並沒有調用onDestroy()方法,這是因為該Service是以startService()方式啟動的而不是以bindService()方法啟動,因此當Activity調用unBindService()方法取消與Service的綁定時,該Service也不會終止

    由此可見,當Activity通過bindService()綁定一個以啟動的Service時,系統只是把該Service的IBinder對象傳給Activity,並不會將Service的生命周期"綁定"到該Activity

,因此當Activity調用unBindService()方法取消與Service的綁定時,也只是切斷該Activity與Service之間的關聯,並不能停止Service組件。

6. 使用IntentService

  IntentService 類是Service類的子類,它比普通的Service類增加了額外的功能

  先看下Service本身存在的兩個問題.

    1>Service不會專門啟動一條單獨的進程,Service與它所在應用位於同一個進程中

    2>Service不是專門一條新的線程,因此不能在Service中處理耗時的任務

  而IntentService可以解決上述兩個不足:IntentService會使用隊列來管理請求Intent,每當客戶端通過Intent請求啟動IntentService時,IntentService會將請求放入到隊列,然后開啟一條新的worker線程來處理intent.對於異步的startService()請求,IntentService會按次序依次處理隊列中的Intent請求,該線程保證同一時刻只處理一個Intent。由於IntentService使用worker線程處理耗時請求,因此不會阻塞主線程。

  歸納起來,IntentService具有如下特征:

    1>IntentService會創建單獨的worker線程來處理所有的Intent請求

    2>IntentService會創建單獨的worker線程來處理onHandlerIntent()方法實現的代碼,開發者無需處理多線程問題

    3>當所有請求處理完之后,IntentService會自動停止,因此開發者無需調用stopSelf()方法停止該Service

    4>為Service的onBind()方法提供了默認實現,默認是實現的onBind()方法返回null

    5>為Service的onStartCommand()方法提供了默認實現,該實現會將請求Intent添加到隊列中

  因此,在重寫IntentService類時只需重寫onHandlerIntent()方法即可。

  下面寫一個demo測試一下

  MainActivity類

 1 package com.example.administrator.intentservicetest;
 2 
 3 import android.content.Intent;
 4 import android.support.v7.app.ActionBarActivity;
 5 import android.os.Bundle;
 6 import android.view.Menu;
 7 import android.view.MenuItem;
 8 import android.view.View;
 9 import android.widget.Toast;
10 
11 
12 public class MainActivity extends ActionBarActivity {
13 
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18     }
19 
20     public void startService(View view){
21         Intent intent = new Intent(this,MyService.class);
22         Toast.makeText(this,"您點擊了--啟動service按鈕",Toast.LENGTH_LONG).show();
23         startService(intent);
24     }
25 
26     public void startIntentService(View view){
27         Intent intent = new Intent(this,MyIntentService.class);
28         Toast.makeText(this,"您點擊了--啟動intentService按鈕",Toast.LENGTH_LONG).show();
29         startService(intent);
30     }
31 
32 }
View Code

  MyService類

 1 package com.example.administrator.intentservicetest;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 import android.util.Log;
 7 
 8 /**
 9  * Created by Administrator on 2015/2/15.
10  */
11 public class MyService extends Service {
12     @Override
13     public IBinder onBind(Intent intent) {
14         return null;
15     }
16 
17     @Override
18     public void onCreate() {
19         super.onCreate();
20     }
21 
22     @Override
23     public int onStartCommand(Intent intent, int flags, int startId) {
24         long currentTime = System.currentTimeMillis();
25         long endTime = currentTime + 20*1000;
26         //模擬一個耗時操作
27         while (currentTime < endTime){
28             synchronized (this){
29                 try {
30                     wait(endTime - currentTime);
31                 } catch (InterruptedException e) {
32                     e.printStackTrace();
33                 }
34             }
35         }
36         return super.onStartCommand(intent, flags, startId);
37     }
38 }
View Code

  MyIntentService類

 1 package com.example.administrator.intentservicetest;
 2 
 3 import android.app.IntentService;
 4 import android.content.Intent;
 5 import android.util.Log;
 6 
 7 /**
 8  * Created by Administrator on 2015/2/15.
 9  */
10 public class MyIntentService extends IntentService {
11     public MyIntentService(String name) {
12         super("MyIntentService");
13     }
14 
15 
16 
17     @Override
18     protected void onHandleIntent(Intent intent) {
19         long currentTime = System.currentTimeMillis();
20         long endTime = currentTime + 20*1000;
21         //模擬一個耗時操作
22         while (currentTime < endTime){
23             synchronized (this){
24                 try {
25                     wait(endTime - currentTime);
26                 } catch (InterruptedException e) {
27                     e.printStackTrace();
28                 }
29             }
30         }
31     }
32 }
View Code

    運行應用程序后:               點擊啟動service,可以看到出現假死的狀況       過會彈出                                          點擊啟動IntentService

                                                                  

 

  總結:可以看出當點擊啟動service按鈕時,程序出現了假死狀況,這是因為Service中進行了耗時操作,前面說到Service是運行在主線程中的,所以該耗時任務阻塞了主線程。點擊啟動intentService時,未出現阻塞狀況,因為IntentService會啟動一個worker線程進行處理耗時操作,並不是在主線程中處理。所以,一般遇到耗時情況時,首先想到的是IntentService,這個類已經幫我們做了很好的處理,無需自己在Service中創建一個線程去處理耗時任務。

    


免責聲明!

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



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