Calvin's Tech Space

          成于堅忍,毀于浮躁

             :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
          在Android中窗體與窗體之間如何互相調(diào)用和交換數(shù)據(jù)?窗體(Activity)和后臺的服務(wù)(Service)如何通信?基于Unix(Linux)的系統(tǒng)都有一個很優(yōu)秀的傳統(tǒng),就是倡導(dǎo)非常輕便的進(jìn)程間通信(IPC)機(jī)制;倡導(dǎo)進(jìn)程通過IPC來互相協(xié)作;倡導(dǎo)功能單一,小巧而強(qiáng)壯的進(jìn)程,而不是又大又復(fù)雜的“萬金油”。同樣,在Android中我們可以將我們的Activity和Service放在不同的進(jìn)程中運(yùn)行,我們可以在我們的Task 中加載其他進(jìn)程的Activity,這些機(jī)制都鼓勵我們“盡量利用已有的功能,利用IPC和包含這些已有功能的程序協(xié)作,來完成一個完整的應(yīng)用”,例如在我們的程序中充分利用Google Map的相關(guān)窗體和服務(wù)。所有這些都建立在一套輕便好用的IPC機(jī)制上。


          Android的組件和進(jìn)程間通信都建立在一種基于稱為Intent的消息基礎(chǔ)之上。Intent就是一種消息,它包含了兩個重要的內(nèi)容:1. 消息的目的,即這個消息是發(fā)給哪個組件的?(消息的目的中不會包含“消息是發(fā)給哪個進(jìn)程”這樣的信息,這里Android有意淡化進(jìn)程的概念,而只讓我們關(guān)心組件,因?yàn)榱私馓嚓P(guān)于進(jìn)程的具體信息會加大復(fù)雜度,而又如何做到進(jìn)程間的消息傳遞呢?下文會說到一種Android中關(guān)于這點(diǎn)比較特別的設(shè)計方式,我認(rèn)為是一種簡捷有用又符合手機(jī)特點(diǎn)的設(shè)計);2. 消息所攜帶的數(shù)據(jù)內(nèi)容,即需要傳遞給目標(biāo)的數(shù)據(jù)。下面是一個簡單的利用Intent來啟動一個Service并向其傳遞數(shù)據(jù)的代碼示例:


          Intent serviceIntent = new Intent(context, svrMain.class);
          serviceIntent.putExtra(“Network_Report”, networkStatus);
          context.startService(serviceIntent);


          上面的代碼首先構(gòu)造了一個Intent對象,并在構(gòu)造的時候指定了這個Intent的目的地,即“svrMain.class”,表示這個Intent是要傳遞給一個類名叫svrMain的Service。然后向這個Intent中放入了一個數(shù)據(jù),數(shù)據(jù)的key為“Network_Report”,value為一個叫networkStatus的int類型變量,用來指明當(dāng)前網(wǎng)絡(luò)的狀態(tài)。最后我們使用系統(tǒng)提供的上下文API,將這個Intent傳遞給指定的Service。


          Intent的消息目的地分為兩種模式,一種是顯式的,一種是隱式的。我們上面的例子中看到的就是一個顯式消息的例子。顯式消息直接指定消息目的地組件的類元信息,例如上面例子中svrMain就是我們寫的一個類名為svrMain的Service,class操作符就是獲取其類元信息。這種模式的消息由于已經(jīng)確切知道了消息目標(biāo)的確切信息,所以只適用于同一進(jìn)程內(nèi)的不同組件之間通信,例如打開一個子窗體,和同一進(jìn)程中的service通信等。對應(yīng)的,隱式消息就一般用于跨進(jìn)程的通信了,隱式消息沒有確定的消息目的地,除了數(shù)據(jù)外,隱式消息只是包含了一些用于表征消息特征的描述字段。而一些需要收到某種特定特征消息的某個程序中的某個組件,需要通過在其所在程序的AndroidMainifest.xml中注冊一種被稱為intent-filter的消息特征篩選器,然后Android系統(tǒng)會按照一定的匹配規(guī)則來匹配發(fā)出的消息特征和所有擁有響應(yīng)這種特征的intent-filter的組件(無論是同一進(jìn)程內(nèi)的組件還是不同程序中的組件),匹配到的組件就會接受到相應(yīng)的消息。前面的描述多少有些拗口,我們舉個實(shí)際的例子來說明,如果我們想開啟一個子窗體(無論這個窗體來自同一進(jìn)程還是不同進(jìn)程),我們除了使用顯式消息外,我們還可以使用隱式消息:


          Intent openSomeDiagIntent = new Intent();
          openSomeDiagIntent.setAction(“edwin.demo.fooActivity”);
          this.startActivity(openSomeDiagIntent);


          上面的隱式消息不包含具體的目的地,而是僅包含一個名位“Action”的特征字符串,Action就是上文所說的Intent特征的一種。從字面上來理解,可以理解為這個消息所代表的是完成某一個動作的含義,由action來標(biāo)明動作的名字。所有能夠處理這種動作的Activity都可以收到該消息。對應(yīng)的,可能在同一個程序或者另外的某個程序的AndroidMainifest.xml中聲明了下面這樣的一個Activity:


          <activity android:name=”.fooActivity”>
          <intent-filter>
          <action android:name=”edwin.demo.fooActivity” />
          </intent-filter>
          </activity>


          那么這就表示這個Activity能夠收到并處理action為“edwin.demo.fooActivity”的消息。所以上面的代碼串起來的效果就是,打開了這個名為.fooActivity的窗口,無論這個窗口是在當(dāng)前的進(jìn)程中還是另外的一個程序中。


          除了Action這種消息特征外,Intent還有category,data這兩個特征描述屬性。Category同樣是一個字符串,從字面上理解就是“消息的分類特征”。從程序上看其和Action的不同在于,一個Intent只能有惟一的一個Action名稱,但是卻可以包含多個Category字符串;一個Intent-Filter可以包含多個Action節(jié)點(diǎn)但至少要包含一個,另一方面一個Intent-Filter可以包含零到多個Category節(jié)點(diǎn)。Android在做Intent-Filter匹配的時候,Intent的Action屬性匹配到Intent-Filter中的任何一個action節(jié)點(diǎn),就表明擁有這個Intent-Filter的組件能夠處理這種消息;而對于Category來講一個Intent中的所有的Category都必須存在于Intent-Filter中的Category節(jié)點(diǎn)中時,才表明匹配成功。Data屬性可以描述一個Intent所要傳遞的數(shù)據(jù)類型和URI,每一個Intent只能包含一個Data屬性。其中數(shù)據(jù)的類型使用MIME類型描述方式來描述,例如video/mpeg表示編碼格式為mpeg的視頻,這里也可以使用通配符,例如video/*表示任意格式的視頻文件類型;數(shù)據(jù)的URI由scheme(協(xié)議),host,port,path四部分組成:scheme://host:port/path,例如http://test.com:8080/file/file1 或者content://edwin.demo.contentProvider:100/forder/content1,其中path部分也是可以支持通配符的。Data屬性是一個很有用的描述特征,例如下面這樣的一個包含data節(jié)點(diǎn)的Intent-Filter:


          <activity android:name=”.actHttpVideoMan”>
          <intent-filter>
          <action android:name=”edwin.demo.actHttpVideoMan.Main” />
          <data android:scheme=”http” android:type=”video/*” />
          </intent-filter>
          </activity>


          它表示窗體actHttpVideoMan能夠處理來自web服務(wù)器的視頻文件。這樣的filter有什么作用呢?最典型的情況就是配合瀏覽器工作。瀏覽器在打開一個鏈接的時候首先會嘗試顯示這個鏈接對應(yīng)的html頁面,如果這個鏈接不是一個html頁面,而是一個視頻文件或者其他瀏覽器本身不能處理的格式的話,瀏覽器會使用隱式消息嘗試開啟一個能夠處理這種數(shù)據(jù)格式的Activity來處理,瀏覽器發(fā)出的隱式消息就是一個包含data屬性,其中URI scheme為http,數(shù)據(jù)類型為video/*的消息,如果有能夠匹配這個intent的組件,例如我們上面的那個activity,瀏覽器就會啟動這個窗體,接著這個窗體會根據(jù)data屬性指定的URI去播放在線視頻,如果沒有可以處理這個intent的Activity,瀏覽器才會調(diào)用下載管理器下載文件。


          隱式消息這個設(shè)計簡單有效,它忽略了進(jìn)程的細(xì)節(jié),讓IPC在一個更高的更接近人腦思維模式的層次工作,讓系統(tǒng)中的不同進(jìn)程協(xié)作看起來就像是同一程序中的協(xié)作一樣,這種簡單的IPC機(jī)制在很大的程度上鼓勵我們和其他進(jìn)程協(xié)作,通過協(xié)作的進(jìn)程來完成一個復(fù)雜的任務(wù),而不是把什么功能都做到一個大而全的程序里面。不過上文還有一些細(xì)節(jié)沒有提到,例如如果一個intent有多個可匹配的處理組件,系統(tǒng)如何處理?這就要分響應(yīng)消息的組件類型來說了,如果是service,那么這些service都可以啟動并處理消息,如果是Activity,則android會彈出一個對話框讓用戶進(jìn)行選擇。比如我們安裝了多個可以處理在線視頻的軟件,當(dāng)我們在瀏覽器中點(diǎn)擊一個在線視頻的鏈接時,系統(tǒng)會讓用戶選擇使用哪個軟件來觀看。另外大家一定會想到安全性的問題,如果不同進(jìn)程間的組件可以通過隱式消息互相通信,那我們的程序不是可以輕易調(diào)用到其他的程序或者系統(tǒng)中的一些敏感程序的組件,這樣會不會很不安全呢?其實(shí)Android在安全方面有一個統(tǒng)一,完備和輕便的安全策略模型,Intent的安全自然是被考慮在內(nèi)的,關(guān)于android的安全模型我會在后續(xù)的系列blog中專門說明。


          最后,除了Intent這種基于消息的進(jìn)程內(nèi)和進(jìn)程間通信模型外,android中也有一種相比起來稍顯笨重一些的IPC機(jī)制,它采用類似遠(yuǎn)程方法調(diào)用的方案,通過接口定義文件AIDL來定義一個IPC接口,然后通過接收方實(shí)現(xiàn)接口,調(diào)用方調(diào)用接口的本地代理實(shí)現(xiàn)來完成IPC。這種模型只適用于Activity和Service間的通信,之所以需要這種稍顯重量的模式,是因?yàn)锳ctivity除了發(fā)送intent去啟動一個service外,可能還需要能夠在Service的運(yùn)行過程中連接到service,對Service發(fā)送一些控制請求。例如音樂播放程序,其后臺的播放服務(wù)往往獨(dú)立運(yùn)行,以方便我們在使用其他程序界面時也能聽到音樂。同時這個后臺播放服務(wù)也會定義一個控制接口,包含比如播放,暫停,快進(jìn)之類的方法,任何時候播放程序的界面都可以通過使用bindService API連接到播放服務(wù),獲取這個接口的包含IPC細(xì)節(jié)的實(shí)現(xiàn)代理,通過這組控制接口方法對其進(jìn)行控制,這時這種IPC的方案就顯的更方便更直觀一些了。有關(guān)使用AIDL這種IPC的更詳細(xì)描述,Google的官方文檔已做了詳細(xì)的講解。

          轉(zhuǎn)自:http://hkbarton.sacredfir.com/?p=93
          posted on 2010-02-03 09:05 calvin 閱讀(231) 評論(0)  編輯  收藏 所屬分類: Android
          主站蜘蛛池模板: 上虞市| 鹤岗市| 西峡县| 孝昌县| 桃园县| 临洮县| 汝州市| 永靖县| 西峡县| 崇礼县| 建昌县| 容城县| 邹城市| 锦屏县| 大厂| 崇礼县| 右玉县| 肇东市| 周口市| 鹤峰县| 三门峡市| 永福县| 安乡县| 上杭县| 定兴县| 湘西| 四川省| 抚顺市| 麻栗坡县| 百色市| 清水河县| 遵化市| 秭归县| 珲春市| 改则县| 濮阳县| 古蔺县| 梁河县| 北辰区| 南京市| 清丰县|