Service的理解和使用

首先推薦一下郭林大神所講的這一節的博客:http://blog.csdn.net/guolin_blog/article/details/11952435
下面結合我對這一部分的學習,自己做一個小節。
Android5.0之後組件必須使用顯示intent來啓動,如果爲隱示的,則設置Intent的包名。intent.setPackage(“com.llay.admin.mydemo”);

一、基本概念

Service是一個應用程序組件,它能夠在後臺執行一些耗時較長的操作,並且不提供用戶界面。服務能被其它應用程序的組件啓動,即使用戶切換到另外的應用時還能保持後臺運行。此外,應用程序組件還能與服務綁定,並與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網絡傳輸、音樂播放、執行文件I/O、或者與content provider進行交互,所有這些都是後臺進行的。

二、服務的分類

服務有以下兩種基本類型:

Started (這裏有一個service的子類IntentService,待會兒單獨講解)
如果一個應用程序組件(比如一個activity)通過調用startService()來啓動服務,則該服務就是被“started”了。一旦被啓動,服務就能在後臺一直運行下去,即使啓動它的組件已經被銷燬了。
通常,started的服務執行單一的操作並且不會向調用者返回結果。比如,它可以通過網絡下載或上傳文件。當操作完成後,服務應該自行終止。

Bound
如果一個應用程序組件通過調用bindService()綁定到服務上,則該服務就是被“bound”了。bound服務提供了一個客戶端/服務器接口,允許組件與服務進行交互、發送請求、獲取結果,甚至可以利用進程間通信(IPC)跨進程執行這些操作。綁定服務的生存期和被綁定的應用程序組件一致。
多個組件可以同時與一個服務綁定,不過所有的組件解除綁定後,服務也就會被銷燬。

三、一些回調方法

服務的生命週期圖:

這裏寫圖片描述

爲了創建一個服務,你必須新建一個Service的子類(或一個已有Service的子類)。在你的實現代碼中,請按需重寫一些回調方法,用於對服務生命週期中的關鍵節點進行處理,以及向組件提供綁定機制。 最重要的需要重寫的回調方法包括:

onStartCommand()
當其它組件,比如一個activity,通過調用startService()請求started方式的服務時,系統將會調用本方法。 一旦本方法執行,服務就被啓動,並在後臺一直運行下去。 如果你的代碼實現了本方法,你就有責任在完成工作後通過調用stopSelf()或stopService()終止服務。 (如果你只想提供bind方式,那就不需要實現本方法。)

onBind()
當其它組件需要通過bindService()綁定服務時(比如執行RPC),系統會調用本方法。 在本方法的實現代碼中,你必須返回IBinder來提供一個接口,客戶端用它來和服務進行通信。 你必須確保實現本方法,不過假如你不需要提供綁定,那就返回null即可。

onCreate()
當服務第一次被創建時,系統會調用本方法,用於執行一次性的配置工作(之前已調用過onStartCommand()或onBind()) 了。如果服務已經運行,則本方法就不會被調用。

onDestroy()
當服務用不上了並要被銷燬時,系統會調用本方法。 你的服務應該實現本方法來進行資源的清理工作,諸如線程、已註冊的偵聽器listener和接收器receiver等等。 這將是服務收到的最後一個調用。

如果組件通過調用startService()(這會導致onStartCommand()的調用)啓動了服務,那麼服務將一直保持運行,直至自行用stopSelf()終止或由其它組件調用stopService()來終止它。

如果組件調用bindService()來創建服務(那onStartCommand()就不會被調用),則服務的生存期就與被綁定的組件一致。一旦所有客戶端都對服務解除了綁定,系統就會銷燬該服務。

僅當內存少得可憐、且必須覆蓋擁有用戶焦點的activity的系統資源時,Android系統纔會強行終止一個服務。 如果服務被擁有用戶焦點的activity綁定着,則它一般不會被殺死。 如果服務聲明爲#在前臺運行服務(下文討論),則它幾乎永遠不會被殺死。 否則,如果服務已被啓動並且已運行了很長時間,那麼系統將會隨時間推移而降低它在後臺任務列表中的級別, 此類服務將很有可能會被殺死——如果服務已經啓動,那你必須好好設計代碼,使其能完美地應付被系統重啓的情況。 如果系統殺死了你的服務,只要資源再度夠用,系統就會再次啓動服務(當然這還取決於onStartCommand()的返回值,下文將會述及)。關於系統可能會在何時銷燬服務的詳細信息,請參閱進程和線程。

