天天看點

《Android 應用案例開發大全(第二版)》——6.6節Android源代碼與過濾器

本節書摘來自異步社群《android 應用案例開發大全(第二版)》一書中的第6章,第6.6節android源代碼與過濾器,作者李甯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

6.6 android源代碼與過濾器

android開發權威指南(第二版)

第x問1 源代碼目錄:src/ch06/androidsrcfilter

隻從android官方文檔中擷取所有的過濾器、action等資訊是不可能的,因為這些文檔的編寫者未必參與了android的設計,是以并不一定對android相關方面的細節描述得特别清楚,而且還有不少錯誤。是以這就需要我們從其他地方來擷取這些知識,例如android源代碼、反編譯apk程式等。本節隻讨論如何利用android源代碼擷取與過濾器相關的資訊,在下一節會介紹如何通過反編譯的方式擷取同樣的資訊。在本書的其他章節還會繼續與讀者一起通過分析android源代碼、反編譯各種資源的方式擷取更多、更權威的資訊。

6.6.1 系統内置程式有哪些視窗可以利用

到現在為止已經可以使用多種方式調用另一個應用程式的視窗了,但可能很多讀者一直有一個疑問,如果是調用其他應用程式(不是自己編寫的程式)中的視窗,又是如何得知這些程式中哪些視窗提供了action,哪些視窗可以通過顯式方式調用呢?這些問題的答案将在本節和下一節為讀者揭曉。

在intent類中定義了一些action常量,這些常量用來調用系統的一些視窗或接收系統廣播。通常選中某一個常量後會在彈出的提示資訊框中顯示與該視窗相關的解釋,如圖6-8所示。

《Android 應用案例開發大全(第二版)》——6.6節Android源代碼與過濾器

盡管intent類中定義了用于調用系統視窗的常量,但并沒有為全部的action定義常量,而且可能有一些解釋不太清楚,是以需要采用下面的方法擷取更詳細的資訊。

由于任何android應用程式的所有視窗都必須在androidmanifest.xml檔案中聲明,是以隻要擷取相應内置程式的androidmanifest.xml檔案的源代碼即可。雖然可以通過擷取apk檔案的方式得到androidmanifest.xml檔案的内容1。但由于系統内置程式的apk檔案都在系統的目錄中(/system/app),如果沒有root權限根本通路不了該目錄,更别提擷取apk檔案了2。盡管熟悉linux的讀者知道可以用su或sudo指令暫時将使用者權限提升到root。但很多手機安裝的android系統根本就沒有su或sudo指令,是以為了方法更通用,本節采用了直接檢視android源代碼的方法(為了友善讀者,在随書CD光牒中已經包含了最新的android源代碼)擷取我們需要的資訊。

android系統内置應用程式的源代碼都放在了如下的目錄:

/packages/apps

每一個應用以一個單獨的目錄存放,例如,下面是一些常用内置應用的目錄。

browser:浏覽器。

calculator:電腦。

calendar:月曆。

camera:照相機。

contacts:聯系人。

packageinstaller:apk安裝器。有很多檔案管理器在單擊apk檔案時可以進行安裝,就是調用了packageinstaller中的某個負責安裝程式的視窗。

phone:電話管理,包括撥号、來去電記錄等視窗。

settings:系統設定。

launcher2:android系統的啟動程式。在系統啟動後第一個運作的就是該程式,主要包括桌面、圖示、程式清單等。經常提及的定制rom,ui部分主要就是修改launcher2。

下面幾節會選一些典型的過濾器進行分析,并通過java代碼調用這些過濾器所在的視窗。

6.6.2 顯示電腦(calculator)

電腦是筆者最喜歡的一個程式,因為calculator可能是唯一沒有使用android sdk内部api的程式,也就意味着calculator可以單獨提出來作為獨立的第三方程式,并通過正常的方法安裝到系統中。

對于很多初學者來說,由于并沒有在intent類中找到帶calculator的activity action,可能第一反應是系統不允許調用calculator。不過在這裡可以肯定地告訴大家,系統中所有帶ui的内置程式都允許調用相應的視窗,隻是有的action或category不太好找罷了。

