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 Emulator 中測試應用程式。然後針對 Android 4.2 平台建構應用程式,以便開始使用最新的 API。

為了針對 Android 4.2 的裝置最佳化您的應用程式,您應將 targetSdkVersion 設定為 "17",在 Android 4.2 系統映像檔中進行安裝,然後透過此變更發布更新。

您可以在 Android 4.2 中使用 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 會多添一層安全保障,您可以更安全將 JavaScript 繫結至 Android 程式碼。如果您將 targetSdkVersion 設為 17 以上,就必須將 @JavascriptInterface 註解新增至要提供給 JavaScript 的任何方法 (該方法也必須公開)。如果您未提供註解,則在 Android 4.2 以上版本執行時,WebView 中的網頁無法存取此方法。如果您將 targetSdkVersion 設為 16 以下,則註解並非必要,但為了加強安全性,建議您更新目標版本,並新增註解。

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

Daydream

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

您可以實作 DreamService 的子類別,為 Daydream 建立 Dream。DreamService API 的設計與 Activity 類似。如果要為動態指定 UI,請在出現視窗後隨時傳遞版面配置資源 ID 或 ViewsetContentView(),例如從 onAttachedToWindow() 回呼。

DreamService 類別除了提供基礎 Service API 之外,還提供其他重要生命週期回呼方法,例如 onDreamingStarted()onDreamingStopped()onDetachedFromWindow()。您無法從應用程式啟動 DreamService,因為系統會自動啟動它。

如果您夢想具有互動性,可以從 夢中啟動活動,將使用者傳送到應用程式的完整 UI,以便進行更多細節或控制。您可以使用 finish() 結束 Dream,以便使用者查看新的 Activity。

如要讓系統可以使用你的 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() 來停止 Dream。
  • 如果想獲得完整的沉浸式顯示畫面,可以呼叫 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.SimpleCallback 連同 ROUTE_TYPE_LIVE_VIDEO 路徑類型傳遞至 MediaRouter.addCallback(),即可註冊。當您收到對 onRoutePresentationDisplayChanged() 的呼叫時,只要按照上述方式呼叫 MediaRouter.getSelectedRoute() 即可。

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

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

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

螢幕鎖定小工具

Android 現在允許使用者將應用程式小工具新增至螢幕鎖定畫面。如要在螢幕鎖定時使用應用程式小工具,請將 android:widgetCategory 屬性新增至指定 AppWidgetProviderInfo 的 XML 檔案。這項屬性支援兩個值: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 Cloud Messaging 提供的註冊 ID。

請注意,如果您的應用程式要求其中一種硬體裝置 ID (例如 Wi-Fi MAC 位址或 SERIAL 號碼),這些 ID 會與硬體連結,而非使用者,因此每位使用者都會提供相同的值。更不用說,這些 ID 引入的其他問題,也如「識別應用程式安裝」網誌文章所述。

新全域設定

系統設定已更新,現在新增 Settings.Global 後,就能支援多位使用者。這組設定與 Settings.Secure 設定很類似,因為這些設定處於唯讀狀態,但會套用至裝置上所有使用者空間。

部分現有設定已從 Settings.SystemSettings.Secure 搬遷至此。如果應用程式目前會變更先前在 Settings.System 中定義的設定 (例如 AIRPLANE_MODE_ON),則在搭載 Android 4.2 以上版本的裝置上,如果這些設定已移至 Settings.Global,則無法繼續運作。您可以繼續讀取 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 限定詞適用於可繪製資源,因此您可以提供方向與閱讀方向相對應的圖形。

整個架構中還提供各種其他 API,藉此支援 RTL 版面配置 (例如 View 類別),方便您為自訂檢視區塊實作適當的行為,並在 Configuration 中查詢目前的版面配置方向。

注意:如果您使用 SQlite 且有「數字」列的資料表或欄名稱,請小心謹慎:如果裝置已設為阿拉伯文語言代碼,使用 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 差異報告