日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

一個(gè)帖子學(xué)會(huì)Android開發(fā)四大組件

 Tornador 2015-06-25

注:本文來自“友盟杯”,僅在此閱讀,學(xué)習(xí)


這個(gè)文章主要是講Android開發(fā)的四大組件,本文主要分為

一、Activity詳解
二、Service詳解
三、Broadcast Receiver詳解
四、Content Provider詳解
外加一個(gè)重要組件 intent的詳解。



一、Activity詳解
Activty的生命周期的也就是它所在進(jìn)程的生命周期。


1.jpg 

一個(gè)Activity的啟動(dòng)順序:

onCreate()——>onStart()——>onResume()

當(dāng)另一個(gè)Activity啟動(dòng)時(shí):
第一個(gè)Activity onPause()——>第二個(gè)Activity    onCreate()——>onStart()——>onResume() 
——>第一個(gè)Activity   onStop()

當(dāng)返回到第一個(gè)Activity時(shí):
第二個(gè)Activity onPause() ——> 第一個(gè)Activity onRestart()——>onStart()——>onResume() 
——>第二個(gè)Activity   onStop()——>onDestroy()

一個(gè)Activity的銷毀順序:
(情況一)onPause()——><Process Killed> 
(情況二)onPause()——>onStop()——><Process Killed> 
(情況三)onPause()——>onStop()——>onDestroy()

  每一個(gè)活動(dòng)( Activity )都處于某一個(gè)狀態(tài),對于開發(fā)者來說,是無法控制其應(yīng)用程序處于某一個(gè)狀態(tài)的,這些均由系統(tǒng)來完成。

  但是當(dāng)一個(gè)活動(dòng)的狀態(tài)發(fā)生改變的時(shí)候,開發(fā)者可以通過調(diào)用 onXX() 的方法獲取到相關(guān)的通知信息。

  在實(shí)現(xiàn) Activity 類的時(shí)候,通過覆蓋( override )這些方法即可在你需要處理的時(shí)候來調(diào)用。


         一、 onCreate :當(dāng)活動(dòng)第一次啟動(dòng)的時(shí)候,觸發(fā)該方法,可以在此時(shí)完成活動(dòng)的初始化工作。 
onCreate 方法有一個(gè)參數(shù),該參數(shù)可以為空( null ),也可以是之前調(diào)用 onSaveInstanceState ()方法保存的狀態(tài)信息。
        二、  onStart :該方法的觸發(fā)表示所屬活動(dòng)將被展現(xiàn)給用戶。
        三、  onResume :當(dāng)一個(gè)活動(dòng)和用戶發(fā)生交互的時(shí)候,觸發(fā)該方法。
       四、  onPause :當(dāng)一個(gè)正在前臺運(yùn)行的活動(dòng)因?yàn)槠渌幕顒?dòng)需要前臺運(yùn)行而轉(zhuǎn)入后臺運(yùn)行的時(shí)候,觸發(fā)該方法。這時(shí)候需要將活動(dòng)的狀態(tài)持久化,比如正在編輯的數(shù)據(jù)庫記錄等。
        五、  onStop :當(dāng)一個(gè)活動(dòng)不再需要展示給用戶的時(shí)候,觸發(fā)該方法。如果內(nèi)存緊張,系統(tǒng)會(huì)直接結(jié)束這個(gè)活動(dòng),而不會(huì)觸發(fā) onStop 方法。 所以保存狀態(tài)信息是應(yīng)該在onPause時(shí)做,而不是onStop時(shí)做?;顒?dòng)如果沒有在前臺運(yùn)行,都將被停止或者Linux管理進(jìn)程為了給新的活動(dòng)預(yù)留足夠的存儲(chǔ)空間而隨時(shí)結(jié)束這些活動(dòng)。因此對于開發(fā)者來說,在設(shè)計(jì)應(yīng)用程序的時(shí)候,必須時(shí)刻牢記這一原則。在一些情況下,onPause方法或許是活動(dòng)觸發(fā)的最后的方法,因此開發(fā)者需要在這個(gè)時(shí)候保存需要保存的信息。
        六、onRestart :當(dāng)處于停止?fàn)顟B(tài)的活動(dòng)需要再次展現(xiàn)給用戶的時(shí)候,觸發(fā)該方法。
        七、 onDestroy :當(dāng)活動(dòng)銷毀的時(shí)候,觸發(fā)該方法。和 onStop 方法一樣,如果內(nèi)存緊張,系統(tǒng)會(huì)直接結(jié)束這個(gè)活動(dòng)而不會(huì)觸發(fā)該方法。
