在上一篇文章中已經提到,Android系統加載應用程序之后,首先會讀取該應用程序的AndroidManifest.xml清單文件,之后根據該清單文件加載后邊的東西。所以要開發應用程序,自然要先知道清單文件中都記錄了什么東西。一般地,在清單文件中聲明定義的內容,稱為靜態注冊,相對應地,可以在代碼中定義的內容,稱為動態注冊。
清單文件的存儲位置就是應用程序的根目錄,而且文件名也是固定的,必須為AndroidManifest.xml,清單文件中所包含的內容在Android官網應用清單文件中可查。其中的內容主要有以下三部分。
應用基本信息
package應用包名屬性,該屬性要保證系統唯一性,也就是說應用程序所運行的Android機器上,不能有相同包名的兩個應用程序。如果想把兩個相同包名的應用程序安裝到同一台Android機器上,那是連安裝這一步都無法成功的。
在開發環境AndroidStudio上,建議清單文件中的package屬性與主應用module下的build.gradle文件中的applicationId保持一致,當然官網也給出了不一致的修改方案,但是開發過程中除非有特殊需求,否則不推薦。
versionCode和versionName兩個屬性分別標記了應用的版本信息,其中versionCode可以在Android系統的設置-應用設置-該應用信息中展示,而versionName只能在應用內部顯示調用展示。
在開發環境AndroidStudio上,這兩個屬性可以通過主應用module下的build.gradle文件直接配置。
上邊這些作為基本信息,在Android系統加載該應用時首先加載使用。簡而言之,package保證了Android系統上運行的軟件與軟件之間的唯一性,而versionCode保證了Android系統上所運行的同一個軟件在不同版本之間的唯一性。有了這些基礎信息,就可以確定下邊要加載的其他內容了。
權限聲明
應用程序可能需要聯網操作,或者訪問Android系統上的系統級應用中的內容,比如通訊錄信息,這些可能涉及到用戶隱私的操作和數據,統一歸納為應用權限。
應用程序所使用到的權限都要在清單文件中以<uses-permisson />標簽形式聲明,在name屬性值中填入相關權限名。這里的權限名不僅可以使用Android系統已經提供的權限,還可以使用當前應用程序的自定義權限和其他應用程序的自定義權限。自定義權限同樣是在清單文件這里定義,使用<permission>標簽填寫相關信息即可。
值得注意的是,從Android6.0開始,應用程序用到的危險權限不僅要在清單文件中聲明,而且在涉及該危險權限的代碼調用之前也要動態檢查申請。可查詢Android官網權限列表中Protection level: dangerous類型的權限即為危險權限。
在動態申請權限時,可使用checkSelfPermission(String permission)
檢查應用是否獲得相關權限,如果用戶沒有授權,就需要使用requestPermission(@NonNull String[] permissions, int requestCode)
申請相關權限,並在Activity中重載onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
以獲得用戶對權限申請的處理結果。
在Android系統檢查並授予當前應用相關權限之后,就可以啟動當前應用程序了。每個應用程序對應AndroidSDK中的android.app.Application
類,Application
的生命周期即從應用程序啟動開始,當Android系統因為內存過低或電源優化時,被動殺死應用程序結束,也可能是應用程序主動退出結束。
設備兼容
Android系統所搭載的硬件設備是千差萬別的,而且同一個應用也可能在不同的AndroidSDK的版本上運行,這也就是經常談到的碎片化問題。如果能在清單文件中聲明當前應用程序需要具備哪些基礎硬件,或者可以在哪些AndroidSDK版本上運行,那就可以在不符合要求的Android系統上禁止安裝該應用程序。
使用<users-feature />標簽可以聲明應用程序所需要的一些硬件要求。
使用<users-sdk />標簽可以聲明應用程序所需要的AndroidSDK版本要求,該標簽下主要有、targetSdkVersion、maxSdkVersion、targetSdkVersion三個屬性值,分別對應應用程序所運行AndroidSdk的最低版本,最高版本,目標版本。在開發環境AndroidStudio上,minSdkVersion和targetSdkVersion這兩個屬性可以通過主應用module下的build.gradle文件直接配置。
組件聲明
Android系統的實現就是為了更好的和人類交流,交流的途徑主要有四種方式,分別是界面操作(以android.app.Activity
類為載體),后台服務(以android.app.Service
類為載體),廣播通知(以android.app.BroadcastReceiver
類為載體),數據交換(以android.app.ContentProvider
類為載體),將這四種交流方式所依賴的載體統一稱為應用組件,應用組件是依賴於當前應用Application
的,所以組件的生命周期只能小於等於當前應用生命周期。
Application
對應於清單文件中的<application></application>標簽。該標簽下有icon屬性加載資源文件下的圖標,作為應用程序在Android系統的顯示圖標;label屬性則是加載資源文件下的字符串,作為應用程序在Android系統中顯示的應用名;name屬性作為可填項,可以使用繼承自android.app.Application
的自定義類,如果不填該屬性值,則默認為android.app.Application
。另外還有其他幾個屬性值都可以在Android官網清單文件-application標簽中查詢,通過這些屬性值配置,可以更方便的指導Android系統管理當前應用程序。
在當前應用確定以后,就需要分別加載該應用下的組件信息了,應用程序只有一個,但是組件可以有多個,只要名稱不沖突即可,所以組件的聲明是嵌套在<application></application>標簽內部的,下面是每種組件在AndroidManifest.xml中對應的標簽名。
組件名 | Activity | Service | BroadcastReceiver | ContentProvider |
---|---|---|---|---|
清單文件標簽名 | activity | service | receiver | provider |
組件標簽內必須要有name屬性,以指向代碼中自定義的組件類,值得注意的是,一個應用程序中,可以有多個界面,多個服務,多個廣播接收器,多個數據提供者,所以name屬性就是為了區分多個相同種類組件的。
provider標簽中還必須要有authorities屬性值,這是由於ContentProvider
是對其他應用提供數據,這就好像該應用將數據保存到一個保險箱中提供給其他應用,而其他應用必須有該保險箱的密碼才能打開保險箱以訪問該應用的數據,而authorities正是起到這個密碼的類似效果。
activity標簽,service標簽,provider標簽,還可以內嵌<intent-filter><\intent-filter>
標簽作為組件內信息,使用意圖過濾標記的組件,可以在代碼中快速響應該意圖,執行響應后邏輯。
啟動應用程序后,必須要加載一個Activity
界面,而加載哪一個呢?這也就用到<intent-filter><\intent-filter>
標簽了,只有在該標簽內嵌套了<action android:name="android.intent.action.MAIN" />
和<category android:name="android.intent.category.LAUNCHER" />
兩個固定標簽內容的<activity><\activity>
,才允許作為第一個界面加載。所以很自然的想到,一個<application><\application>
標簽內只允許有一個<activity><\activity>
可以嵌套上面的意圖過濾器內容。
至此,Android系統對應用程序的清單文件基本解析之后,就獲取了該應用的所有靜態信息,當應用安裝之后,就可以在桌面Launcher應用程序中顯示該應用,等待用戶點擊啟動該應用了。