Android 4.2 API

API 級別:17

Android 4.2 (JELLY_BEAN_MR1) 是 Jelly Bean 的更新版本,可為使用者和應用程式開發人員提供新功能。本文件將介紹開發人員最著名、最實用的新 API。

應用程式開發人員應盡快從 SDK Manager 下載 Android 4.2 系統映像檔和 SDK 平台。如果您沒有搭載 Android 4.2 的裝置可以測試應用程式,請使用 Android 4.2 系統映像檔在 Android 模擬器上測試應用程式。然後針對 Android 4.2 平台建構應用程式,即可開始使用最新的 API。

如要針對搭載 Android 4.2 的裝置進一步最佳化應用程式,您應將 targetSdkVersion 設為 "17",在 Android 4.2 系統映像檔上安裝、進行測試,然後再透過這項變更發布更新。

如要在 Android 4.2 中使用 API,同時支援舊版 API,您可以在程式碼中新增條件,先檢查系統 API 級別再執行 minSdkVersion 不支援的 API。如要進一步瞭解如何維持回溯相容性,請參閱建立回溯相容的 UI

如要進一步瞭解 API 級別的運作方式,請參閱「什麼是 API 級別?」一文。

重要行為變更

如果您先前發布過 Android 應用程式,請注意以下可能會影響應用程式行為的變更:

  • 系統已不再預設匯出內容供應器。也就是說,android:exported 屬性的預設值是 “false"。若您有其他應用程式可存取內容供應器,現在必須明確設定 android:exported="true"

    只有在 android:targetSdkVersionandroid:minSdkVersion 設為 17 以上時,這項變更才會生效。否則,即使在 Android 4.2 以上版本中執行,預設值仍為 “true"

  • 與先前的 Android 版本相比,如果應用程式要求 ACCESS_COARSE_LOCATION 權限,但未要求 ACCESS_FINE_LOCATION 權限,使用者位置結果的精確度可能會降低。

    當應用程式要求取得概略位置 (而非精確位置資訊) 的權限時,為了滿足使用者的隱私權期望,系統不會提供比城市區塊更準確的使用者位置預測結果。

  • Settings.System 定義的部分裝置設定現在處於唯讀狀態。如果應用程式嘗試寫入 Settings.System 中定義的設定變更,且這些變更已移至 Settings.Global,則在 Android 4.2 以上版本中執行寫入作業會失敗,而且不會顯示相關通知。

    即使 android:targetSdkVersionandroid:minSdkVersion 的值低於 17,應用程式在 Android 4.2 以上版本中執行時,也無法修改移至 Settings.Global 的設定。

  • 如果您的應用程式使用 WebView,Android 4.2 會為 Android 程式碼多添一層防護,可讓您更安全地將 JavaScript 繫結至 Android 程式碼。如果將 targetSdkVersion 設為 17 以上,您就必須在想適用於 JavaScript 的任何方法中加入 @JavascriptInterface 註解 (該方法也必須公開)。如果未提供註解,則在 Android 4.2 以上版本中執行時,WebView 中的網頁無法存取這個方法。如果將 targetSdkVersion 設為 16 以下,則不一定要提供註解,但我們建議您更新目標版本並新增註解,進一步確保安全性。

    進一步瞭解如何將 JavaScript 程式碼繫結至 Android 程式碼

Daydream

Daydream 是適用於 Android 裝置的全新互動式螢幕保護程式模式。當裝置插入座架,或裝置在接上充電器時處於閒置狀態時 (而非關閉螢幕),Pixel 便會自動啟動。Daydream 一次顯示一個夢,可能是純視覺、被動顯示,使用者輕觸後就會關閉,或可能與整個輸入事件互動且回應。您的夢在應用程式的處理程序中執行,而且擁有 Android UI 工具包的完整存取權 (包括檢視畫面、版面配置和動畫),因此比動態桌布或應用程式小工具更靈活有力。

您可以實作 DreamService 的子類別,為 Daydream 建立夢幻。DreamService API 的設計與 Activity 的相似。為指定 UI 時,請在開啟視窗後 (例如透過 onAttachedToWindow() 回呼),將版面配置資源 ID 或 View 傳遞至 setContentView()

DreamService 類別會在基本 Service API 之上提供其他重要的生命週期回呼方法,例如 onDreamingStarted()onDreamingStopped()onDetachedFromWindow()。您無法從應用程式啟動 DreamService,因為系統會自動啟動這個程式。