四、代碼的實現

注意:Service是Android的四大組件,所以記得在Androidmanifests.xml文件中註冊。注意:四大組件只有BroadCast能動態註冊。

界面圖片:
這裏寫圖片描述

4、1 Started服務的代碼實現

started服務是指其它組件通過調用startService()來啓動的服務,這會引發對該服務onStartCommand()方法的調用。

一旦服務被啓動started,它就擁有了自己的生命週期,這是獨立於啓動它的組件的。並且它能夠在後臺一直運行下去,即使啓動它的組件已被銷燬 也是如此。 因此,服務應該能夠在完成工作後自行終止,通過調用stopSelf()即可,或者由其它組件通過調用stopService()也可以。

諸如activity之類的應用程序組件,可以通過調用startService()啓動服務,並傳入一個給出了服務和服務所需數據的Intent對象。服務將在onStartCommand()方法中接收到該Intent對象。

舉個例子,假定某activity需要把一些數據保存到在線數據庫中。此activity可以啓動一個守護服務並通過傳入startService()一個intent把需要保存的數據發送給該服務。該服務在onStartCommand()內接收intent,連接Internet,再進行數據庫事務處理。當事務完成後,服務自行終止,並被系統銷燬。

在Activity中代碼的方法:

<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initStartedService</span>() {
        <span class="hljs-keyword">final</span> Intent intent = <span class="hljs-keyword">new</span> Intent(ServiceDemoActivity.<span class="hljs-keyword">this</span>, HelloStartedService.class);
        startedService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(startedServiceTAG, <span class="hljs-string">"開啓服務"</span>);
                intent.putExtra(<span class="hljs-string">"StartedServiceTest"</span>, <span class="hljs-string">"StartedServiceTest"</span>);
                startService(intent);
            }
        });

        stopService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(stopServiceTAG, <span class="hljs-string">"手動停止服務"</span>);
                stopService(intent);
            }
        });
    }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>

