Android復習(四)權限—>應用權限最佳做法
應用權限最佳做法
權限請求可以保護設備上的敏感信息,僅在需要訪問信息以使應用正常工作時才應使用。利用本文檔提供的技巧,您可能無需請求訪問此類信息即可實現相同(或更好)的功能;但本文不會詳細討論權限在 Android 操作系統中的工作方式。
要比較籠統地了解 Android 權限,請參閱權限概述。要詳細了解如何在代碼中使用權限,請參閱請求應用權限。
使用 Android 權限的原則
使用 Android 權限時,我們建議遵循以下原則:
#1:僅使用應用正常工作所需的權限。根據您使用權限的方式,您可以通過其他方式執行所需的操作(系統 intent、標識符、電話后臺處理),而無需依賴于訪問敏感信息。
#2: 注意庫所需的權限。 包含某個庫時,您也會繼承它的權限要求。您應了解正要包含的庫、它們需要的權限以及這些權限的用途。
#3:公開透明。 請求權限時,請清晰說明您要訪問的內容以及訪問原因,以便用戶可以做出明智的決策。在請求權限時(包括安裝、運行時或更新權限對話框)列出這些信息。
#4:讓系統以顯式方式訪問。 在訪問敏感功能(如攝像頭或麥克風)時提供連續指示,讓用戶知道您在收集數據,避免讓他們認為您在暗自收集數據。
本指南其余部分將以開發 Android 應用為背景詳細介紹這些規則。
Android 6.0+ 中的權限
Android 6.0 Marshmallow 引入了一個新的權限模式,讓應用可以在運行時而不是安裝之前向用戶請求權限。支持這種新模式的應用會在應用確實需要相關服務或這些服務保護的數據時才請求權限。盡管這不會(不一定會)改變整體應用行為,但會給敏感用戶數據的處理方式帶來一些變化:
增加了情境上下文:系統會在運行時在應用的上下文中提示用戶提供訪問相關權限組涵蓋的功能所需的權限。用戶對請求權限的上下文更加敏感,如果您請求的權限與應用的用途不匹配,則一定要向用戶詳細解釋您為什么請求此權限;您應盡可能在請求時以及后續對話框中(如果用戶拒絕請求)解釋您的請求。
在授予權限時更加靈活:用戶可以在收到請求時以及在設置中拒絕訪問各個權限,但是當功能因此而中斷時,他們可能仍會感到驚訝。最好監控有多少用戶拒絕權限請求(例如,使用 Google Analytics(分析)),以便重構應用以避免依賴該權限,或更好地解釋應用需要此權限才能正常工作的原因。您還應確保應用可以處理當用戶拒絕權限請求或在設置中關閉權限時產生的異常。
增加了事務負擔:系統將要求用戶單獨授予權限組的訪問權限,而不是以集合的形式授予。這樣,最大程度降低請求的權限數量就變得非常重要,因為數量多會增加用戶授予權限的負擔,并且會增大至少有一個請求被拒絕的概率。
需要成為默認處理程序的權限
有些應用依賴于訪問與調用日志和短信有關的敏感用戶信息。如果您想請求特定于調用日志和短信的權限,并將應用發布到 Play 商店,則必須在請求這些運行時權限之前提示用戶將應用設置為默認處理程序以獲得核心系統功能。
如需有關默認處理程序的更多信息,包括有關向用戶顯示默認處理程序提示的指南,請參閱有關僅在默認處理程序中使用的權限的指南。
避免請求不必要的權限
每次您請求某個權限時,都是在強迫用戶做出決定。您應盡量減少提出這些請求的次數。如果用戶運行的是 Android 6.0(API 級別 23)或更高版本,則每次用戶嘗試一些請求權限的新應用功能時,應用都必須中斷用戶的操作而發起權限請求。如果用戶運行的是較低版本的 Android,則在安裝應用時必須授予應用每一種權限;如果列表過長或看起來不合適,用戶可能會決定根本不安裝應用。因此,應盡量減少應用需要的權限數量。
本部分提供了常見用例的替代方法,有助于限制您提出權限請求的次數。由于向用戶請求的權限數量和類型會影響下載量(與其他請求較少權限的類似應用相比),因此最好避免為不必要的功能請求權限。
改用 Intent
在許多情況下,要讓應用執行某項任務,有兩種方法供您選擇。應用可以要求提供權限來自行執行該任務,也可以使用 intent 讓其他應用執行該任務。
例如,假設應用需要使用設備攝像頭才能夠拍攝照片。應用可以請求 CAMERA 權限,以便允許應用直接訪問攝像頭。然后,應用將使用攝像頭 API 控制攝像頭并拍攝照片。此方法使應用能夠完全控制拍攝過程,并且您可以將攝像頭界面整合到應用中。
不過,如果您很少需要訪問用戶數據,換句話說,每次您需要訪問數據時都向用戶顯示運行時對話框,這種中斷操作并非不可接受,那么您可以使用基于 intent 的請求。Android 提供了一些系統 intent,借助這些 intent,應用無需請求權限,因為在發出基于 intent 的請求時用戶會選擇與應用共享的內容(如果有)。
例如,您可以使用 intent 操作類型 MediaStore.ACTION_IMAGE_CAPTURE 或 MediaStore.ACTION_VIDEO_CAPTURE 來拍攝圖像或視頻,而無需直接使用 Camera 對象(或請求權限)。在這種情況下,每次拍攝圖像時,系統 intent 都會代表您請求用戶提供權限。
同樣,如果您需要撥打電話、訪問用戶的聯系人或執行其他操作,您可以通過創建適當的 intent 來完成,也可以直接請求權限并訪問相應的對象。每種方法各有優缺點。
如果使用權限:
- 當您執行操作時,您的應用可以完全控制用戶體驗。不過,如此廣泛的控制會增加代碼的復雜性,因為您需要設計適當的界面。
- 系統會在運行時或安裝時(具體取決于用戶的 Android 版本)提示用戶授予權限一次。之后,應用即可執行操作,不再需要用戶進行其他互動。不過,如果用戶未授予權限(或之后撤消權限),則應用將根本無法執行操作。
如果使用 intent:
- 您不必為操作設計界面。處理 intent 的應用將提供界面。
- 用戶可以使用他們首選的應用執行任務。例如,用戶可以選擇用他們喜愛的照片應用拍照。
- 如果用戶沒有適用于操作的默認應用,則系統會提示用戶選擇一款應用。如果用戶未指定默認處理程序,則他們每次執行此操作時都可能必須處理一個額外的對話框。
不要讓用戶感到無所適從
如果用戶運行的是 Android 6.0(API 級別 23)或更高版本,則用戶必須在運行應用時為其授予權限。如果您讓用戶一次面對大量權限請求,可能會使用戶感到無所適從,導致他們退出您的應用。您應根據需要請求權限。
在某些情況下,一項或幾項權限可能對您的應用來說必不可少。在這種情況下,合理的做法是,在應用啟動之后立即請求提供所有這些權限。例如,如果您創建的是攝影應用,則該應用將需要訪問設備的攝像頭。當用戶首次啟動該應用時,系統會要求他們提供攝像頭使用權限,這不會令他們感到驚訝。但是,如果同一應用還具備與用戶的聯系人分享照片的功能,那么您或許不應在應用首次啟動時請求用戶提供 READ_CONTACTS 權限,而應等到用戶嘗試使用“分享”功能之后再請求該權限。
如果應用提供教程,則合理的做法是,在教程結束時請求提供應用的必要權限。
失去音頻焦點后暫停媒體
在這種情況下,用戶接到電話時您的應用需要轉入后臺,只有在通話停止后才會重新獲得焦點。
出現此類情況(例如,媒體播放器在通話期間靜音或暫停)時,通常采用的方法是使用 PhoneStateListener 或監聽 android.intent.action.PHONE_STATE 的廣播,以監聽通話狀態有無變化。這種解決方法的問題是它需要 READ_PHONE_STATE 權限,這將強制用戶授予對廣泛的敏感數據(如用戶的設備和 SIM 硬件 ID 以及來電的電話號碼)的訪問權限。此外,當應用在 Android 10(API 級別 29)或更高版本上運行,LISTEN_CELL_LOCATION 和 LISTEN_CELL_INFO 事件需要位置權限;尤其而言,當應用以 Android 10 或更高版本為目標時將需要 ACCESS_FINE_LOCATION。
您可以通過為應用請求 AudioFocus,在沒有 READ_PHONE_STATE 或 MODIFY_PHONE_STATE 權限的情況下檢測用戶是否在通話中,這么做不需要顯式權限,因為它不訪問敏感信息。只需將對音頻放入后臺所需的代碼放入 onAudioFocusChange() 事件處理程序,當操作系統轉換其音頻焦點時,它將自動運行。要詳細了解如何執行此操作,請參閱此文檔。
確定正在運行實例的設備
在這種情況下,您需要一個唯一標識符來確定您的應用實例正在哪個設備上運行。
應用可能具有設備特定的偏好設置或消息(例如,在云端為用戶保存設備特定的播放列表,以便他們在車上和家里可以有不同的播放列表)。常見的解決方案是利用設備標識符(如 Device IMEI),但這需要 Device ID and call information 權限組(M+ 中為 PHONE)。它還使用一個無法重置且在所有應用之間共享的標識符。
下面兩種方法可以替代這些類型的標識符:
- 使用
com.google.android.gms.iidInstanceID API。getInstance(Context context).getID()將為您的應用實例返回一個唯一設備標識符。結果得到一個應用實例作用域標識符,在存儲與應用有關的信息時可以將該標識符用作密鑰,在用戶重新安裝應用時此標識符將重置。 - 使用
randomUUID()之類的基本系統函數創建您自己的標識符,其作用域限定為應用的存儲空間。
為廣告或用戶分析創建唯一標識符
在這種情況下,您需要一個唯一標識符來為沒有登錄您的應用的用戶構建個人資料(例如,用于廣告定位或衡量轉化率)。
為廣告和用戶分析構建個人資料有時需要一個在其他應用之間共享的標識符。此問題的常見解決方案需要利用設備標識符(如 Device IMEI),這需要 Device ID and call information 權限組(API 級別 23+ 中為 PHONE),并且無法由用戶重置。無論是上述哪種情況,除了使用不可重置的標識符并請求用戶可能認為不尋常的權限外,還會違反 Play 開發者計劃政策。
遺憾的是,在這些情況下,使用 com.google.android.gms.iid InstanceID API 或系統函數創建應用作用域 ID 并不是適當的解決方案,因為可能需要在應用之間共享該 ID。一種替代解決方案是通過 getId() 方法使用 AdvertisingIdClient.Info 類提供的 Advertising Identifier。您可以使用 getAdvertisingIdInfo(Context) 方法創建一個 AdvertisingIdClient.Info 對象,并調用 getId() 方法來使用該標識符。請注意,此方法會造成堵塞,因此,您不應從主線程調用它;有關此方法的詳細說明請查看此處。
了解您正在使用的庫
有時,您在應用中使用的庫需要一些權限。例如,廣告和分析庫可能需要訪問 LOCATION 權限組以實現必需的功能。但從用戶的角度來看,權限請求來自于您的應用,而不是庫。
就像用戶會選擇使用較少權限即可實現相同功能的應用一樣,開發者也應檢查他們的庫,并選擇不會使用非必要權限的第三方 SDK。例如,如果您使用的庫提供了定位功能,請確保您不會請求 FINE_LOCATION 權限,除非您要使用基于位置的定位功能。
解釋為何需要權限
系統在您調用 requestPermissions() 時顯示的權限對話框將說明應用需要哪些權限,但不會解釋為何需要這些權限。在某些情況下,用戶可能會感到困惑。最好在調用 requestPermissions() 之前向用戶解釋應用需要相應權限的原因。
研究表明,如果用戶知道應用需要相應權限的原因,他們會更容易接受權限請求。用戶研究表明:
用戶是否愿意為某個移動應用授予給定權限,在很大程度上受此類權限關聯用途的影響。例如,用戶是否愿意授予訪問其位置的權限取決于該權限請求是否為支持應用的核心功能所必需,或者應用是否會與廣告網絡或分析公司分享此信息。1
卡內基梅隆大學 (CMU) 的 Jason Hong 教授根據他所帶領的小組的研究成果得出一個一般結論:
與只是告訴用戶應用正在使用其位置相比,如果用戶知道應用為什么使用像他們的位置這樣敏感的信息(例如,用于定向廣告),那么用戶會更容易接受。1
因此,如果您僅使用歸入權限組的一小部分 API 調用,明確列出您使用哪些權限以及使用原因會非常有用。例如:
- 如果您僅使用粗略位置,請在應用說明或應用幫助文檔中告知用戶。
-
如果您需要訪問短信以接收身份驗證碼,從而防止用戶被欺詐,請在應用說明中和/或首次訪問數據時告知用戶。
注意:如果應用面向 Android 8.0(API 級別 26)或更高版本,請不要在驗證用戶憑據過程中請求
READ_SMS權限,而應使用createAppSpecificSmsToken()生成應用特定的令牌,然后將此令牌傳遞給可以發送驗證短信的其他應用或服務。
在特定條件下,讓用戶實時了解應用在訪問敏感數據也是非常有益的。例如,如果應用要訪問攝像頭或麥克風,通常最好在應用中的某個位置或在通知托盤中(如果應用正在后臺運行)使用通知圖標告知用戶,這樣不會讓您看起來像是在暗自收集數據。
最后,如果您需要請求權限以便在應用中運行某項功能,但用戶不清楚原因,則需要找到一種方法讓用戶知道您為什么需要最敏感的權限。
測試兩種權限模式
自 Android 6.0(API 級別 23)起,用戶在運行時(而不是在安裝應用時)授予和撤消應用權限。因此,您必須在多種不同條件下測試應用。在低于 Android 6.0 的版本中,您可以合理地認為,如果應用能運行,它就已經獲得在應用清單中聲明的全部權限。自 Android 6.0 起,用戶可以開啟或關閉任何應用的權限,即使面向 API 級別 22 或更低級別的應用也是如此。您應測試以確保您的應用能正常運行,無論它是否具有任何權限。
以下提示可幫助您在搭載 API 級別 23 或更高級別的設備上找出與權限有關的代碼問題:
- 確定應用的當前權限和相關的代碼路徑。
- 在各種受權限保護的服務和數據中測試用戶流。
- 使用授予或撤消權限的各種組合進行測試。例如,相機應用可能會在其清單中列出
CAMERA、READ_CONTACTS和ACCESS_FINE_LOCATION。您應在測試該應用時逐一開啟和關閉這些權限,確保應用可以妥善處理所有權限配置。 - 使用 abd 工具從命令行管理權限:
- 按組列出權限和狀態:
$ adb shell pm list permissions -d -g
- 授予或撤消一項或多項權限:
$ adb shell pm [grant|revoke] <permission-name> ...
- 按組列出權限和狀態:
- 針對使用權限的服務對應用進行分析。

浙公網安備 33010602011771號