如果您的夢想是互動式內容,可以從夢中啟動活動,讓使用者進入應用程式的完整使用者介面,以取得更多詳細資料或控制。您可以使用 finish() 結束夢境,讓使用者能查看新的活動。

如要在系統中使用您的 Daydream,請使用資訊清單檔案中的 <service> 元素宣告 DreamService。然後,您必須加入動作為 "android.service.dreams.DreamService" 的意圖篩選器。例如:

<service android:name=".MyDream" android:exported="true"
    android:icon="@drawable/dream_icon" android:label="@string/dream_label" >
    <intent-filter>
        <action android:name="android.service.dreams.DreamService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

此外,DreamService 中還有一些其他實用的方法需要注意:

  • setInteractive(boolean) 可控制 Dream 是否要在使用者輸入內容後立即接收輸入事件或結束。如果夢是互動,使用者可能會使用「Back」或「Home」按鈕退出夢,或是呼叫 finish() 來停止夢。
  • 如果您需要完全沉浸式顯示螢幕,可以呼叫 setFullscreen() 隱藏狀態列。
  • 在 Daydream 啟動前,螢幕會調暗,向使用者告知閒置逾時即將到來。呼叫 setScreenBright(true) 可改為將螢幕設為平常的亮度。

詳情請參閱 DreamService 說明文件。

第二螢幕

Android 現在允許應用程式在透過有線連線或 Wi-Fi 連線至使用者裝置的其他畫面上,顯示獨創內容。如要為次要螢幕建立不重複內容,請擴充 Presentation 類別並實作 onCreate() 回呼。在 onCreate() 中呼叫 setContentView(),為次要螢幕指定 UI。Presentation 類別是 Dialog 類別的擴充功能,提供應用程式在次要螢幕上顯示專屬 UI 的區域。

如要偵測可顯示 Presentation 的次要螢幕,請使用 DisplayManagerMediaRouter API。雖然 DisplayManager API 可讓您列舉可能一次連結的多個螢幕,但通常應使用 MediaRouter 快速存取系統的呈現預設顯示畫面。

如要取得簡報的預設顯示畫面,請呼叫 MediaRouter.getSelectedRoute() 並傳遞 ROUTE_TYPE_LIVE_VIDEO。這會回傳 MediaRouter.RouteInfo 物件,說明系統為影片呈現選擇的目前所選路線。如果 MediaRouter.RouteInfo 不是空值,請呼叫 getPresentationDisplay() 以取得 Display 代表已連接螢幕。

接著,只要將 Display 物件傳遞至 Presentation 類別的建構函式,即可顯示簡報。您的分享畫面現在會顯示在次要螢幕上

如要在連上新螢幕時在執行階段進行偵測,請建立 MediaRouter.SimpleCallback 的執行個體,並實作 onRoutePresentationDisplayChanged() 回呼方法,讓系統在連上新的顯示畫面時呼叫該方法。接著將 MediaRouter.SimpleCallbackROUTE_TYPE_LIVE_VIDEO 路線類型傳送給 MediaRouter.addCallback() 進行註冊。當您收到 onRoutePresentationDisplayChanged() 的呼叫時,只要按照上述方式呼叫 MediaRouter.getSelectedRoute() 即可。

如要針對次要畫面進一步最佳化 Presentation 中的使用者介面,您可以在套用至應用程式或活動的 <style> 中指定 android:presentationTheme 屬性,藉此套用不同的主題。

請注意,使用者裝置連接的螢幕通常尺寸較大,且螢幕密度可能不同。由於螢幕特性可能各有不同,因此您應提供針對這類較大螢幕最佳化的資源。如果需要向 Presentation 要求其他資源,請呼叫 getContext().getResources() 以取得與螢幕對應的 Resources 物件。這樣就能從應用程式中提供最適合次要螢幕的螢幕大小和密度資源。

如需詳細資訊和部分程式碼範例,請參閱 Presentation 類別說明文件。

螢幕鎖定小工具

Android 現在允許使用者將應用程式小工具新增至螢幕鎖定畫面。如要在螢幕鎖定畫面上使用應用程式小工具,請在指定 AppWidgetProviderInfo 的 XML 檔案中加入 android:widgetCategory 屬性。這個屬性支援兩個值:home_screenkeyguard。這項屬性預設為 home_screen,方便使用者將應用程式小工具新增至主畫面。如要讓螢幕鎖定畫面顯示應用程式小工具,請新增 keyguard 值:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:widgetCategory="keyguard|home_screen">
</appwidget-provider>