·        onSaveInstanceState :系統(tǒng)調(diào)用該方法,允許活動(dòng)保存之前的狀態(tài),比如說在一串字符串中的光標(biāo)所處的位置等。 
通常情況下,開發(fā)者不需要重寫覆蓋該方法,在默認(rèn)的實(shí)現(xiàn)中,已經(jīng)提供了自動(dòng)保存活動(dòng)所涉及到的用戶界面組件的所有狀態(tài)信息。

  Activity棧

  上面提到開發(fā)者是無法控制Activity的狀態(tài)的,那Activity的狀態(tài)又是按照何種邏輯來運(yùn)作的呢?這就要知道 Activity 棧。
  每個(gè)Activity的狀態(tài)是由它在Activity棧(是一個(gè)后進(jìn)先出LIFO,包含所有正在運(yùn)行Activity的隊(duì)列)中的位置決定的。
  當(dāng)一個(gè)新的Activity啟動(dòng)時(shí),當(dāng)前的活動(dòng)的Activity將會(huì)移到Activity棧的頂部。
  如果用戶使用后退按鈕返回的話,或者前臺的Activity結(jié)束,活動(dòng)的Activity就會(huì)被移出棧消亡,而在棧上的上一個(gè)活動(dòng)的Activity將會(huì)移上來并變?yōu)榛顒?dòng)狀態(tài)。如下圖所示:2.jpg   一個(gè)應(yīng)用程序的優(yōu)先級是受最高優(yōu)先級的Activity影響的。當(dāng)決定某個(gè)應(yīng)用程序是否要終結(jié)去釋放資源,Android內(nèi)存管理使用棧來決定基于Activity的應(yīng)用程序的優(yōu)先級。
  Activity狀態(tài)
  一般認(rèn)為Activity有以下四種狀態(tài):
  活動(dòng)的:當(dāng)一個(gè)Activity在棧頂,它是可視的、有焦點(diǎn)、可接受用戶輸入的。Android試圖盡最大可能保持它活動(dòng)狀態(tài),殺死其它Activity來確保當(dāng)前活動(dòng)Activity有足夠的資源可使用。當(dāng)另外一個(gè)Activity被激活,這個(gè)將會(huì)被暫停。
  暫停:在很多情況下,你的Activity可視但是它沒有焦點(diǎn),換句話說它被暫停了。有可能原因是一個(gè)透明或者非全屏的Activity被激活。
  當(dāng)被暫停,一個(gè)Activity仍會(huì)當(dāng)成活動(dòng)狀態(tài),只不過是不可以接受用戶輸入。在極特殊的情況下,Android將會(huì)殺死一個(gè)暫停的Activity來為活動(dòng)的Activity提供充足的資源。當(dāng)一個(gè)Activity變?yōu)橥耆[藏,它將會(huì)變成停止。
  停止:當(dāng)一個(gè)Activity不是可視的,它“停止”了。這個(gè)Activity將仍然在內(nèi)存中保存它所有的狀態(tài)和會(huì)員信息。盡管如此,當(dāng)其它地方需要內(nèi)存時(shí),它將是最有可能被釋放資源的。當(dāng)一個(gè)Activity停止后,一個(gè)很重要的步驟是要保存數(shù)據(jù)和當(dāng)前UI狀態(tài)。一旦一個(gè)Activity退出或關(guān)閉了,它將變?yōu)榇脿顟B(tài)。
  待用: 在一個(gè)Activity被殺死后和被裝在前,它是待用狀態(tài)的。待用Acitivity被移除Activity棧,并且需要在顯示和可用之前重新啟動(dòng)它。
  activity的四種加載模式
  在android的多activity開發(fā)中,activity之間的跳轉(zhuǎn)可能需要有多種方式,有時(shí)是普通的生成一個(gè)新實(shí)例,有時(shí)希望跳轉(zhuǎn)到原來某個(gè)activity實(shí)例,而不是生成大量的重復(fù)的activity。加載模式便是決定以哪種方式啟動(dòng)一個(gè)跳轉(zhuǎn)到原來某個(gè)Activity實(shí)例。
  在android里,有4種activity的啟動(dòng)模式,分別為:
  ·standard: 標(biāo)準(zhǔn)模式,一調(diào)用startActivity()方法就會(huì)產(chǎn)生一個(gè)新的實(shí)例。
  ·singleTop: 如果已經(jīng)有一個(gè)實(shí)例位于Activity棧的頂部時(shí),就不產(chǎn)生新的實(shí)例,而只是調(diào)用Activity中的newInstance()方法。如果不位于棧頂,會(huì)產(chǎn)生一個(gè)新的實(shí)例。
  ·singleTask: 會(huì)在一個(gè)新的task中產(chǎn)生這個(gè)實(shí)例,以后每次調(diào)用都會(huì)使用這個(gè),不會(huì)去產(chǎn)生新的實(shí)例了。
  ·singleInstance: 這個(gè)跟singleTask基本上是一樣,只有一個(gè)區(qū)別:在這個(gè)模式下的Activity實(shí)例所處的task中,只能有這個(gè)activity實(shí)例,不能有其他的實(shí)例。
  這些啟動(dòng)模式可以在功能清單文件AndroidManifest.xml中進(jìn)行設(shè)置,中的launchMode屬性。
  相關(guān)的代碼中也有一些標(biāo)志可以使用,比如我們想只啟用一個(gè)實(shí)例,則可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 標(biāo)志,這個(gè)標(biāo)志表示:如果這個(gè)activity已經(jīng)啟動(dòng)了,就不產(chǎn)生新的activity,而只是把這個(gè)activity實(shí)例加到棧頂來就可以了。
  1.   Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
  2.   intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
  3.   startActivity(intent);
復(fù)制代碼


  Activity的加載模式受啟動(dòng)Activity的Intent對象中設(shè)置的Flag和manifest文件中Activity的元素的特性值交互控制。
  下面是影響加載模式的一些特性
  核心的Intent Flag有:
  FLAG_ACTIVITY_NEW_TASK
  FLAG_ACTIVITY_CLEAR_TOP
  FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
  FLAG_ACTIVITY_SINGLE_TOP

