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