此外,在螢幕鎖定畫面中使用 android:initialKeyguardLayout 屬性時,您也應該為應用程式小工具指定初始版面配置。其運作方式與 android:initialLayout 相同,因為它會提供一個版面配置,在應用程式小工具初始化並能夠更新版面配置前會立即顯示。

如要進一步瞭解如何建構適用於螢幕鎖定畫面的應用程式小工具,包括如何在螢幕鎖定畫面上調整應用程式小工具的大小,請參閱「應用程式小工具」指南。

多位使用者

Android 現在允許在平板電腦等共用裝置上建立多個使用者空間。裝置上的每位使用者都有自己的一組帳戶、應用程式、系統設定、檔案,以及其他與使用者相關聯的資料。

應用程式開發人員不必採取其他動作,就能讓應用程式在單一裝置上正常與多位使用者搭配運作。無論裝置上有多少使用者,您的應用程式為特定使用者儲存的資料會與應用程式為其他使用者儲存的資料分開。系統會追蹤哪些使用者資料屬於執行應用程式的使用者程序,並僅允許應用程式存取該使用者的資料,同時禁止存取其他使用者的資料。

將資料儲存在多使用者環境中

每當應用程式儲存使用者偏好設定、建立資料庫,或是將檔案寫入使用者的內部或外部儲存空間時,這些資料只能在以該使用者身分執行時存取。

為確保應用程式能在多使用者環境中正確運作,請勿使用硬式編碼路徑參照內部應用程式目錄或外部儲存空間位置,而是一律使用適當的 API:

無論您使用何種 API 儲存特定使用者的資料,在以其他使用者的身分執行時,您將無法存取這些資料。從應用程式的角度來看,每位使用者都是在一部完全獨立的裝置上執行。

識別多使用者環境中的使用者

如果您的應用程式想要識別不重複使用者,例如收集數據分析資料或建立其他帳戶關聯,請按照建議的做法,瞭解如何識別不重複的安裝數。只要在應用程式首次啟動時建立新的 UUID,就能取得一個專屬 ID,用來追蹤每位使用者,無論有多少使用者在單一裝置上安裝應用程式。或者,您也可以儲存從伺服器擷取的本機權杖,或使用 Google 雲端通訊提供的註冊 ID。

請注意,如果應用程式要求其中一個硬體裝置 ID (例如 Wi-Fi MAC 位址或 SERIAL 號碼),這些 ID 就會為每位使用者提供相同的值,因為這些 ID 是連結至硬體,而非使用者。不會提及這些 ID 導入的其他問題,詳情請參閱「識別應用程式安裝」網誌文章。

新版通用設定

我們已更新系統設定,支援新增 Settings.Global 的使用者。此集合與 Settings.Secure 設定類似,因為它們是唯讀狀態,但會套用至裝置上的所有使用者空間。

部分現有設定已從 Settings.SystemSettings.Secure 移到這裡。如果應用程式正在變更先前在 Settings.System 中定義的設定 (例如 AIRPLANE_MODE_ON),如果這些設定移至 Settings.Global,執行 Android 4.2 或以上版本的裝置上應該不會再有該設定。您可以繼續讀取 Settings.Global 中的設定,但由於這項設定對於應用程式變更已不符合安全考量,因此如果嘗試這麼做,系統將無法顯示通知,而且當您在 Android 4.2 以上版本執行應用程式時,系統會在系統記錄中寫入警告。

支援 RTL 版面配置

Android 現在提供多個 API,可讓您建構使用者介面來優雅轉換版面配置方向,支援使用從右至左 (RTL) UI 和閱讀方向的語言,例如阿拉伯文和希伯來文。

如要開始在應用程式中支援 RTL 版面配置,請將 android:supportsRtl 屬性設為資訊清單檔案中的 <application> 元素,並將其設為 “true"。啟用這項功能後,系統將啟用各種 RTL API,以 RTL 版面配置顯示應用程式。舉例來說,動作列會在右側顯示圖示和標題,並在左側顯示動作按鈕,使用架構提供的 View 類別建立的所有版面配置也會還原。