核心的特性有:
  taskAffinity
  launchMode
  allowTaskReparenting
  clearTaskOnLaunch
  alwaysRetainTaskState
  finishOnTaskLaunch

 四種加載模式的區(qū)別
  所屬task的區(qū)別
  一般情況下,“standard”和”singleTop”的activity的目標(biāo)task,和收到的Intent的發(fā)送者在同一個(gè)task內(nèi),就相當(dāng)于誰調(diào)用它,它就跟誰在同一個(gè)Task中。
  除非Intent包括參數(shù)FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK參數(shù),會(huì)啟動(dòng)到別的task里。
  “singleTask”和”singleInstance” 總是把要啟動(dòng)的activity作為一個(gè)task的根元素,他們不會(huì)被啟動(dòng)到一個(gè)其他task里。
  是否允許多個(gè)實(shí)例
  “standard”和”singleTop”可以被實(shí)例化多次,并且是可以存在于不同的task中;這種實(shí)例化時(shí)一個(gè)task可以包括一個(gè)activity的多個(gè)實(shí)例;
  “singleTask”和”singleInstance”則限制只生成一個(gè)實(shí)例,并且是task的根元素。
  singleTop 要求如果創(chuàng)建intent的時(shí)候棧頂已經(jīng)有要?jiǎng)?chuàng)建的Activity的實(shí)例,則將intent發(fā)送給該實(shí)例,而不創(chuàng)建新的實(shí)例。
  是否允許其它activity存在于本task內(nèi)
  “singleInstance”獨(dú)占一個(gè)task,其它activity不能存在那個(gè)task里;
  如果它啟動(dòng)了一個(gè)新的activity,不管新的activity的launch mode 如何,新的activity都將會(huì)到別的task里運(yùn)行(如同加了FLAG_ACTIVITY_NEW_TASK參數(shù))。
  而另外三種模式,則可以和其它activity共存。
  是否每次都生成新實(shí)例
  “standard”對于每一個(gè)啟動(dòng)Intent都會(huì)生成一個(gè)activity的新實(shí)例;
  “singleTop”的activity如果在task的棧頂?shù)脑挘瑒t不生成新的該activity的實(shí)例,直接使用棧頂?shù)膶?shí)例,否則,生成該activity的實(shí)例。
  比如:
  現(xiàn)在task棧元素為A-B-C-D(D在棧頂),這時(shí)候給D發(fā)一個(gè)啟動(dòng)intent,如果D是 “standard”的,則生成D的一個(gè)新實(shí)例,棧變?yōu)锳-B-C-D-D。
  如果D是singleTop的話,則不會(huì)生產(chǎn)D的新實(shí)例,棧狀態(tài)仍為A-B-C-D
  如果這時(shí)候給B發(fā)Intent的話,不管B的launchmode是”standard” 還是 “singleTop” ,都會(huì)生成B的新實(shí)例,棧狀態(tài)變?yōu)锳-B-C-D-B。
  “singleInstance”是其所在棧的唯一activity,它會(huì)每次都被重用。
  “singleTask” 如果在棧頂,則接受intent,否則,該intent會(huì)被丟棄,但是該task仍會(huì)回到前臺。 當(dāng)已經(jīng)存在的activity實(shí)例處理新的intent時(shí)候,會(huì)調(diào)用onNewIntent()方法,如果收到intent生成一個(gè)activity實(shí)例,那么用戶可以通過back鍵回到上一個(gè)狀態(tài);如果是已經(jīng)存在的一個(gè)activity來處理這個(gè)intent的話,用戶不能通過按back鍵返回到這之前的狀態(tài)。
-----------------------------------
二、Service詳解

  service可以在和多場合的應(yīng)用中使用,比如播放多媒體的時(shí)候用戶啟動(dòng)了其他Activity這個(gè)時(shí)候程序要在后臺繼續(xù)播放,比如檢測SD卡上文件的變化,再或者在后臺記錄你地理信息位置的改變等等,總之服務(wù)嘛,總是藏在后頭的。

  Service是在一段不定的時(shí)間運(yùn)行在后臺,不和用戶交互應(yīng)用組件。每個(gè)Service必須在manifest中 通過<service>來聲明??梢酝ㄟ^contect.startservice和contect.bindserverice來啟動(dòng)。

  Service和其他的應(yīng)用組件一樣,運(yùn)行在進(jìn)程的主線程中。這就是說如果service需要很多耗時(shí)或者阻塞的操作,需要在其子線程中實(shí)現(xiàn)。

  service的兩種模式(startService()/bindService()不是完全分離的):

  本地服務(wù) Local Service 用于應(yīng)用程序內(nèi)部。

  它可以啟動(dòng)并運(yùn)行,直至有人停止了它或它自己停止。在這種方式下,它以調(diào)用Context.startService()啟動(dòng),而以調(diào)用Context.stopService()結(jié)束。它可以調(diào)用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調(diào)用了多少次startService()方法,你只需要調(diào)用一次stopService()來停止服務(wù)。

  用于實(shí)現(xiàn)應(yīng)用程序自己的一些耗時(shí)任務(wù),比如查詢升級信息,并不占用應(yīng)用程序比如Activity所屬線程,而是單開線程后臺執(zhí)行,這樣用戶體驗(yàn)比較好。
  遠(yuǎn)程服務(wù) Remote Service 用于android系統(tǒng)內(nèi)部的應(yīng)用程序之間。

  它可以通過自己定義并暴露出來的接口進(jìn)行程序操作。客戶端建立一個(gè)到服務(wù)對象的連接,并通過那個(gè)連接來調(diào)用服務(wù)。連接以調(diào)用Context.bindService()方法建立,以調(diào)用 Context.unbindService()關(guān)閉。多個(gè)客戶端可以綁定至同一個(gè)服務(wù)。如果服務(wù)此時(shí)還沒有加載,bindService()會(huì)先加載它。

  可被其他應(yīng)用程序復(fù)用,比如天氣預(yù)報(bào)服務(wù),其他應(yīng)用程序不需要再寫這樣的服務(wù),調(diào)用已有的即可。

 生命周期

使用context.startService() 啟動(dòng)Service是會(huì)會(huì)經(jīng)歷:

  context.startService() ->onCreate()- >onStart()->Service running

  context.stopService() | ->onDestroy() ->Service stop

  如果Service還沒有運(yùn)行,則android先調(diào)用onCreate()然后調(diào)用onStart();如果Service已經(jīng)運(yùn)行,則只調(diào)用onStart(),所以一個(gè)Service的onStart方法可能會(huì)重復(fù)調(diào)用多次。

  stopService的時(shí)候直接onDestroy,如果是調(diào)用者自己直接退出而沒有調(diào)用stopService的話,Service會(huì)一直在后臺運(yùn)行。該Service的調(diào)用者再啟動(dòng)起來后可以通過stopService關(guān)閉Service。

  所以調(diào)用startService的生命周期為:onCreate --> onStart(可多次調(diào)用) --> onDestroy
  使用使用context.bindService()啟動(dòng)Service會(huì)經(jīng)歷:

  context.bindService()->onCreate()->onBind()->Service running

  onUnbind() -> onDestroy() ->Service stop

  onBind將返回給客戶端一個(gè)IBind接口實(shí)例,IBind允許客戶端回調(diào)服務(wù)的方法,比如得到Service運(yùn)行的狀態(tài)或其他操作。這個(gè)時(shí)候把調(diào)用者(Context,例如Activity)會(huì)和Service綁定在一起,Context退出了,Srevice就會(huì)調(diào)用onUnbind->onDestroy相應(yīng)退出。

  所以調(diào)用bindService的生命周期為:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。

  在Service每一次的開啟關(guān)閉過程中,只有onStart可被多次調(diào)用(通過多次startService調(diào)用),其他onCreate,onBind,onUnbind,onDestory在一個(gè)生命周期中只能被調(diào)用一次。

