Android應用基礎知識

葉應是葉發表於2017-02-18

Android 應用採用 Java 程式語言編寫,Android SDK 工具將程式碼連同任何資料和資原始檔編譯到一個 APK(Android 軟體包)當中,即帶有 .apk 字尾的存檔檔案中。一個 APK 檔案包含 Android 應用的所有內容,它是基於 Android 系統的裝置用來安裝應用的檔案

安裝到裝置後,每個 Android 應用都執行在自己的安全沙箱內:

  • Android 作業系統是一種多使用者 Linux 系統,其中的每個應用都是一個不同的使用者
  • 預設情況下,系統會為每個應用分配一個唯一的 Linux 使用者 ID(該 ID 僅由系統使用,應用並不知曉)。系統為應用中的所有檔案設定許可權,使得只有分配給該應用的使用者 ID 才能訪問這些檔案
  • 每個程式都具有自己的虛擬機器 (VM),因此應用程式碼是在與其他應用隔離的環境中執行

Android 系統可以通過這種方式實現最小許可權原則。也就是說,預設情況下,每個應用都只能訪問執行其工作所需的元件,而不能訪問其他元件。 這樣便營造出一個非常安全的環境,在這個環境中,應用無法訪問系統中其未獲得許可權的部分

不過,應用仍然可以通過一些途徑與其他應用共享資料以及訪問系統服務:

  • 可以安排兩個應用共享同一 Linux 使用者 ID,在這種情況下,它們能夠相互訪問彼此的檔案。 為了節省系統資源,可以安排具有相同使用者 ID 的應用在同一 Linux 程式中執行,並共享同一 VM(應用還必須使用相同的證照籤署)
  • 應用可以請求訪問裝置資料(如使用者的聯絡人、簡訊、可裝載儲存裝置 [SD 卡]、相機、藍芽等)的許可權。 使用者必須明確授予這些許可權

一、核心框架元件

應用元件是 Android 應用的基本構建基塊。每個元件都是一個不同的點,系統可以通過它進入應用中。 並非所有元件都是使用者的實際入口點,有些元件相互依賴,但每個元件都以獨立實體形式存在,併發揮特定作用 — 每個元件都是唯一的構建基塊,有助於定義應用的總體行為

共有四種不同的應用元件型別。每種型別都服務於不同的目的,並且具有定義元件的建立和銷燬方式的不同生命週期

以下便是這四種應用元件型別:

Activity

Activity 表示具有使用者介面的單一螢幕。例如,電子郵件應用可能具有一個顯示新電子郵件列表的 Activity、一個用於撰寫電子郵件的 Activity 以及一個用於閱讀電子郵件的 Activity。 儘管這些 Activity 通過協作在電子郵件應用中形成了一種緊密結合的使用者體驗,但每一個 Activity 都獨立於其他 Activity 而存在。 因此,其他應用可以啟動其中任何一個 Activity(如果電子郵件應用允許)。 例如,相機應用可以啟動電子郵件應用內用於撰寫新電子郵件的 Activity,以便使用者共享圖片

服務

服務是一種在後臺執行的元件,用於執行長時間執行的操作或為遠端程式執行作業。 服務不提供使用者介面。 例如,當使用者位於其他應用中時,服務可能在後臺播放音樂或者通過網路獲取資料,但不會阻斷使用者與 Activity 的互動。 諸如 Activity 等其他元件可以啟動服務,讓其執行或與其繫結以便與其進行互動

內容提供者

內容提供程式管理一組共享的應用資料,可以將資料儲存在檔案系統、SQLite 資料庫、網路上或應用可以訪問的任何其他永久性儲存位置。 其他應用可以通過內容提供程式查詢資料,甚至修改資料(如果內容提供程式允許)。 例如,Android 系統可提供管理使用者聯絡人資訊的內容提供程式。 因此,任何具有適當許可權的應用都可以查詢內容提供程式的某一部分(如 ContactsContract.Data),以讀取和寫入有關特定人員的資訊
內容提供程式也適用於讀取和寫入您的應用不共享的私有資料。 例如,記事本示例應用使用內容提供程式來儲存筆記

廣播接收器

廣播接收器是一種用於響應系統範圍廣播通知的元件。 許多廣播都是由系統發起的 。例如,通知螢幕已關閉、電池電量不足或已拍攝照片的廣播。應用也可以發起廣播 ,例如,通知其他應用某些資料已下載至裝置,並且可供其使用。 儘管廣播接收器不會顯示使用者介面,但它們可以建立狀態列通知,在發生廣播事件時提醒使用者。 但廣播接收器更常見的用途只是作為通向其他元件的“通道”,設計用於執行極少量的工作。 例如,它可能會基於事件發起一項服務來執行某項工作