HelloStartedService.java文件

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloStartedService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Service</span> {</span>
    <span class="hljs-keyword">private</span> Looper looper;
    <span class="hljs-keyword">private</span> StartedServiceHandler startedServiceHandler;
    <span class="hljs-keyword">public</span> String s;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> String TAG = <span class="hljs-string">"startedService"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-title">HelloStartedService</span>() {
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>() {
        <span class="hljs-comment">//這裏配置一些信息</span>
        <span class="hljs-comment">//啓動運行服務的線程。</span>
        <span class="hljs-comment">//請記住我們要創建一個單獨的線程,因爲服務通常運行於進程的主線程中,可我們不想阻塞主線程。</span>
        <span class="hljs-comment">//我們還要賦予它後臺運行的優先級,以便計算密集的工作不會干擾我們的UI。</span>
        HandlerThread handlerThread = <span class="hljs-keyword">new</span> HandlerThread(<span class="hljs-string">"StartedService"</span>);
        handlerThread.start();
        <span class="hljs-comment">//獲得HandlerThread的Looper隊列並用於Handler</span>
        looper = handlerThread.getLooper();
        startedServiceHandler = <span class="hljs-keyword">new</span> StartedServiceHandler(looper);
        Log.e(TAG, <span class="hljs-string">"onCreate"</span>);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">onStartCommand</span>(Intent intent, <span class="hljs-keyword">int</span> flags, <span class="hljs-keyword">int</span> startId) {
        Log.e(TAG, <span class="hljs-string">"onStartCommand"</span>);
        s = intent.getStringExtra(<span class="hljs-string">"StartedServiceTest"</span>);
        <span class="hljs-comment">//對於每一個啓動請求,都發送一個消息來啓動一個處理</span>
        <span class="hljs-comment">//同時傳入啓動ID,以便任務完成後我們知道該終止哪一個請求。</span>
        Message message = startedServiceHandler.obtainMessage();
        message.arg1 = <span class="hljs-number">1</span>;
        startedServiceHandler.sendMessage(message);
        <span class="hljs-comment">//如果我們被殺死了,那從這裏返回之後被重啓</span>
        <span class="hljs-keyword">return</span> START_STICKY;

    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDestroy</span>() {
        <span class="hljs-keyword">super</span>.onDestroy();
        Log.e(TAG, <span class="hljs-string">"onDestroy"</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StartedServiceHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span> {</span>
        <span class="hljs-keyword">public</span> <span class="hljs-title">StartedServiceHandler</span>(Looper looper) {
            <span class="hljs-keyword">super</span>(looper);
        }

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) {
            <span class="hljs-comment">//通常我們在這裏執行一些工作,比如下載文件。</span>
            <span class="hljs-keyword">try</span> {
                Log.e(TAG, <span class="hljs-string">"開始在服務中處理信息"</span>);
                Thread.sleep(<span class="hljs-number">3000</span>);
            } <span class="hljs-keyword">catch</span> (InterruptedException e) {
                e.printStackTrace();
            }
            <span class="hljs-comment">//根據startId終止服務,這樣我們就不會在處理其它工作的過程中再來終止服務</span>
            <span class="hljs-comment">//如果組件通過調用startService()(這會導致onStartCommand()的調用)啓動了服務,那麼服務將一直保持運行,直至自行用stopSelf()終止或由其它組件調用stopService()來終止它。</span>
            <span class="hljs-comment">//如果組件調用bindService()來創建服務(那onStartCommand()就不會被調用),則服務的生存期就與被綁定的組件一致。一旦所有客戶端都對服務解除了綁定,系統就會銷燬該服務。</span>
            stopSelf(msg.arg1);
            Log.e(TAG, <span class="hljs-string">"startedService處理數據完成後自動停止"</span>);
        }
    }


    <span class="hljs-comment">//不支持綁定,所以我們返回null</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> IBinder <span class="hljs-title">onBind</span>(Intent intent) {
        <span class="hljs-comment">// TODO: Return the communication channel to the service.</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
}</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li></ul>

注意:我這裏在StartedService中,使用了HandlerThread來實現在線程中處理事件。因爲這裏的服務還是在主線程中的,不能阻塞。

AndroidManifests.xml文件中註冊

<code class="hljs xml has-numbering"><span class="hljs-tag"><<span class="hljs-title">service
</span>            <span class="hljs-attribute">android:name</span>=<span class="hljs-value">"com.llay.admin.service.HelloStartedService"</span>
            <span class="hljs-attribute">android:enabled</span>=<span class="hljs-value">"true"</span>
            <span class="hljs-attribute">android:exported</span>=<span class="hljs-value">"true"</span> /></span></code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul>

運行圖:
打開服務:
這裏寫圖片描述
關閉服務(完成後自動關閉):
在處理過程後,有這樣一句代碼,自動關閉。
這裏寫圖片描述
這裏寫圖片描述
關閉服務(手動關閉):
在按鈕點擊事件中,手動關閉。
這裏寫圖片描述
這裏寫圖片描述

4、1、1 IntentService代碼實現

注意:默認情況下,運行服務的進程與應用程序的相同,並且運行在應用程序的主線程中。
因此,如果你的服務要執行計算密集或阻塞的操作,而同時用戶又需要與同一個應用程序中的activity進行交互,那麼服務將會降低activity的性能。
爲了避免對應用程序性能的影響,你應該在服務中啓動一個新的線程。

IntentService:異步處理服務,新開一個線程:handlerThread在線程中發消息,然後接受處理完成後,會清理線程,並且關掉服務。
IntentService有以下特點:

(1) 它創建了一個獨立的工作線程來處理所有的通過onStartCommand()傳遞給服務的intents。

(2) 創建了一個工作隊列,來逐個發送intent給onHandleIntent()。

(3) 不需要主動調用stopSelft()來結束服務。因爲,在所有的intent被處理完後,系統會自動關閉服務。

(4) 默認實現的onBind()返回null

(5) 默認實現的onStartCommand()的目的是將intent插入到工作隊列中

Activity中的方法代碼:

<code class="hljs java has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initIntentService</span>() {
        <span class="hljs-comment">//Android5.0之後必須使用顯示intent來啓動</span>
        <span class="hljs-comment">//Android的四大組件,只有BroadcastReceiver能夠動態在AndroidManifests文件中註冊</span>
        <span class="hljs-keyword">final</span> Intent intent = <span class="hljs-keyword">new</span> Intent(<span class="hljs-string">"com.llay.admin.service.action.llay"</span>).setPackage(<span class="hljs-string">"com.llay.admin.mydemo"</span>);
        startedIntentService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(startedServiceTAG, <span class="hljs-string">"開啓Intent服務"</span>);
                intent.putExtra(<span class="hljs-string">"IntentServiceTest"</span>, <span class="hljs-string">"IntentServiceTest"</span>);
                startService(intent);
            }
        });
        stopIntentService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(stopServiceTAG, <span class="hljs-string">"手動Intent停止服務"</span>);
                stopService(intent);
            }
        });
    }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul>