而啟動(dòng)service,根據(jù)onStartCommand的返回值不同,有兩個(gè)附加的模式:

  1. START_STICKY 用于顯示啟動(dòng)和停止service。

  2. START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要處理時(shí)才運(yùn)行的模式。

  服務(wù)不能自己運(yùn)行,需要通過調(diào)用Context.startService()或Context.bindService()方法啟動(dòng)服務(wù)。這兩個(gè)方法都可以啟動(dòng)Service,但是它們的使用場合有所不同。

  1. 使用startService()方法啟用服務(wù),調(diào)用者與服務(wù)之間沒有關(guān)連,即使調(diào)用者退出了,服務(wù)仍然運(yùn)行。

  如果打算采用Context.startService()方法啟動(dòng)服務(wù),在服務(wù)未被創(chuàng)建時(shí),系統(tǒng)會(huì)先調(diào)用服務(wù)的onCreate()方法,接著調(diào)用onStart()方法。

  如果調(diào)用startService()方法前服務(wù)已經(jīng)被創(chuàng)建,多次調(diào)用startService()方法并不會(huì)導(dǎo)致多次創(chuàng)建服務(wù),但會(huì)導(dǎo)致多次調(diào)用onStart()方法。

  采用startService()方法啟動(dòng)的服務(wù),只能調(diào)用Context.stopService()方法結(jié)束服務(wù),服務(wù)結(jié)束時(shí)會(huì)調(diào)用onDestroy()方法。

  2. 使用bindService()方法啟用服務(wù),調(diào)用者與服務(wù)綁定在了一起,調(diào)用者一旦退出,服務(wù)也就終止,大有“不求同時(shí)生,必須同時(shí)死”的特點(diǎn)。

  onBind()只有采用Context.bindService()方法啟動(dòng)服務(wù)時(shí)才會(huì)回調(diào)該方法。該方法在調(diào)用者與服務(wù)綁定時(shí)被調(diào)用,當(dāng)調(diào)用者與服務(wù)已經(jīng)綁定,多次調(diào)用Context.bindService()方法并不會(huì)導(dǎo)致該方法被多次調(diào)用。

  采用Context.bindService()方法啟動(dòng)服務(wù)時(shí)只能調(diào)用onUnbind()方法解除調(diào)用者與服務(wù)解除,服務(wù)結(jié)束時(shí)會(huì)調(diào)用onDestroy()方法。

  看看官方給出的比較流程示意圖:

  官方文檔告訴我們,一個(gè)service可以同時(shí)start并且bind,在這樣的情況,系統(tǒng)會(huì)一直保持service的運(yùn)行狀態(tài)如果service已經(jīng)start了或者BIND_AUTO_CREATE標(biāo)志被設(shè)置。如果沒有一個(gè)條件滿足,那么系統(tǒng)將會(huì)調(diào)用onDestory方法來終止service.所有的清理工作(終止線程,反注冊接收器)都在onDestory中完成。

  擁有service的進(jìn)程具有較高的優(yōu)先級

  官方文檔告訴我們,Android系統(tǒng)會(huì)盡量保持擁有service的進(jìn)程運(yùn)行,只要在該service已經(jīng)被啟動(dòng)(start)或者客戶端連接(bindService)到它。當(dāng)內(nèi)存不足時(shí),需要保持,擁有service的進(jìn)程具有較高的優(yōu)先級。

  1. 如果service正在調(diào)用onCreate,onStartCommand或者onDestory方法,那么用于當(dāng)前service的進(jìn)程則變?yōu)榍芭_進(jìn)程以避免被killed。

  2. 如果當(dāng)前service已經(jīng)被啟動(dòng)(start),擁有它的進(jìn)程則比那些用戶可見的進(jìn)程優(yōu)先級低一些,但是比那些不可見的進(jìn)程更重要,這就意味著service一般不會(huì)被killed.

  3. 如果客戶端已經(jīng)連接到service (bindService),那么擁有Service的進(jìn)程則擁有最高的優(yōu)先級,可以認(rèn)為service是可見的。

  4. 如果service可以使用startForeground(int, Notification)方法來將service設(shè)置為前臺狀態(tài),那么系統(tǒng)就認(rèn)為是對用戶可見的,并不會(huì)在內(nèi)存不足時(shí)killed。

  如果有其他的應(yīng)用組件作為Service,Activity等運(yùn)行在相同的進(jìn)程中,那么將會(huì)增加該進(jìn)程的重要性。

  本地service

  1.不需和Activity交互的本地服務(wù)

  1. public class LocalService extends Service {

  2. private static final String TAG = "LocalService";

  3. @Override
  4. public IBinder onBind(Intent intent) {
  5. Log.i(TAG, "onBind");
  6. return null;
  7. }

  8. @Override
  9. public void onCreate() {
  10. Log.i(TAG, "onCreate");
  11. super.onCreate();
  12. }

  13. @Override
  14. public void onDestroy() {
  15. Log.i(TAG, "onDestroy");
  16. super.onDestroy();
  17. }

  18. @Override
  19. public void onStart(Intent intent, int startId) {
  20. Log.i(TAG, "onStart");
  21. super.onStart(intent, startId);
  22. }
  23. }