如果需要進一步最佳化使用 RTL 版面配置顯示的應用程式外觀,有以下兩個基本層級的最佳化功能:

  1. 將左側和右側版面配置屬性轉換為起始和結束的版面配置屬性。

    舉例來說,使用 android:layout_marginStart 取代 android:layout_marginLeftandroid:layout_marginEnd 取代 android:layout_marginRight

    RelativeLayout 類別也提供對應的版面配置屬性,用於取代左側/右側位置,例如 android:layout_alignParentStart 以取代 android:layout_alignParentLeftandroid:layout_toStartOf,而不是 android:layout_toLeftOf

  2. 或者,如要針對 RTL 版面配置提供完整最佳化功能,您可以使用 ldrtl 資源限定詞提供完全獨立的版面配置檔案 (ldrtl 代表 layout-direction-right-to-left)。舉例來說,您可以將預設版面配置檔案儲存在 res/layout/ 中,並將 RTL 最佳化版面配置儲存在 res/layout-ldrtl/ 中。

    ldrtl 限定詞適用於可繪製資源,因此可提供方向與閱讀方向對應的方向圖形。

架構中提供多種支援 RTL 版面配置的 API (例如 View 類別),因此您可以為自訂檢視區塊實作適當的行為,並在 Configuration 中查詢目前的版面配置方向。

注意:如果您使用的是 SQlite,資料表或資料欄名稱只有「number」時,請留意:如果裝置已設為阿拉伯文語言代碼,則使用 String.format(String, Object...) 可能會導致數字已轉換成阿拉伯文的對等項目。您必須使用 String.format(Locale,String,Object...),確保數字會以 ASCII 的形式保留。此外,設定數字格式時,也請使用 String.format("%d", int),而非使用 String.valueOf(int)

巢狀片段

您現在可以在片段中嵌入片段。當您想將動態和可重複使用的 UI 元件放入本身動態且可重複使用的 UI 元件時,這種做法適用於多種情況。舉例來說,如果您使用 ViewPager 建立分別用於左右滑動並佔用大部分螢幕空間的片段,現在可以將片段插入各個片段頁面。

如要建立巢狀片段,只要在要新增片段的 Fragment 上呼叫 getChildFragmentManager() 即可。這樣做會傳回一個 FragmentManager,您可以像平常在頂層活動一樣建立片段交易。例如,以下程式碼會從現有 Fragment 類別中新增片段:

Kotlin

val videoFragment = VideoPlayerFragment()
childFragmentManager.beginTransaction().apply {
    add(R.id.video_fragment, videoFragment)
    commit()
}

Java

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

在巢狀片段中,您可以呼叫 getParentFragment() 取得父項片段的參照。

Android 支援資料庫現在也支援巢狀片段,因此您可以在 Android 1.6 以上版本中實作巢狀片段設計。

注意:如果版面配置包含 <fragment>,則無法將版面配置加載至片段中。巢狀片段只有在以動態方式新增至片段時才能支援。

RenderScript

Renderscript 運算功能已強化以下功能:

指令碼內建函式

您可以使用 Renderscript 的內建指令碼內建函式來執行常見作業,例如:

如要使用指令碼內建函式,請針對每個內建函式呼叫靜態 create() 方法,建立指令碼的執行個體。接著,您可以呼叫每個指令碼內建函式的可用 set() 方法,以設定任何必要的輸入和選項。最後,請呼叫 forEach() 方法來執行指令碼。

指令碼群組

ScriptGroup 可讓您將相關的 Renderscript 指令碼鏈結在一起,並透過單一呼叫執行。

使用 ScriptGroup.Builder 呼叫 addKernel(),將所有指令碼新增至群組。加入所有指令碼後,請呼叫 addConnection() 在指令碼之間建立連線。新增連線後,請呼叫 create() 建立指令碼群組。執行指令碼群組之前,請指定要使用 setInput(Script.KernelID, Allocation) 方法執行的輸入 Allocation 和初始指令碼,並提供輸出 Allocation (這會寫入結果),並提供要用 setOutput() 執行的最終指令碼。最後,呼叫 execute() 來執行指令碼群組。

Filterscript

Filterscript 定義現有 Renderscript API 的限制,允許產生的程式碼在更多種處理器 (CPU、GPU 和 DSP) 上執行。如要建立 Filterscript 檔案,請建立 .fs 檔案 (而非 .rs 檔案),並指定 #pragma rs_fp_relaxed,告知 Renderscript 執行階段不需要嚴格的 IEEE 754-2008 浮點精確度。這種精確度允許將非正規數清除至零,以及朝零捨去。此外,Filterscript 指令碼不得使用 32 位元的內建類型,而且必須使用 __attribute__((kernel)) 屬性指定自訂根函式,因為 Filterscript 不支援指標 (即 root() 函式的預設簽名定義)。

注意:雖然平台支援 Filterscript,但開發人員將在 SDK 工具版本 21.0.1 中提供開發人員支援。

如需 Android 4.2 所有 API 變更的詳細資訊,請參閱 API 差異報告