Android 系統設計的獨特之處在於,任何應用都可以啟動其他應用的元件。 例如,如果想讓使用者使用裝置的相機拍攝照片,很可能有另一個應用可以執行該操作,那麼你的應用就可以利用該應用,而不是開發一個 Activity 來自行拍攝照片。 不需要整合甚至連結到該相機應用的程式碼,而是隻需啟動拍攝照片的相機應用中的 Activity。 完成拍攝時,系統甚至會將照片返回您的應用,以便應用使用。對使用者而言,就好像相機真正是應用的組成部分。
當系統啟動某個元件時,會啟動該應用的程式(如果尚未執行),並例項化該元件所需的類。 例如,如果你的應用啟動相機應用中拍攝照片的 Activity,則該 Activity 會在屬於相機應用的程式,而不是你的應用的程式中執行。因此,與大多數其他系統上的應用不同,Android 應用並沒有單一入口點(例如,沒有 main() 函式)

由於系統在單獨的程式中執行每個應用,且其檔案許可權會限制對其他應用的訪問,因此你的應用無法直接啟動其他應用中的元件, 但 Android 系統卻可以。因此,要想啟動其他應用中的元件,你必須向系統傳遞一則訊息,說明想啟動特定元件的 Intent。 系統隨後便會為你啟動該元件

二、Intent

四種元件型別中的三種:Activity、服務和廣播接收器 ,都通過名為 Intent 的非同步訊息進行啟動。Intent 會在執行時將各個元件相互繫結(可以將 Intent 視為從其他元件請求操作的信使),無論元件屬於你的應用還是其他應用。

Intent 使用 Intent 物件建立,它定義的訊息用於啟動特定元件或特定型別的元件 — Intent 可以是顯式的,也可以是隱式的

對於 Activity 和服務, Intent 定義要執行的操作(例如,“檢視”或“傳送”某個內容),並且可以指定要執行操作的資料的 URI(以及正在啟動的元件可能需要了解的資訊)。 例如, Intent 傳達的請求可以是啟動一個顯示影像或開啟網頁的 Activity。 在某些情況下,可以啟動 Activity 來接收結果,在這種情況下,Activity 也會在 Intent 中返回結果(例如,可以發出一個 Intent,讓使用者選取某位聯絡人並將其返回給應用,返回 Intent 包括指向所選聯絡人的 URI)

對於廣播接收器, Intent 只會定義要廣播的通知(例如,指示裝置電池電量不足的廣播只包括指示“電池電量不足”的已知操作字串)

Intent 不會啟動另一個元件型別:內容提供程式,後者會在成為 ContentResolver 的請求目標時啟動。 內容解析程式通過內容提供程式處理所有直接事務,使得通過提供程式執行事務的元件可以無需執行事務,而是改為在 ContentResolver 物件上呼叫方法。 這會在內容提供程式與請求資訊的元件之間留出一個抽象層(以確保安全)

每種型別的元件有不同的啟動方法:

  • 可以通過將 Intent 傳遞到 startActivity() 或 startActivityForResult()(當想讓 Activity 返回結果時使用)來啟動 Activity(或為其安排新任務)
  • 可以通過將 Intent 傳遞到 startService() 來啟動服務(或對執行中的服務下達新指令)。 或者,也可以通過將 Intent 傳遞到 bindService() 來繫結到該服務
  • 可以通過將 Intent 傳遞到 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast() 等方法來發起廣播
  • 可以通過在 ContentResolver 上呼叫 query() 來對內容提供程式執行查詢

三、清單檔案

在 Android 系統啟動應用元件之前,系統必須通過讀取應用的 AndroidManifest.xml 檔案(”清單”檔案)確認元件存在。應用必須在此檔案中宣告其所有元件,該檔案必須位於應用專案目錄的根目錄中。

除了宣告應用的元件外,清單檔案還有許多其他作用,如:

  • 確定應用需要的任何使用者許可權,如網際網路訪問許可權或對使用者聯絡人的讀取許可權
  • 根據應用使用的 API,宣告應用所需的最低 API 級別
  • 宣告應用使用或需要的硬體和軟體功能,如相機、藍芽服務或多點觸控螢幕
  • 應用需要連結的 API 庫(Android 框架 API 除外),如 Google 地相簿
  • 其他功能

四、宣告元件

清單檔案的主要任務是告知系統有關應用元件的資訊。例如,清單檔案可以像下面這樣宣告 Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >
        </activity>
        ...
    </application>
</manifest>

< application >元素中,android:icon 屬性指向標識應用的圖示所對應的資源
< activity >元素中,android:name 屬性指定 Activity 子類的完全限定類名,android:label 屬性指定用作 Activity 的使用者可見標籤的字串。

通過以下方式宣告所有應用元件:

  • Activity 的 < activity > 元素
  • 服務的 < service > 元素
  • 廣播接收器的 < receiver > 元素
  • 內容提供者的 < provider > 元素