復(fù)制代碼
  Activity:
  1. public class ServiceActivity extends Activity {

  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.servicedemo);

  6. ((Button) findViewById(R.id.startLocalService)).setOnClickListener(
  7. new View.OnClickListener(){

  8. @Override
  9. public void onClick(View view) {
  10. // TODO Auto-generated method stub
  11. startService(new Intent("com.demo.SERVICE_DEMO"));

  12. });

  13. ((Button) findViewById(R.id.stopLocalService)).setOnClickListener(
  14. new View.OnClickListener(){

  15. @Override
  16. public void onClick(View view) {
  17. // TODO Auto-generated method stub
  18. stopService(new Intent("com.demo.SERVICE_DEMO"));
  19. }
  20. });
  21. }

  22. }
復(fù)制代碼
  在AndroidManifest.xml添加:
  1. <service android:name=".LocalService">
  2. <intent-filter>
  3. <action android:name="com.demo.SERVICE_DEMO" />
  4. <category android:name="android.intent.category.default" />
  5. </intent-filter>
  6. </service>
復(fù)制代碼
  否則啟動(dòng)服務(wù)時(shí)會(huì)提示new Intent找不到"com.demo.SERVICE_DEMO"。

  對于這類不需和Activity交互的本地服務(wù),是使用startService/stopService的最好例子。

  運(yùn)行時(shí)可以發(fā)現(xiàn)第一次startService時(shí),會(huì)調(diào)用onCreate和onStart,在沒有stopService前,無論點(diǎn)擊多少次startService,都只會(huì)調(diào)用onStart。而stopService時(shí)調(diào)用onDestroy。再次點(diǎn)擊stopService,會(huì)發(fā)現(xiàn)不會(huì)進(jìn)入service的生命周期的,即不會(huì)再調(diào)用onCreate,onStart和onDestroy。

  而onBind在startService/stopService中沒有調(diào)用。

  2.本地服務(wù)和Activity交互

  對于這種case,官方的sample(APIDemo\app.LocalService)是最好的例子:

  1. /**
  2. * This is an example of implementing an application service that runs locally
  3. * in the same process as the application. The {@link LocalServiceController}
  4. * and {@link LocalServiceBinding} classes show how to interact with the
  5. * service.
  6. *
  7. * <p>Notice the use of the {@link NotificationManager} when interesting things
  8. * happen in the service. This is generally how background services should
  9. * interact with the user, rather than doing something more disruptive such as
  10. * calling startActivity().
  11. */
  12. public class LocalService extends Service {
  13. private NotificationManager mNM;

  14. /**
  15. * Class for clients to access. Because we know this service always
  16. * runs in the same process as its clients, we don't need to deal with
  17. * IPC.
  18. */
  19. public class LocalBinder extends Binder {
  20. LocalService getService() {
  21. return LocalService.this;
  22. }
  23. }

  24. @Override
  25. public void onCreate() {
  26. mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

  27. // Display a notification about us starting. We put an icon in the status bar.
  28. showNotification();
  29. }

  30. @Override
  31. public int onStartCommand(Intent intent, int flags, int startId) {
  32. Log.i("LocalService", "Received start id " + startId + ": " + intent);
  33. // We want this service to continue running until it is explicitly
  34. // stopped, so return sticky.
  35. return START_STICKY;
  36. }

  37. @Override
  38. public void onDestroy() {
  39. // Cancel the persistent notification.
  40. mNM.cancel(R.string.local_service_started);

  41. // Tell the user we stopped.
  42. Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
  43. }

  44. @Override
  45. public IBinder onBind(Intent intent) {
  46. return mBinder;
  47. }

  48. // This is the object that receives interactions from clients. See
  49. // RemoteService for a more complete example.
  50. private final IBinder mBinder = new LocalBinder();

  51. /**
  52. * Show a notification while this service is running.
  53. */
  54. private void showNotification() {
  55. // In this sample, we'll use the same text for the ticker and the expanded notification
  56. CharSequence text = getText(R.string.local_service_started);

  57. // Set the icon, scrolling text and timestamp
  58. Notification notification = new Notification(R.drawable.stat_sample, text,
  59. System.currentTimeMillis());

  60. // The PendingIntent to launch our activity if the user selects this notification
  61. PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
  62. new Intent(this, LocalServiceController.class), 0);

  63. // Set the info for the views that show in the notification panel.
  64. notification.setLatestEventInfo(this, getText(R.string.local_service_label),
  65. text, contentIntent);

  66. // Send the notification.
  67. // We use a layout id because it is a unique number. We use it later to cancel.
  68. mNM.notify(R.string.local_service_started, notification);
  69. }
  70. }