HelloIntentService.java文件

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloIntentService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">IntentService</span> {</span>

    <span class="hljs-javadoc">/**
     * 構造方法是必需的,必須用工作線程名稱作爲參數
     * 調用父類的[http://developer.android.com/reference/android/app/IntentService.html#IntentService(java.lang.String) IntentService(String)]構造方法。
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">HelloIntentService</span>() {
        <span class="hljs-keyword">super</span>(<span class="hljs-string">"HelloIntentService"</span>);
    }


    <span class="hljs-javadoc">/**
     * IntentService從缺省的工作線程中調用本方法,並用啓動服務的intent作爲參數。
     * 本方法返回後,IntentService將適時終止這個服務。
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onHandleIntent</span>(Intent intent) {
        String s = intent.getStringExtra(<span class="hljs-string">"IntentServiceTest"</span>);
        Log.e(<span class="hljs-string">"IntentServiceTest"</span>, s);
    }
}</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>

AndroidManifests.xml文件中註冊

<code class="hljs xml has-numbering"><span class="hljs-tag"><<span class="hljs-title">service
</span>            <span class="hljs-attribute">android:name</span>=<span class="hljs-value">"com.llay.admin.service.HelloIntentService"</span>
            <span class="hljs-attribute">android:enabled</span>=<span class="hljs-value">"true"</span>
            <span class="hljs-attribute">android:exported</span>=<span class="hljs-value">"true"</span> /></span></code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul>

運行圖:
開啓Intent服務:
這裏寫圖片描述
關閉Intent服務(這裏和上面started服務一樣,可以自動關閉,可以手動關閉):
這裏寫圖片描述
注意:這裏我們來分析一下Service和IntentService的區別和使用地方。

<code class="hljs scss has-numbering">利用IntentService來實現一個started服務非常簡單。 不過,假如你的服務需要多線程運行(而不是通過一個工作隊列來處理啓動請求),那你可以擴展Service類來完成每個intent的處理。如你所見,它要乾的事情比用IntentService時多了很多。不過,因爲是自行處理每個<span class="hljs-function">onStartCommand()</span>調用,你可以同時處理多個請求。 本例中沒有這麼去實現,但只要你願意,你就可以爲每個請求創建一個新的線程,並立即運行它們(而不是等待前一個請求處理完畢)。</code><ul class="pre-numbering" style=""><li>1</li></ul><ul class="pre-numbering" style=""><li>1</li></ul>

總結: 在當需要一起在線程(自己開)中處理,則使用Started服務。 在當需要一個一個在線程(自帶)中處理,則使用IntentService。

4、2 Bound服務的代碼實現

Activity中的方法代碼:

<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initBoundService</span>() {
        boundService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(boundServiceTAG, <span class="hljs-string">"綁定服務"</span>);
                <span class="hljs-comment">//綁定到LocalService</span>
                Intent intent = <span class="hljs-keyword">new</span> Intent(ServiceDemoActivity.<span class="hljs-keyword">this</span>, HelloBoundService.class);
                bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
                Log.e(<span class="hljs-string">"BoundServiceTest"</span>, <span class="hljs-string">"BoundServiceStart"</span>);
            }
        });

        unboundService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                Log.e(unBoundServiceTAG, <span class="hljs-string">"手動解除綁定"</span>);
                unbindService(serviceConnection);
                mBound = <span class="hljs-keyword">false</span>;
            }
        });

        showNumber.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                <span class="hljs-keyword">if</span> (mBound) {
                    <span class="hljs-comment">//調用LocalService中的方法。</span>
                    <span class="hljs-comment">//不過,如果該調用會導致某些操作的掛起,那麼調用應該放入單獨的線程中進行,</span>
                    <span class="hljs-comment">//以免降低activity的性能。</span>
                    <span class="hljs-keyword">int</span> num = helloBoundService.getRandomNumber();
                    Toast.makeText(ServiceDemoActivity.<span class="hljs-keyword">this</span>, <span class="hljs-string">"number: "</span> + num, Toast.LENGTH_SHORT).show();
                }
            }
        });

        serviceConnection = <span class="hljs-keyword">new</span> ServiceConnection() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onServiceConnected</span>(ComponentName name, IBinder service) {
                <span class="hljs-comment">//我們已經綁定到LocalService了,對IBinder進行類型轉換(cast)並獲得LocalService對象的實例</span>
                HelloBoundService.LocalBinder localBinder = (HelloBoundService.LocalBinder) service;
                helloBoundService = localBinder.getHelloBoundService();
                mBound = <span class="hljs-keyword">true</span>;
            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onServiceDisconnected</span>(ComponentName name) {
                mBound = <span class="hljs-keyword">false</span>;
            }
        };
    }


    <span class="hljs-comment">//以下是綁定服務的一些回調函數</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onStart</span>() {
        <span class="hljs-keyword">super</span>.onStart();
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onStop</span>() {
        <span class="hljs-keyword">super</span>.onStop();
        <span class="hljs-comment">//與服務解除綁定</span>
        <span class="hljs-keyword">if</span> (mBound) {
            unbindService(serviceConnection);
            mBound = <span class="hljs-keyword">false</span>;
        }
        Log.e(unBoundServiceTAG, <span class="hljs-string">"自動解除綁定"</span>);
    }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li></ul>

HelloBoundService.java文件:

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloBoundService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Service</span> {</span>
    <span class="hljs-comment">//給客戶端的Binder</span>
    <span class="hljs-keyword">public</span> IBinder mBinder = <span class="hljs-keyword">new</span> LocalBinder();
    <span class="hljs-comment">//生成隨機數</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Random mGenerator = <span class="hljs-keyword">new</span> Random();


    <span class="hljs-javadoc">/**
     * 用於客戶端Binder的類。
     * 因爲知道本服務總是運行於與客戶端相同的進程中,我們就不需要用IPC進行處理。
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LocalBinder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Binder</span> {</span>
        <span class="hljs-keyword">public</span> HelloBoundService <span class="hljs-title">getHelloBoundService</span>() {
            <span class="hljs-keyword">return</span> HelloBoundService.<span class="hljs-keyword">this</span>;
        }
    }

    <span class="hljs-annotation">@Nullable</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> IBinder <span class="hljs-title">onBind</span>(Intent intent) {
        <span class="hljs-keyword">return</span> mBinder;
    }

    <span class="hljs-javadoc">/**
     * method for clients
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getRandomNumber</span>() {
        <span class="hljs-keyword">return</span> mGenerator.nextInt(<span class="hljs-number">100</span>);
    }
}</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul>

注意:如果你的服務只用於本地應用程序並且不需要跨進程工作,那你只要實現自己的 Binder
類即可,這樣你的客戶端就能直接訪問服務中的公共方法了。
注意:僅當客戶端和服務位於同一個應用程序和進程中,這也是最常見的情況,這種方式纔會有用。比如,一個音樂應用需要把一個activity綁定到它自己的後臺音樂播放服務上,採用這種方式就會很不錯。
以下是設置步驟: 1、在你的服務中,創建一個Binder的實例,其中實現以下三者之一:

  • 包含了可供客戶端調用的公共方法
  • 返回當前Service實例,其中包含了可供客戶端調用的公共方法。
  • 或者,返回內含服務類的其它類的一個實例,服務中包含了可供客戶端調用的公共方法。

2、從回調方法onBind()中返回Binder的該實例。
3、在客戶端中,在回調方法onServiceConnected()中接收Binder並用所提供的方法對綁定的服務進行調用。 注意:
服務和客戶端之所以必須位於同一個應用程序中,是爲了讓客戶端能夠正確轉換(cast)返回的對象並調用對象的API。
服務和客戶端也必須位於同一個進程中,因爲這種方式不能執行任何跨進程的序列化(marshalling)操作。

AndroidManifests.xml文件中註冊

<code class="hljs xml has-numbering"><span class="hljs-tag"><<span class="hljs-title">service
</span>            <span class="hljs-attribute">android:name</span>=<span class="hljs-value">"com.llay.admin.service.HelloBoundService"</span>
            <span class="hljs-attribute">android:enabled</span>=<span class="hljs-value">"true"</span>
            <span class="hljs-attribute">android:exported</span>=<span class="hljs-value">"true"</span> /></span></code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul>

運行圖:
綁定服務:
這裏寫圖片描述
解絆服務:
這裏寫圖片描述

五、擴展

創建一個支持綁定的服務時,你必須提供一個 IBinder ,用作客戶端和服務間進行通信的編程接口。定義這類接口的方式有三種:

擴展Binder類
如果服務是你的應用程序所私有的,並且與客戶端運行於同一個進程中(通常都是如此),你應該通過擴展Binder類來創建你的接口,並從onBind()返回一個它的實例。客戶端接收該Binder對象並用它來直接訪問Binder甚至Service中可用的公共(public)方法。
如果你的服務只是爲你自己的應用程序執行一些後臺工作,那這就是首選的技術方案。不用這種方式來創建接口的理由只有一個,就是服務要被其它應用程序使用或者要跨多個進程使用。

使用Messenger
如果你需要接口跨越多個進程進行工作,可以通過Messenger來爲服務創建接口。在這種方式下,服務定義一個響應各類消息對象Message的Handler。此Handler是Messenger與客戶端共享同一個IBinder的基礎,它使得客戶端可以用消息對象Message向服務發送指令。此外,客戶端還可以定義自己的Message,以便服務能夠往回發送消息。
這是執行進程間通信(IPC)最爲簡便的方式,因爲Messenger會把所有的請求放入一個獨立進程中的隊列,這樣你就不一定非要把服務設計爲線程安全的模式了。

使用AIDL
Android接口定義語言AIDL(Android Interface Definition Language)完成以下的所有工作:將對象解析爲操作系統可識別的原始形態,並將它們跨進程序列化(marshal)以完成IPC。前一個使用Messenger的方式,實際上也是基於AIDL的,它用AIDL作爲底層結構。如上所述,Messenger將在一個單獨的進程中創建一個包含了所有客戶端請求的隊列,這樣服務每次就只會收到一個請求。可是,如果想讓你的服務能同時處理多個請求,那你就可以直接使用AIDL。這種情況下,你的服務必須擁有多線程處理能力,並且是以線程安全的方式編寫的。
要直接使用AIDL,你必須創建一個.aidl文件,其中定義了編程的接口。Android SDK工具使用此文件來生成一個抽象類(abstract class),其中實現了接口及對IPC的處理,然後你就可以在自己的服務中擴展該類。

注意:

絕大多數應用程序都不應該用AIDL來創建bound服務,因爲這可能需要多線程處理能力並且會讓代碼變得更爲複雜。
因此,AIDL對絕大多數應用程序都不適用,並且本文也不會討論如何在服務中使用它的內容。如果你確信需要直接使用AIDL,那請參閱 AIDL
文檔。

發佈了287 篇原創文章 · 獲贊 216 · 訪問量 175萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章