包含在原始碼中,但未在清單檔案中宣告的 Activity、服務和內容提供者對系統不可見,因此也永遠不會執行。 不過,廣播接收器可以在清單檔案中宣告或在程式碼中動態建立(如 BroadcastReceiver 物件)並通過呼叫 registerReceiver() 在系統中註冊

五、宣告元件功能

如上文啟動元件中所述,可以使用 Intent 來啟動 Activity、服務和廣播接收器。 你可以通過在 Intent 中顯式命名目標元件(使用元件類名)來執行此操作。 不過,Intent 的真正強大之處在於隱式 Intent 概念。 隱式 Intent 的作用無非是描述要執行的操作型別(還可選擇描述想執行的操作所針對的資料),讓系統能夠在裝置上找到可執行該操作的元件,並啟動該元件。 如果有多個元件可以執行 Intent 所描述的操作,則由使用者選擇使用哪一個元件

系統通過將接收到的 Intent 與裝置上的其他應用的清單檔案中提供的 Intent 過濾器進行比較來確定可以響應 Intent 的元件。

當在應用的清單檔案中宣告 Activity 時,可以選擇性地加入宣告 Activity 功能的 Intent 過濾器,以便響應來自其他應用的 Intent。 可以通過將 < intent-filter > 元素作為元件宣告元素的子項進行新增來為元件宣告 Intent 過濾器

例如,如果你開發的電子郵件應用包含一個用於撰寫新電子郵件的 Activity,則可以像下面這樣宣告一個 Intent 過濾器來響應“send” Intent(以傳送新電子郵件):

<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

然後,如果另一個應用建立了一個包含ACTION_SEND操作的 Intent,並將其傳遞到 startActivity(),則系統可能會啟動你的 Activity(因為系統中可能有多個應用能提供該服務,所以需要使用者來選定使用哪個Activity),以便使用者能夠用來傳送電子郵件

六、宣告應用要求

基於 Android 系統的裝置多種多樣,並非所有裝置都提供相同的特性和功能。 為防止將應用安裝在缺少應用所需特性的裝置上,必須通過在清單檔案中宣告裝置和軟體要求,為應用支援的裝置型別明確定義一個配置檔案。 其中的大多數宣告只是為了提供資訊,系統不會讀取它們,但 Google Play 等外部服務會讀取它們,以便當使用者在其裝置中搜尋應用時為使用者提供過濾功能

例如,如果應用需要相機,並使用 Android 2.1(API 級別 7)中引入的 API,則應該像下面這樣在清單檔案中以要求形式宣告這些資訊:

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    ...
</manifest>

現在,沒有相機且 Android 版本低於 2.1 的裝置將無法從 Google Play 安裝該應用。

不過,你也可以宣告應用有使用到相機,但並不要求必須使用。 在這種情況下,應用必須將 required 屬性設定為 “false”,並在執行時檢查裝置是否具有相機,然後根據需要停用任何相機功能

七、應用資源

Android 應用並非只包含程式碼,它還需要與原始碼分離的資源,如影像、音訊檔案以及任何與應用的視覺呈現有關的內容。 例如,你應該通過 XML 檔案定義 Activity 使用者介面的動畫、選單、樣式、顏色和佈局。 使用應用資源能夠在不修改程式碼的情況下輕鬆地更新應用的各種特性,並可通過提供備用資源集讓你能夠針對各種裝置配置(如不同的語言和螢幕尺寸)優化應用。

對於Android 專案中包括的每一項資源,SDK 構建工具都會定義一個唯一的整型 ID,你可以利用它來引用應用程式碼或 XML 中定義的其他資源中的資源。 例如,如果應用包含一個名為 logo.png 的影像檔案(儲存在 res/drawable/ 目錄中),則 SDK 工具會生成一個名為 R.drawable.logo 的資源 ID,可以利用它來引用該影像並將其插入使用者介面當中

提供與原始碼分離的資源的其中一個最重要優點在於,您可以提供針對不同裝置配置的備用資源。 例如,通過在 XML 中定義 UI 字串,可以將字串翻譯為其他語言,並將這些字串儲存在單獨的檔案中。 然後,Android 系統會根據向資源目錄名稱追加的語言限定符(如為法語字串值追加 res/values-fr/)和使用者的語言設定,應用相應的語言字串

Android 支援許多不同的備用資源限定符。限定符是一種加入到資源目錄名稱中,用來定義這些資源適用的裝置配置的簡短字串。 再舉一例,你應該經常會根據裝置的螢幕方向和尺寸為 Activity 建立不同的佈局。 例如,當裝置螢幕為縱向(長型)時,你可能想要一種垂直排列按鈕的佈局;但當螢幕為橫向(寬型)時,應按水平方向排列按鈕。 要想根據方向更改佈局,你可以定義兩種不同的佈局,然後對每個佈局的目錄名稱應用相應的限定符。 然後,系統會根據當前裝置方向自動應用相應的佈局


相關文章