復(fù)制代碼
  這里可以發(fā)現(xiàn)onBind需要返回一個(gè)IBinder對象。也就是說和上一例子LocalService不同的是,

  1. 添加了一個(gè)public內(nèi)部類繼承Binder,并添加getService方法來返回當(dāng)前的Service對象;

  2. 新建一個(gè)IBinder對象--new那個(gè)Binder內(nèi)部類;

  3. onBind方法返還那個(gè)IBinder對象。

  Activity:

  1. /**
  2. * <p>Example of binding and unbinding to the {@link LocalService}.
  3. * This demonstrates the implementation of a service which the client will
  4. * bind to, receiving an object through which it can communicate with the service.</p>
  5. */
  6. public class LocalServiceBinding extends Activity {
  7. private boolean mIsBound;
  8. private LocalService mBoundService;

  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);

  12. setContentView(R.layout.local_service_binding);

  13. // Watch for button clicks.
  14. Button button = (Button)findViewById(R.id.bind);
  15. button.setOnClickListener(mBindListener);
  16. button = (Button)findViewById(R.id.unbind);
  17. button.setOnClickListener(mUnbindListener);
  18. }

  19. private ServiceConnection mConnection = new ServiceConnection() {
  20. public void onServiceConnected(ComponentName className, IBinder service) {
  21. // This is called when the connection with the service has been
  22. // established, giving us the service object we can use to
  23. // interact with the service. Because we have bound to a explicit
  24. // service that we know is running in our own process, we can
  25. // cast its IBinder to a concrete class and directly access it.
  26. mBoundService = ((LocalService.LocalBinder)service).getService(); 

  27. // Tell the user about this for our demo.
  28. Toast.makeText(LocalServiceBinding.this, R.string.local_service_connected,
  29. Toast.LENGTH_SHORT).show();
  30. }

  31. public void onServiceDisconnected(ComponentName className) { 
  32. // This is called when the connection with the service has been
  33. // unexpectedly disconnected -- that is, its process crashed.
  34. // Because it is running in our same process, we should never
  35. // see this happen.
  36. mBoundService = null;
  37. Toast.makeText(LocalServiceBinding.this, R.string.local_service_disconnected,
  38. Toast.LENGTH_SHORT).show();
  39. }
  40. };

  41. private OnClickListener mBindListener = new OnClickListener() {
  42. public void onClick(View v) {
  43. // Establish a connection with the service. We use an explicit
  44. // class name because we want a specific service implementation that
  45. // we know will be running in our own process (and thus won't be
  46. // supporting component replacement by other applications).
  47. bindService(new Intent(LocalServiceBinding.this, 
  48. LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
  49. mIsBound = true;
  50. }
  51. };

  52. private OnClickListener mUnbindListener = new OnClickListener() {
  53. public void onClick(View v) {
  54. if (mIsBound) {
  55. // Detach our existing connection.
  56. unbindService(mConnection);
  57. mIsBound = false;
  58. }
  59. }
  60. };
  61. }
復(fù)制代碼


  明顯看出這里面添加了一個(gè)名為ServiceConnection類,并實(shí)現(xiàn)了onServiceConnected(從IBinder獲取Service對象)和onServiceDisconnected(set Service to null)。

  而bindService和unbindService方法都是操作這個(gè)ServiceConnection對象的。

  AndroidManifest.xml里添加:

  1.   <service android:name=".app.LocalService" />
復(fù)制代碼
  這里沒什么特別的,因?yàn)閟ervice沒有需要什么特別的action,所以只是聲明service而已,而activity和普通的沒差別。

  運(yùn)行時(shí),發(fā)現(xiàn)調(diào)用次序是這樣的:

  bindService:

  1.LocalService : onCreate

  2.LocalService : onBind

  3.Activity: onServiceConnected

  unbindService: 只是調(diào)用onDestroy

  可見,onStart是不會(huì)被調(diào)用的,而onServiceDisconnected沒有調(diào)用的原因在上面代碼的注釋有說明。


------------------------
三、Broadcast Receiver詳解


  BroadcastReceiver 用于異步接收廣播Intent。主要有兩大類,用于接收廣播的:

  ·正常廣播 Normal broadcasts(用 Context.sendBroadcast()發(fā)送)是完全異步的。它們都運(yùn)行在一個(gè)未定義的順序,通常是在同一時(shí)間。這樣會(huì)更有效,但意味著receiver不能包含所要使用的結(jié)果或中止的API。

  ·有序廣播 Ordered broadcasts(用 Context.sendOrderedBroadcast()發(fā)送)每次被發(fā)送到一個(gè)receiver。所謂有序,就是每個(gè)receiver執(zhí)行后可以傳播到下一個(gè)receiver,也可以完全中止傳播--不傳播給其他receiver。 而receiver運(yùn)行的順序可以通過matched intent-filter 里面的android:priority來控制,當(dāng)priority優(yōu)先級相同的時(shí)候,Receiver以任意的順序運(yùn)行。

  要注意的是,即使是Normal broadcasts,系統(tǒng)在某些情況下可能會(huì)恢復(fù)到一次傳播給一個(gè)receiver。 特別是receiver可能需要?jiǎng)?chuàng)建一個(gè)進(jìn)程,為了避免系統(tǒng)超載,只能一次運(yùn)行一個(gè)receiver。

  Broadcast Receiver 并沒有提供可視化的界面來顯示廣播信息??梢允褂肗otification和Notification Manager來實(shí)現(xiàn)可視化的信息的界面,顯示廣播信息的內(nèi)容,圖標(biāo)及震動(dòng)信息。

  生命周期

  一個(gè)BroadcastReceiver 對象只有在被調(diào)用onReceive(Context, Intent)的才有效的,當(dāng)從該函數(shù)返回后,該對象就無效的了,結(jié)束生命周期。

  因此從這個(gè)特征可以看出,在所調(diào)用的onReceive(Context, Intent)函數(shù)里,不能有過于耗時(shí)的操作,不能使用線程來執(zhí)行。對于耗時(shí)的操作,請start service來完成。因?yàn)楫?dāng)?shù)玫狡渌惒讲僮魉祷氐慕Y(jié)果時(shí),BroadcastReceiver 可能已經(jīng)無效了。

  發(fā)送廣播

  事件的廣播比較簡單,構(gòu)建Intent對象,可調(diào)用sendBroadcast(Intent)方法將廣播發(fā)出。另外還有sendOrderedBroadcast(),sendStickyBroadcast()等方法,請查閱API Doc。

  1.new Intent with action name

  Intent intent = new Intent(String action);

  或者 只是new Intent, 然后

  intent.setAction(String action);

  2.set data等準(zhǔn)備好了后,in activity,

  sendBroadcast(Intent); // 發(fā)送廣播

  接收廣播

  通過定義一個(gè)繼承BroadcastReceiver類來實(shí)現(xiàn),繼承該類后覆蓋其onReceiver方法,并在該方法中響應(yīng)事件。
  1. public class SMSReceiver extends BroadcastReceiver { 

  2.         @Override 
  3.         public void onReceive(Context context, Intent intent) { 
  4.                 // get data from SMS intent 
  5.                 Bundle bundle = intent.getExtras(); 
  6.                 if (bundle != null){ 
  7.                         // get message by "pdus" 
  8.                         Object[] objArray = (Object[]) bundle.get("pdus"); 

  9.                         // rebuild SMS 
  10.                         SmsMessage[] messages = new SmsMessage[objArray.length]; 
  11.                         for (int i=0; i < objArray.length; i++){ 
  12.                                 messages[i] = SmsMessage.createFromPdu((byte[])objArray[i]); 

  13.                                 StringBuilder str = new StringBuilder("from: "); 
  14.                                 str.append(messages[i].getDisplayOriginatingAddress()); 
  15.                                 str.append("\nmessage:\n"); 
  16.                                 str.append(messages[i].getDisplayMessageBody()); 

  17.                                 Toast.makeText(context, str.toString(), Toast.LENGTH_LONG) 
  18.                                                 .show(); 
  19.                         } 
  20.                 } 
  21.         } 
  22. }