現在先不考慮intent類中定義的常量,首先來看看calculator中androidmanifest.xml檔案的源代碼。calculator程式隻有一個視窗類(calculator),是以androidmanifest.xml檔案的内容也很簡單,下面是聲明calculator類的代碼。

從這些聲明代碼可以看出,calculator隻有一個過濾器,在該過濾器中有1個action和3個category。讀者可以對照6.5節介紹的過濾機制,想一想如何調用calculator,然後再看下面的内容。

可能通常認為android.intent.action.main是給系統用的,與第三方程式無關。實際上,該action不僅系統可以調用,第三方程式同樣也可以調用。後面定義的3個category指定一個就可以,不過指定前兩個,系統中肯定有重複的定義。是以通常使用下面的代碼調用calculator。

如果intent類中已經為某些action和category定義了常量,應盡量使用這些常量,而不要直接使用字元串形式的action和category。因為如果應用程式中的相應action和category改變,可能會影響視窗的調用者,盡管發生這種情況的可能性并不大。在不知道具體的action或category定義的常量名的情況下,可以通過檢視android源代碼擷取相應的字元串形式的action和category,然後在eclipse中跟蹤進intent類,并查找這些字元串,這樣就可以很容易找到相應的常量。例如,intent.action_main和intent.category_app_calculator的定義代碼如下:

在6.4節的例子中曾使用了顯式的方式調用calculator,這一點從本節給出的calculator類的聲明代碼中更容易了解。不過筆者仍然建議盡可能使用隐式方式調用系統的視窗,除非别無選擇,才使用顯式的方式調用視窗。

注意

calculator隻能簡單地顯示主視窗,并不能向主視窗傳遞任何值。由于聲明calculator時未指定data,是以自然無法傳遞uri和mime type了。當然還可以通過extra向主視窗傳遞資料,不過這就需要直接檢視calculator的源代碼了。經過筆者檢視,calculator并沒有處理extra,是以自然也無法通過extra傳遞資料了。

6.6.3 用浏覽器(browser)顯示網頁

在5.7.4小節曾使用下面的代碼調用浏覽器顯示指定的頁面。

在代碼中使用了intent.action_view作為activity action。intent.action_view常量的值是android.intent.action.view,在系統中的很多視窗都包含該action。例如,browser程式在聲明主視窗類(browseractivity)時就定義了很多過濾器,這些過濾器有很多都指定了該action。下面的代碼就是其中一個過濾器,也是上面的代碼能夠比對的過濾器。

在過濾器中除了前面提到的android.intent.action.view外,還指定了兩個category,以及4個data。現在先說說android.intent.category.browsable。由于android.intent.action.view在系統中已經被很多過濾器指定了,這些過濾器可能屬于不同的視窗。是以隻使用該action可能會有多個視窗符合條件,這也會增大出現選擇清單的可能性。是以,調用浏覽器時應加上android.intent.category.browsable,以便盡可能減小顯示選擇清單的可能性(因為多一個條件,系統中符合所有條件的過濾器就越少),代碼如下:

最後看一看過濾器中指定的4個data。通過這4個data指定了4個scheme:http、https、about和javascript。也就是說uri必須以如下4個字元串中的一個作為字首(scheme是以冒号結尾的)。

我們會發現,使用這個過濾器必須要指定uri,但如果不想指定uri該怎麼辦呢?可能有的讀者會将上面代碼中intent類的構造方法第2個參數去掉,不過執行代碼後會發現抛出異常,說明聲明browseractivity類時根本就沒有定義這樣的過濾器。不過browseractivity的過濾器還有很多,經過仔細查找,還是找到了如下的過濾器。

這個過濾器并沒有指定data,但卻有用于啟動程式的action和category。說明這個過濾器的功能之一是給系統用于單擊浏覽器程式圖示時顯示browseractivity視窗的。不過最後又定義了兩個category:android.intent.category.browsable和android.intent.category.app_browser。是以可以直接用android.intent.action.main和這兩個category顯示browseractivity,代碼如下:

但要注意最好不要單獨隻用其中一個category,否則可能會有多個過濾器滿足過濾條件,這就有可能顯示選擇清單了。例如,隻指定intent.category_browsable,可能會顯示如圖6-9所示的選擇清單。可以看到,browser、people和phone程式中都有視窗滿足過濾條件。

