Service的理解和使用
首先推薦一下郭林大神所講的這一節(jié)的博客:http://blog.csdn.net/guolin_blog/article/details/11952435
下面結(jié)合我對這一部分的學習,自己做一個小節(jié)。
Android5.0之后組件必須使用顯示intent來啟動,如果為隱示的,則設置Intent的包名。intent.setPackage(“com.llay.admin.mydemo”);
一、基本概念
Service是一個應用程序組件,它能夠在后臺執(zhí)行一些耗時較長的操作,并且不提供用戶界面。服務能被其它應用程序的組件啟動,即使用戶切換到另外的應用時還能保持后臺運行。此外,應用程序組件還能與服務綁定,并與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網(wǎng)絡傳輸、音樂播放、執(zhí)行文件I/O、或者與content provider進行交互,所有這些都是后臺進行的。
二、服務的分類
服務有以下兩種基本類型:
Started (這里有一個service的子類IntentService,待會兒單獨講解)
如果一個應用程序組件(比如一個activity)通過調(diào)用startService()來啟動服務,則該服務就是被“started”了。一旦被啟動,服務就能在后臺一直運行下去,即使啟動它的組件已經(jīng)被銷毀了。
通常,started的服務執(zhí)行單一的操作并且不會向調(diào)用者返回結(jié)果。比如,它可以通過網(wǎng)絡下載或上傳文件。當操作完成后,服務應該自行終止。
Bound
如果一個應用程序組件通過調(diào)用bindService()綁定到服務上,則該服務就是被“bound”了。bound服務提供了一個客戶端/服務器接口,允許組件與服務進行交互、發(fā)送請求、獲取結(jié)果,甚至可以利用進程間通信(IPC)跨進程執(zhí)行這些操作。綁定服務的生存期和被綁定的應用程序組件一致。
多個組件可以同時與一個服務綁定,不過所有的組件解除綁定后,服務也就會被銷毀。
三、一些回調(diào)方法
服務的生命周期圖:
為了創(chuàng)建一個服務,你必須新建一個Service的子類(或一個已有Service的子類)。在你的實現(xiàn)代碼中,請按需重寫一些回調(diào)方法,用于對服務生命周期中的關鍵節(jié)點進行處理,以及向組件提供綁定機制。 最重要的需要重寫的回調(diào)方法包括:
onStartCommand()
當其它組件,比如一個activity,通過調(diào)用startService()請求started方式的服務時,系統(tǒng)將會調(diào)用本方法。 一旦本方法執(zhí)行,服務就被啟動,并在后臺一直運行下去。 如果你的代碼實現(xiàn)了本方法,你就有責任在完成工作后通過調(diào)用stopSelf()或stopService()終止服務。 (如果你只想提供bind方式,那就不需要實現(xiàn)本方法。)
onBind()
當其它組件需要通過bindService()綁定服務時(比如執(zhí)行RPC),系統(tǒng)會調(diào)用本方法。 在本方法的實現(xiàn)代碼中,你必須返回IBinder來提供一個接口,客戶端用它來和服務進行通信。 你必須確保實現(xiàn)本方法,不過假如你不需要提供綁定,那就返回null即可。
onCreate()
當服務第一次被創(chuàng)建時,系統(tǒng)會調(diào)用本方法,用于執(zhí)行一次性的配置工作(之前已調(diào)用過onStartCommand()或onBind()) 了。如果服務已經(jīng)運行,則本方法就不會被調(diào)用。
onDestroy()
當服務用不上了并要被銷毀時,系統(tǒng)會調(diào)用本方法。 你的服務應該實現(xiàn)本方法來進行資源的清理工作,諸如線程、已注冊的偵聽器listener和接收器receiver等等。 這將是服務收到的最后一個調(diào)用。
如果組件通過調(diào)用startService()(這會導致onStartCommand()的調(diào)用)啟動了服務,那么服務將一直保持運行,直至自行用stopSelf()終止或由其它組件調(diào)用stopService()來終止它。
如果組件調(diào)用bindService()來創(chuàng)建服務(那onStartCommand()就不會被調(diào)用),則服務的生存期就與被綁定的組件一致。一旦所有客戶端都對服務解除了綁定,系統(tǒng)就會銷毀該服務。
僅當內(nèi)存少得可憐、且必須覆蓋擁有用戶焦點的activity的系統(tǒng)資源時,Android系統(tǒng)才會強行終止一個服務。 如果服務被擁有用戶焦點的activity綁定著,則它一般不會被殺死。 如果服務聲明為#在前臺運行服務(下文討論),則它幾乎永遠不會被殺死。 否則,如果服務已被啟動并且已運行了很長時間,那么系統(tǒng)將會隨時間推移而降低它在后臺任務列表中的級別, 此類服務將很有可能會被殺死——如果服務已經(jīng)啟動,那你必須好好設計代碼,使其能完美地應付被系統(tǒng)重啟的情況。 如果系統(tǒng)殺死了你的服務,只要資源再度夠用,系統(tǒng)就會再次啟動服務(當然這還取決于onStartCommand()的返回值,下文將會述及)。關于系統(tǒng)可能會在何時銷毀服務的詳細信息,請參閱進程和線程。
四、代碼的實現(xiàn)
注意:Service是Android的四大組件,所以記得在Androidmanifests.xml文件中注冊。注意:四大組件只有BroadCast能動態(tài)注冊。
界面圖片:
4、1 Started服務的代碼實現(xiàn)
started服務是指其它組件通過調(diào)用startService()來啟動的服務,這會引發(fā)對該服務onStartCommand()方法的調(diào)用。
一旦服務被啟動started,它就擁有了自己的生命周期,這是獨立于啟動它的組件的。并且它能夠在后臺一直運行下去,即使啟動它的組件已被銷毀 也是如此。 因此,服務應該能夠在完成工作后自行終止,通過調(diào)用stopSelf()即可,或者由其它組件通過調(diào)用stopService()也可以。
諸如activity之類的應用程序組件,可以通過調(diào)用startService()啟動服務,并傳入一個給出了服務和服務所需數(shù)據(jù)的Intent對象。服務將在onStartCommand()方法中接收到該Intent對象。
舉個例子,假定某activity需要把一些數(shù)據(jù)保存到在線數(shù)據(jù)庫中。此activity可以啟動一個守護服務并通過傳入startService()一個intent把需要保存的數(shù)據(jù)發(fā)送給該服務。該服務在onStartCommand()內(nèi)接收intent,連接Internet,再進行數(shù)據(jù)庫事務處理。當事務完成后,服務自行終止,并被系統(tǒng)銷毀。
在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">//請記住我們要創(chuàng)建一個單獨的線程,因為服務通常運行于進程的主線程中,可我們不想阻塞主線程。</span><span class="hljs-comment">//我們還要賦予它后臺運行的優(yōu)先級,以便計算密集的工作不會干擾我們的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">//對于每一個啟動請求,都發(fā)送一個消息來啟動一個處理</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">//通常我們在這里執(zhí)行一些工作,比如下載文件。</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">//根據(jù)startId終止服務,這樣我們就不會在處理其它工作的過程中再來終止服務</span><span class="hljs-comment">//如果組件通過調(diào)用startService()(這會導致onStartCommand()的調(diào)用)啟動了服務,那么服務將一直保持運行,直至自行用stopSelf()終止或由其它組件調(diào)用stopService()來終止它。</span><span class="hljs-comment">//如果組件調(diào)用bindService()來創(chuàng)建服務(那onStartCommand()就不會被調(diào)用),則服務的生存期就與被綁定的組件一致。一旦所有客戶端都對服務解除了綁定,系統(tǒng)就會銷毀該服務。</span>stopSelf(msg.arg1);Log.e(TAG, <span class="hljs-string">"startedService處理數(shù)據(jù)完成后自動停止"</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來實現(xiàn)在線程中處理事件。因為這里的服務還是在主線程中的,不能阻塞。
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代碼實現(xiàn)
注意:默認情況下,運行服務的進程與應用程序的相同,并且運行在應用程序的主線程中。
因此,如果你的服務要執(zhí)行計算密集或阻塞的操作,而同時用戶又需要與同一個應用程序中的activity進行交互,那么服務將會降低activity的性能。
為了避免對應用程序性能的影響,你應該在服務中啟動一個新的線程。
IntentService:異步處理服務,新開一個線程:handlerThread在線程中發(fā)消息,然后接受處理完成后,會清理線程,并且關掉服務。
IntentService有以下特點:
(1) 它創(chuàng)建了一個獨立的工作線程來處理所有的通過onStartCommand()傳遞給服務的intents。
(2) 創(chuàng)建了一個工作隊列,來逐個發(fā)送intent給onHandleIntent()。
(3) 不需要主動調(diào)用stopSelft()來結(jié)束服務。因為,在所有的intent被處理完后,系統(tǒng)會自動關閉服務。
(4) 默認實現(xiàn)的onBind()返回null
(5) 默認實現(xiàn)的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能夠動態(tài)在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">/*** 構(gòu)造方法是必需的,必須用工作線程名稱作為參數(shù)* 調(diào)用父類的[http://developer.android.com/reference/android/app/IntentService.html#IntentService(java.lang.String) IntentService(String)]構(gòu)造方法。*/</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從缺省的工作線程中調(diào)用本方法,并用啟動服務的intent作為參數(shù)。* 本方法返回后,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的區(qū)別和使用地方。
總結(jié): 在當需要一起在線程(自己開)中處理,則使用Started服務。 在當需要一個一個在線程(自帶)中處理,則使用IntentService。
4、2 Bound服務的代碼實現(xiàn)
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">//調(diào)用LocalService中的方法。</span><span class="hljs-comment">//不過,如果該調(diào)用會導致某些操作的掛起,那么調(diào)用應該放入單獨的線程中進行,</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">//我們已經(jīng)綁定到LocalService了,對IBinder進行類型轉(zhuǎn)換(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">//以下是綁定服務的一些回調(diào)函數(shù)</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">//生成隨機數(shù)</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>注意:如果你的服務只用于本地應用程序并且不需要跨進程工作,那你只要實現(xiàn)自己的 Binder
類即可,這樣你的客戶端就能直接訪問服務中的公共方法了。
注意:僅當客戶端和服務位于同一個應用程序和進程中,這也是最常見的情況,這種方式才會有用。比如,一個音樂應用需要把一個activity綁定到它自己的后臺音樂播放服務上,采用這種方式就會很不錯。
以下是設置步驟: 1、在你的服務中,創(chuàng)建一個Binder的實例,其中實現(xiàn)以下三者之一:
- 包含了可供客戶端調(diào)用的公共方法
- 返回當前Service實例,其中包含了可供客戶端調(diào)用的公共方法。
- 或者,返回內(nèi)含服務類的其它類的一個實例,服務中包含了可供客戶端調(diào)用的公共方法。
2、從回調(diào)方法onBind()中返回Binder的該實例。
3、在客戶端中,在回調(diào)方法onServiceConnected()中接收Binder并用所提供的方法對綁定的服務進行調(diào)用。 注意:
服務和客戶端之所以必須位于同一個應用程序中,是為了讓客戶端能夠正確轉(zhuǎn)換(cast)返回的對象并調(diào)用對象的API。
服務和客戶端也必須位于同一個進程中,因為這種方式不能執(zhí)行任何跨進程的序列化(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>運行圖:
綁定服務:
解絆服務:
五、擴展
創(chuàng)建一個支持綁定的服務時,你必須提供一個 IBinder ,用作客戶端和服務間進行通信的編程接口。定義這類接口的方式有三種:
擴展Binder類
如果服務是你的應用程序所私有的,并且與客戶端運行于同一個進程中(通常都是如此),你應該通過擴展Binder類來創(chuàng)建你的接口,并從onBind()返回一個它的實例。客戶端接收該Binder對象并用它來直接訪問Binder甚至Service中可用的公共(public)方法。
如果你的服務只是為你自己的應用程序執(zhí)行一些后臺工作,那這就是首選的技術方案。不用這種方式來創(chuàng)建接口的理由只有一個,就是服務要被其它應用程序使用或者要跨多個進程使用。
使用Messenger
如果你需要接口跨越多個進程進行工作,可以通過Messenger來為服務創(chuàng)建接口。在這種方式下,服務定義一個響應各類消息對象Message的Handler。此Handler是Messenger與客戶端共享同一個IBinder的基礎,它使得客戶端可以用消息對象Message向服務發(fā)送指令。此外,客戶端還可以定義自己的Message,以便服務能夠往回發(fā)送消息。
這是執(zhí)行進程間通信(IPC)最為簡便的方式,因為Messenger會把所有的請求放入一個獨立進程中的隊列,這樣你就不一定非要把服務設計為線程安全的模式了。
使用AIDL
Android接口定義語言AIDL(Android Interface Definition Language)完成以下的所有工作:將對象解析為操作系統(tǒng)可識別的原始形態(tài),并將它們跨進程序列化(marshal)以完成IPC。前一個使用Messenger的方式,實際上也是基于AIDL的,它用AIDL作為底層結(jié)構(gòu)。如上所述,Messenger將在一個單獨的進程中創(chuàng)建一個包含了所有客戶端請求的隊列,這樣服務每次就只會收到一個請求。可是,如果想讓你的服務能同時處理多個請求,那你就可以直接使用AIDL。這種情況下,你的服務必須擁有多線程處理能力,并且是以線程安全的方式編寫的。
要直接使用AIDL,你必須創(chuàng)建一個.aidl文件,其中定義了編程的接口。Android SDK工具使用此文件來生成一個抽象類(abstract class),其中實現(xiàn)了接口及對IPC的處理,然后你就可以在自己的服務中擴展該類。
注意:
絕大多數(shù)應用程序都不應該用AIDL來創(chuàng)建bound服務,因為這可能需要多線程處理能力并且會讓代碼變得更為復雜。
因此,AIDL對絕大多數(shù)應用程序都不適用,并且本文也不會討論如何在服務中使用它的內(nèi)容。如果你確信需要直接使用AIDL,那請參閱 AIDL
文檔。
總結(jié)
以上是生活随笔為你收集整理的Service的理解和使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android异步下载网络图片(其三:E
- 下一篇: service和thread的区别,何时