復(fù)制代碼
  注冊Receiver

  注冊有兩種方式:

  1. 靜態(tài)方式,在AndroidManifest.xml的application里面定義receiver并設(shè)置要接收的action。

  1.   <receiver android:name=".SMSReceiver">

  2.   <intent-filter>

  3.   <action android:name="android.provider.Telephony.SMS_RECEIVED" />

  4.   </intent-filter>

  5.   </receiver>
復(fù)制代碼
2. 動(dòng)態(tài)方式, 在activity里面調(diào)用函數(shù)來注冊,和靜態(tài)的內(nèi)容差不多。一個(gè)形參是receiver,另一個(gè)是IntentFilter,其中里面是要接收的action。
  1. public class HelloDemo extends Activity {    
  2.         private BroadcastReceiver receiver;    

  3.         @Override 
  4.         protected void onStart() { 
  5.                 super.onStart(); 

  6.                 receiver = new CallReceiver(); 
  7.                 registerReceiver(receiver, new IntentFilter("android.intent.action.PHONE_STATE")); 
  8.         } 

  9.         @Override 
  10.         protected void onStop() { 
  11.                 unregisterReceiver(receiver); 
  12.                 super.onStop(); 
  13.         } 
  14. }
復(fù)制代碼
  一個(gè)receiver可以接收多個(gè)action的,即可以有多個(gè)intent-filter,需要在onReceive里面對intent.getAction(action name)進(jìn)行判斷。

  個(gè)人推薦使用靜態(tài)注冊方式,由系統(tǒng)來管理receiver,而且程序里的所有receiver,可以在xml里面一目了然。而動(dòng)態(tài)注冊方式,隱藏在代碼中,比較難發(fā)現(xiàn)。

  而且動(dòng)態(tài)注冊,需要特別注意的是,在退出程序前要記得調(diào)用Context.unregisterReceiver()方法。一般在activity的onStart()里面進(jìn)行注冊, onStop()里面進(jìn)行注銷。官方提醒,如果在Activity.onResume()里面注冊了,就必須在Activity.onPause()注銷。

  Permission權(quán)限

  要接收某些action,需要在AndroidManifest.xml里面添加相應(yīng)的permission。例如接收SMS:

  1. <uses-permission android:name="android.permission.RECEIVE_SMS" />
復(fù)制代碼
  下面給出動(dòng)態(tài)注冊的接收來電的廣播處理的CallReceiver的代碼:

  一種方式是直接讀取intent.getStringExtra("incoming_number")來獲取來電號碼:

  1. public class CallReceiver extends BroadcastReceiver { 

  2.         @Override 
  3.         public void onReceive(Context context, Intent intent) { 
  4.                 TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
  5.                  
  6.                 switch(teleManager.getCallState()){ 
  7.                 case TelephonyManager.CALL_STATE_RINGING: //響鈴 
  8.                         Toast.makeText(context, "Ringing: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show(); 
  9.                         break; 
  10.                 case TelephonyManager.CALL_STATE_OFFHOOK: //接聽 
  11.                         Toast.makeText(context, "OffHook: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show(); 
  12.                         break; 
  13.                 case TelephonyManager.CALL_STATE_IDLE: //掛斷 
  14.                         Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG).show(); 
  15.                         break; 
  16.                 } 
  17.         } 
  18. }
復(fù)制代碼
  在運(yùn)行時(shí),發(fā)現(xiàn)除了響鈴時(shí)可以獲取來電號碼,接聽和掛斷都不能成功獲取的,顯示為null。

  另一種方式是通過PhoneStateListener的onCallStateChanged來監(jiān)聽狀態(tài)的變化:

  1. public class CallReceiver extends BroadcastReceiver { 

  2.         private Context m_context; 
  3.         @Override 
  4.         public void onReceive(Context context, Intent intent) { 
  5.                 m_context = context; 
  6.                 TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
  7.                 teleManager.listen(new PhoneStateListener(){ 

  8.                         @Override 
  9.                         public void onCallStateChanged(int state, String incomingNumber) { 
  10.                                 switch(state){ 
  11.                                 case TelephonyManager.CALL_STATE_RINGING: //響鈴 
  12.                                         Toast.makeText(m_context, "Ringing: " + incomingNumber, Toast.LENGTH_LONG) 
  13.                                                                 .show(); 
  14.                                         break; 
  15.                                 case TelephonyManager.CALL_STATE_OFFHOOK: //接聽 
  16.                                         Toast.makeText(m_context, "OffHook: " + incomingNumber, Toast.LENGTH_LONG) 
  17.                                         .show(); 
  18.                                         break; 
  19.                                 case TelephonyManager.CALL_STATE_IDLE: //掛斷 
  20.                                         Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG) 
  21.                                         .show(); 
  22.                                         break; 
  23.                                 } 
  24.                         }}, PhoneStateListener.LISTEN_CALL_STATE);  
  25.         } 
  26. }
復(fù)制代碼
  運(yùn)行時(shí)也發(fā)現(xiàn)incomingNumber在接聽和掛斷時(shí)獲取為blank。

  因?yàn)檫@里監(jiān)聽的是通話的狀態(tài)變化,所以這個(gè)receiver會(huì)被調(diào)用3次。

  監(jiān)聽通話狀態(tài)需要加上權(quán)限:

  1. <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