《Android 應用案例開發大全(第二版)》——6.6節Android源代碼與過濾器

答疑解惑:為什麼顯式調用浏覽器會導緻uri無效

在6.4節中曾在invokeotheractivity程式中使用顯式的方式調用了webbrowser中的主視窗用于顯示指定的網頁,在這個例子中程式可以完美地運作。但如果我們使用這種方法顯式調用系統内置的浏覽器會發現,盡管浏覽器可以成功調用,但往裡傳入的uri卻無效了。究其原因是因為在我們實作的webbrowser程式的主視窗類的oncreate方法中不管三七二十一都從intent.getdata方法中擷取了uri,是以無論何種情況,隻要主視窗成功顯示,就一定會擷取傳入的uri。而系統内置的浏覽器對action進行了驗證。隻有在action和category符合要求的情況下才會繼續讀取data中的資料。

系統浏覽器在啟動時會通過intent.getaction方法傳回一個action,如果要讓該方法傳回非空值,必須使用下面兩種方法調用視窗。>

隐式調用視窗。

單擊程式圖示啟動程式。

第1種情況就不需要多講了,因為已經為視窗指定了一個action,intent.getaction方法傳回的就是這個action。而第2種情況intent.getaction方法傳回了android.intent.action.main。讀者可以在oncreate方法中加入如下的代碼,看看在啟動程式時是否會在logcat視圖輸出這個action。

如果直接使用顯式方法調用視窗,intent.getaction方法會傳回null。而從本節前面給出的過濾器代碼可知,要處理uri,action必須是android.intent.action.view。是以如果顯式調用浏覽器的browseractivity視窗,根本不可能通過action檢測,是以當然不會執行到接收uri的代碼了,這也是為什麼顯式調用系統浏覽器顯示網頁後,uri會被忽略的原因。

6.6.4 撥打電話(phone)與授權

撥打電話在5.7.1小節已經介紹過了,現在再來回顧一下撥号的過程,代碼如下:

其中intent.action_call的值是android.intent.action.call。現在來研究一下具體哪個程式中的哪個視窗來響應上面代碼的請求。

由于本功能是撥打電話,是以很自然就會想到是調用了phone程式中的某個視窗。打開該程式的androidmanifest.xml檔案後,根據android.intent.action.call會找到如下的過濾器。

從這段過濾器的代碼中會看到除了指定action和category外,還指定了一個叫tel的scheme。是以通過uri指定電話号時一定要指定“tel:”字首(由于uri區分大小寫,是以不能寫成tel、tel等形式)。

到這裡還不算完,我們會發現隻執行前面兩行代碼會抛出異常,異常的大概意思是沒有權限調用撥号視窗。出現這個問題的關鍵是撥号盤的權限設定,也就是上面過濾器所在的視窗類,該類的聲明代碼如下:

從撥号盤類(outgoingcallbroadcaster)的聲明代碼可以看出,在标簽中設定了android:permission屬性,并指定了android.permission.call_phone權限,是以在調用者的androidmanifest.xml檔案中要加入如下的代碼才可以成功進行撥号。

《Android 應用案例開發大全(第二版)》——6.6節Android源代碼與過濾器

前面已經有很多代碼涉及uri。uri的标準格式是“xxx://host:port/path”。但有的uri并未指定“//”,例如“tel:12345678”。實際上這是由相應的程式進行處理的,因為如果不指定“//”,是無法通過uri.gethost方法擷取host的,自然也就無法擷取電話号的。不過處理來去電的程式(phone)會從“tel:12345678”中解析出電話号。如果某些程式隻能從uri的host中擷取資料,那就必須加“//”了。

1 apk檔案就是zip壓縮包,隻要解壓就可以得到apk檔案中的資源。不過java源代碼和xml檔案(包括androidmanifest.xml)都是編譯後的,需要一些工具反編譯。在6.7節會介紹如何反編譯androidmanifest.xml檔案。

2 android模拟器預設就是有root權限的,是以可以從android模拟器中獲得大多數系統内置的apk程式(但有些程式沒有包含在android模拟器中,如google play),分析這些apk的方法會在6.7節詳細讨論。