復(fù)制代碼
  ===========

  小結(jié):

  1. 對于sendBroadCast的intent對象,需要設(shè)置其action name;

  2. 推薦使用顯式指明receiver,在配置文件AndroidManifest.xml指明;

  3. 一個(gè)receiver可以接收多個(gè)action;

  4. 每次接收廣播都會(huì)重新生成一個(gè)接收廣播的對象,再次調(diào)用onReceive;

  5. 在BroadCast 中盡量不要處理太多邏輯問題,建議復(fù)雜的邏輯交給Activity 或者 Service 去處理。



--------------------------------------------
四、Content Provider詳解

  ContentProvider(內(nèi)容提供者)是Android中的四大組件之一。主要用于對外共享數(shù)據(jù),也就是通過ContentProvider把應(yīng)用中的數(shù)據(jù)共享給其他應(yīng)用訪問,其他應(yīng)用可以通過ContentProvider對指定應(yīng)用中的數(shù)據(jù)進(jìn)行操作。ContentProvider分為系統(tǒng)的和自定義的,系統(tǒng)的也就是例如聯(lián)系人,圖片等數(shù)據(jù)。

  android中對數(shù)據(jù)操作包含有:

  file, sqlite3, Preferences, ContectResolver與ContentProvider前三種數(shù)據(jù)操作方式都只是針對本應(yīng)用內(nèi)數(shù)據(jù),程序不能通過這三種方法去操作別的應(yīng)用內(nèi)的數(shù)據(jù)。

  android中提供ContectResolver與ContentProvider來操作別的應(yīng)用程序的數(shù)據(jù)。

  使用方式:

  一個(gè)應(yīng)用實(shí)現(xiàn)ContentProvider來提供內(nèi)容給別的應(yīng)用來操作,

  一個(gè)應(yīng)用通過ContentResolver來操作別的應(yīng)用數(shù)據(jù),當(dāng)然在自己的應(yīng)用中也可以。

  以下這段是Google Doc中對ContentProvider的大致概述:

  內(nèi)容提供者將一些特定的應(yīng)用程序數(shù)據(jù)供給其它應(yīng)用程序使用。內(nèi)容提供者繼承于ContentProvider 基類,為其它應(yīng)用程序取用和存儲(chǔ)它管理的數(shù)據(jù)實(shí)現(xiàn)了一套標(biāo)準(zhǔn)方法。然而,應(yīng)用程序并不直接調(diào)用這些方法,而是使用一個(gè) ContentResolver 對象,調(diào)用它的方法作為替代。ContentResolver可以與任意內(nèi)容提供者進(jìn)行會(huì)話,與其合作來對所有相關(guān)交互通訊進(jìn)行管理。

  1.ContentProvider

  Android提供了一些主要數(shù)據(jù)類型的ContentProvider,比如音頻、視頻、圖片和私人通訊錄等??稍赼ndroid.provider包下面找到一些Android提供的ContentProvider。通過獲得這些ContentProvider可以查詢它們包含的數(shù)據(jù),當(dāng)然前提是已獲得適當(dāng)?shù)淖x取權(quán)限。

  主要方法:

  public boolean onCreate() 在創(chuàng)建ContentProvider時(shí)調(diào)用
  public Cursor query(Uri, String[], String, String[], String) 用于查詢指定Uri的ContentProvider,返回一個(gè)Cursor

  public Uri insert(Uri, ContentValues) 用于添加數(shù)據(jù)到指定Uri的ContentProvider中

  public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的數(shù)據(jù)

  public int delete(Uri, String, String[]) 用于從指定Uri的ContentProvider中刪除數(shù)據(jù)

  public String getType(Uri) 用于返回指定的Uri中的數(shù)據(jù)的MIME類型

  *如果操作的數(shù)據(jù)屬于集合類型,那么MIME類型字符串應(yīng)該以vnd.android.cursor.dir/開頭。

  例如:要得到所有person記錄的Uri為content://contacts/person,那么返回的MIME類型字符串為"vnd.android.cursor.dir/person"。

  *如果要操作的數(shù)據(jù)屬于非集合類型數(shù)據(jù),那么MIME類型字符串應(yīng)該以vnd.android.cursor.item/開頭。

  例如:要得到id為10的person記錄的Uri為content://contacts/person/10,那么返回的MIME類型字符串應(yīng)為"vnd.android.cursor.item/person"。

  2.ContentResolver

  當(dāng)外部應(yīng)用需要對ContentProvider中的數(shù)據(jù)進(jìn)行添加、刪除、修改和查詢操作時(shí),可以使用ContentResolver類來完成,要獲取ContentResolver對象,可以使用Context提供的getContentResolver()方法。

  ContentResolver cr = getContentResolver();

  ContentResolver提供的方法和ContentProvider提供的方法對應(yīng)的有以下幾個(gè)方法。

  public Uri insert(Uri uri, ContentValues values) 用于添加數(shù)據(jù)到指定Uri的ContentProvider中。

  public int delete(Uri uri, String selection, String[] selectionArgs) 用于從指定Uri的ContentProvider中刪除數(shù)據(jù)。

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 用于更新指定Uri的ContentProvider中的數(shù)據(jù)。

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查詢指定Uri的ContentProvider。



  3.Uri

  Uri指定了將要操作的ContentProvider,其實(shí)可以把一個(gè)Uri看作是一個(gè)網(wǎng)址,我們把Uri分為三部分。

  第一部分是"content://"。可以看作是網(wǎng)址中的"http://"。

  第二部分是主機(jī)名或authority,用于唯一標(biāo)識這個(gè)ContentProvider,外部應(yīng)用需要根據(jù)這個(gè)標(biāo)識來找到它??梢钥醋魇蔷W(wǎng)址中的主機(jī)名,比如"blog.csdn.net"。

  第三部分是路徑名,用來表示將要操作的數(shù)據(jù)。可以看作網(wǎng)址中細(xì)分的內(nèi)容路徑。

1.jpg 
---------------------------------

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多