1. 程式人生 > >Android7.0新特性,及Android N適配

Android7.0新特性,及Android N適配

部落格園轉載Url:http://www.cnblogs.com/gccbuaa/p/7375272.html

新特性部分

Android 7.0 Nougat 提供新功能以提升效能、生產效率和安全性,主要新增了下面的新特性和優化:

一、新的Notification

Android N 添加了很多新的notifications API,進行了又一次的設計,引入了新的風格。

  • 模板更新: 開發人員將能夠充分利用新模板,僅僅需進行少量的程式碼調整。
  • 訊息樣式自己定義: 新增自己定義樣式、訊息回覆、訊息分組等更加靈活。
  • 捆綁通知: 系統能夠將訊息組合在一起(比如,按訊息主題)並顯示組。使用者能夠適當地進行 Dismiss 或 Archive 等操作。
  • 直接回復: 對於實時通訊應用。Android 系統支援內聯回覆,以便使用者能夠直接在通知介面中高速回復簡訊。
  • 自己定義檢視: 兩個新的 API ,在通知中使用自己定義檢視時能夠充分利用系統裝飾元素,如通知標題和操作。

這裡寫圖片描寫敘述

二、多窗體支援(分屏模式)

  • 手機和平板: 使用者能夠並排執行兩個應用,或者處於分屏模式時一個應用位於還有一個應用之上。使用者能夠通過拖動兩個應用之間的分隔線來調整應用。
  • Android TV: 應用能夠將自身置於畫中畫模式。從而讓它們能夠在使用者瀏覽或與其它應用互動時繼續顯示內容。

這裡寫圖片描寫敘述

三、Quick Settings Tile API

高速設定”通經常使用於直接從通知欄顯示關鍵設定和操作。非常easy。在 Android N中。已擴充套件“高速設定”的範圍,使其更加實用更方便。為額外的“高速設定”圖塊加入了很多其它空間,使用者能夠通過向左或向右滑動跨分頁的顯示區域訪問它們。

還讓使用者能夠控制顯示哪些“高速設定”圖塊以及顯示的位置 — 使用者能夠通過拖放圖塊來加入或移動圖塊。

對於開發人員,Android N 還加入了一個新的 API。從而能夠定義自己的“高速設定”圖塊,使使用者能夠輕鬆訪問應用中的關鍵控制元件和操作。

這裡寫圖片描寫敘述

四、高速的應用安裝路徑

Android 執行元件的 JIT 編譯器最實際的優點之中的一個是應用安裝和系統更新的速度。

即使在Android 6.0 中須要幾分鐘進行優化和安裝的大型應用,如今僅僅需幾秒鐘就能夠完畢安裝。系統更新也變得更快,由於省去了優化步驟。

五、隨時隨地低電耗模式

在 Android N 中。低電耗模式又前進了一步,隨時隨地能夠省電。

僅僅要螢幕關閉了一段時間。且裝置未插入電源,低電耗模式就會相應用使用熟悉的 CPU 和網路限制。這意味著使用者即使將裝置放入口袋裡也能夠省電。

六、Project Svelte:後臺優化

Project Svelte在持續改善,以最大程度降低生態系統中一系列 Android 裝置中系統和應用使用的 RAM。在 Android N 中,Project Svelte 注重優化在後臺中執行應用的方式。

後臺處理是大多數應用的一個重要部分。

處理得當,可實現非常棒的使用者體驗—即時、高速和情境感知。假設處理不得當,後臺處理會毫無必要地消耗 RAM和電池,同一時候影響其它應用的系統性能。

Android N 刪除了三項隱式廣播(CONNECTIVITY_ACTION、ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO) 
。以幫助優化記憶體使用和電量消耗。

此項變更非常有必要,由於隱式廣播會在後臺頻繁啟動已註冊偵聽這些廣播的應用,刪除這些廣播能夠顯著提升裝置效能和使用者體驗。

移動裝置會經歷頻繁的連線變更,比如在 Wi-Fi 和移動資料之間切換時。眼下,能夠通過在應用清單檔案裡註冊一個接收器來偵聽隱式 CONNECTIVITY_ACTION廣播,讓應用能夠監控這些變更。由於非常多應用會註冊接收此廣播,因此單次網路切換即會導致全部應用被喚醒並同一時候處理此廣播。同理,應用能夠註冊接收來自其它應用(比如相機)的隱式ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO 廣播。當用戶使用相機應用拍攝照片時。這些應用即會被喚醒處理廣播。

為減緩這些問題。Android N應用了下面優化措施:

1、面向 Android N 開發的應用不會收到 CONNECTIVITY_ACTION 廣播,即使它們已有清單條目來請求接受這些事件的通知。在前臺執行的應用假設使用BroadcastReceiver請求接收通知,則仍能夠在主執行緒中偵聽CONNECTIVITY_CHANGE。

2、 應用無法傳送或接收 ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO廣播。此項優化會影響全部應用,而不僅僅是面向 Android N 的應用。

未來的 Android 版本號還可能會棄用其它隱式廣播以及未繫結的後臺服務。有鑑於此。應避免依賴在清單檔案裡宣告的接收器來偵聽隱式廣播或刪除此依賴關係,以及避免或刪除對後臺服務的依賴關係。

Android 框架提供多種解決方式來降低這些隱式廣播或後臺服務的必要性。比如,JobScheduler API 提供了一個穩健可靠的機制來安排滿足指定條件(比如連入不按流量計費的網路)時所執行的網路操作。甚至能夠使用JobScheduler來響應內容提供程式所發生的變更。

七、Data Saver

在移動裝置的整個生命週期。蜂窩資料計劃的成本一般會超出裝置本身的成本。對於很多使用者而言。蜂窩資料是他們想要節省的昂貴資源。

Android N推出了Data Saver模式,這是一項新的系統服務,有助於降低應用使用的蜂窩資料,不管是在漫遊,賬單週期即將結束。還是使用少量的預付費資料包。

Data Saver讓使用者能夠控制應用使用蜂窩資料的方式。同一時候讓開發人員開啟Data Saver時能夠提供很多其它有效的服務。

這裡寫圖片描寫敘述

八、Data Saver

Android N 將一項新的 3D 渲染 API Vulkan™ 整合到平臺中。就像 OpenGL™ ES 一樣,Vulkan是 3D 圖形和渲染的一項開放標準。由Khronos Group 維護。

Vulkan是全然從零開始設計,以最小化驅動器中的 CPU 開銷,並能讓應用更直接地控制 GPU 操作。Vulkan還同意多個執行緒同一時候執行工作。如命令緩衝區構建。以獲得更好的並行化。

Vulkan開發工具和庫都已捲入Android NDK。

它們包含:

● 頭 
● 驗證層(除錯庫) 
● SPIR-V 著色程式編譯器 
● SPIR-V 執行時著色器編譯庫 
● Vulkan僅適用於已啟用Vulkan硬體的裝置上的應用。如 Nexus 5X、Nexus 6P 和Nexus Player。

九、號碼遮蔽

Android N 如今支援在平臺中進行號碼遮蔽。提供框架 API,讓服務提供商能夠維護遮蔽的號碼列表。

預設簡訊應用、預設手機應用和提供商應用能夠對遮蔽的號碼列表進行讀取和寫入操作。其它應用則無法訪問此列表。

十、來電過濾

Android N 同意預設的手機應用過濾來電。手機應用執行此操作的方式是實現新的CallScreeningService。該方法同意手機應用基於來電的Call.Details執行大量操作,比如:

● 拒絕來電 
● 不同意來電到達通話記錄 
● 不向使用者顯示來電通知

十一、多區域設定支援、多語言

Android N 如今同意使用者在設定中選擇多個區域設定,以更好地支援雙語用例。

應用能夠使用新的 API 獲取使用者選擇的區域設定,然後為多區域設定使用者提供更成熟的使用者體驗,如以多個語言顯示搜尋結果,而且不會以使用者瞭解的語言翻譯網頁。

除多區域設定支援外,Android N 還擴充套件了使用者可用的語言範圍。它針對經常使用語言提供超過 25種的變體,如英語、西班牙語、法語和阿拉伯語。它還針對 100 多種新語言加入了部分支援。

應用能夠通過呼叫 LocaleList.GetDefault() 獲取使用者設定的區域設定列表。

為支援擴充套件的區域設定數量。Android N 正在改變其解析資源的方式。

十二、新增的表情符號

Android N引入很多其它表情符號和表情符號相關功能,包含膚色表情符號和支援變數選擇符。假設應用支援表情符號,請遵循下面準則,以便能充分利用這些表情符號相關功能優勢。

  • 在插入之前,檢查裝置是否包含表情符號。 若要檢查系統字型中有哪些表情符號。使用hasGlyph(String) 方法。
  • 檢查表情符號是否支援變數選擇符。 變數選擇符能夠呈現一些彩色或黑白的表情符號。在移動裝置上。應用應呈現彩色的表情符號,而不是黑白的。

    可是,假設應用顯示嵌入在文字中的表情符號。那應使用黑白變數。若要確定表情符號是否有變數,使用變數選擇符。如需有關支援變數的字元的完整清單,請參閱變數的 Unicode 文件中的表情符號變數序列部分。

  • * 檢查表情符號是否支援膚色。* Android N同意使用者依照他們的喜好改動表情符號呈現的膚色。鍵盤應用應為有多個膚色的表情符號提供視覺化的指示。並應同意使用者選擇他們喜歡的膚色。

    若要確定哪些系統表情符號有膚色改動器,使用hasGlyph(String) 方法。能夠通過讀取Unicode 文件來確定哪些表情符號使用膚色。

十三、Android 中的 ICU4J API

ICU4J 是一個廣泛使用的開源 Java 庫集合,為軟體應用提供 Unicode 和全球化支援。Android N 在android.icu軟體包下顯示 Android 框架中的 ICU4J API 子集,供應用開發人員使用。

遷移非常easy,主要是須要從com.java.icu名稱空間更改為android.icu。假設已在應用中使用 ICU4J 捆綁包,切換到 Android 框架中提供的android.icu API 能夠大量節省 APK 大小。

十四、OpenGL™ ES 3.2 API

Android N 加入了框架介面和對 OpenGL ES 3.2 的平臺支援。包含: 
● 來自 Android 擴充套件包 (AEP) 的全部擴充套件(EXT_texture_sRGB_decode除外)。 
● 針對 HDR 的浮點幀緩衝和延遲著色。 
● Android N同意使用者依照他們的喜好改動表情符號呈現的膚色。鍵盤應用應為有多個膚色的表情符號提供視覺化的指示,並應同意使用者選擇他們喜歡的膚色。若要確定哪些系統表情符號有膚色改動器。使用hasGlyph(String) 方法。

能夠通過讀取Unicode 文件來確定哪些表情符號使用膚色。 
● BaseVertex畫圖呼叫可實現更好的批處理和流媒體服務。 
● 強大的緩衝區訪問控制可降低WebGL開銷。

十五、VR 支援

(面向Android的 Google VR SDK)

Android N 加入了新的VR 模式的平臺支援和優化,以使開發人員能為使用者打造高質量移動 VR體驗。新版針對開發人員提供了大量效能增強特性。包含單一緩衝區渲染以及同意 VR 應用訪問某個專屬的CPU 核心。在應用中,能夠享受到專為 VR 設計的平滑頭部跟蹤和立體聲通知功能。

這裡寫圖片描寫敘述

十六、無障礙增強功能

(API參考 android.accessibilityservice.GestureDescription)

Android N 如今針對新的裝置設定直接在歡迎螢幕上提供“Vision Settings”。這使使用者能夠更easy發現和配置他們裝置上的無障礙功能,包含放大手勢、字型大小、顯示屏尺寸和TalkBack。

十七、金鑰認證

使用硬體支援的金鑰庫。可更安全地在 Android 裝置上建立、儲存和使用加密金鑰。

它們可保護金鑰免受 Linux 核心、潛在的 Android 漏洞的攻擊。也可防止從已取得根許可權的裝置提取金鑰。

為了讓硬體支援的金鑰庫使用起來更簡單和更安全。Android N 引入了金鑰認證。應用和關閉的裝置可使用金鑰認證以堅決地確定 RSA 或 EC 金鑰對是否受硬體支援、金鑰對的屬性怎樣,以及其使用和有效性有何限制。

應用和關閉的裝置服務能夠通過 X.509 認證證書(必須由有效的認證金鑰簽署)請求有關金鑰對的資訊。

認證金鑰是一個 ECDSA 簽署金鑰,其在出廠時被注入裝置的硬體支援的金鑰庫。

因此。有效的認證金鑰簽署的認證證書可確認硬體支援的金鑰庫是否存在。以及該金鑰庫中金鑰對的具體資訊。

為確保裝置使用安全的官方 Android 出廠映像,金鑰認證要求裝置 bootloader向可信執行環境(TEE)提供下面資訊:

裝置上安裝的作業系統版本號和補丁級別 
● 驗證的啟動公鑰和鎖定狀態。 
● 除金鑰認證外,Android N 還推出了指紋繫結金鑰,在指紋註冊時不會撤銷。

適配部分

在Android 7.0 的適配中,遇到了些問題,主要是新特性上的一些變化,須要針對性的做適配。

一、許可權更改

隨著Android版本號越來越高,Android對隱私的保護力度也越來越大。

從Android6.0引入的動態許可權控制(Runtime Permissions)到Android7.0的“私有資料夾被限制訪問”,“StrictMode API 政策”。這些更改在為使用者帶來更加安全的作業系統的同一時候也為開發人員帶來了一些新的任務。怎樣讓你的APP能夠適應這些改變而不是cash,是擺在每一位Android開發人員身上的責任。

二、資料夾被限制訪問

隨著Android版本號越來越高,Android對隱私的保護力度也越來越大。

從Android6.0引入的動態許可權控制(Runtime Permissions)到Android7.0的“私有資料夾被限制訪問”,“StrictMode API 政策”。這些更改在為使用者帶來更加安全的作業系統的同一時候也為開發人員帶來了一些新的任務。怎樣讓你的APP能夠適應這些改變而不是cash,是擺在每一位Android開發人員身上的責任。

● 私有檔案的檔案許可權不在放權給全部的應用,使用 MODE_WORLD_READABLE 或 MODE_WORLD_WRITEABLE 進行的操作將觸發 SecurityException。

應對策略:這項許可權的變更將意味著你無法通過File API訪問手機儲存上的資料了。基於File API的一些檔案瀏覽器等也將受到非常大的影響,看到這大家是不是驚呆了呢,只是迄今為止,這樣的限制尚不能全然執行。

應用仍可能使用原生 API 或 File API 來改動它們的私有資料夾許可權。 可是,Android官方強烈反對放寬私有資料夾的許可權。

能夠看出收起對私有檔案的訪問許可權是Android將來發展的趨勢。

● 給其它應用傳遞 file:// URI 型別的Uri,可能會導致接受者無法訪問該路徑。 因此,在Android7.0中嘗試傳遞 file:// URI 會觸發 FileUriExposedException。

應對策略:大家能夠通過使用FileProvider來解決這一問題。

● DownloadManager 不再按檔名稱分享私人儲存的檔案。

COLUMN_LOCAL_FILENAME在Android7.0中被標記為deprecated, 舊版應用在訪問 COLUMN_LOCAL_FILENAME時可能出現無法訪問的路徑。

面向 Android N 或更高版本號的應用在嘗試訪問 COLUMN_LOCAL_FILENAME 時會觸發 SecurityException。

應對策略:大家能夠通過ContentResolver.openFileDescriptor()來訪問由 DownloadManager 公開的檔案。

三、應用間共享檔案

在Android7.0系統上。Android 框架強制運行了 StrictMode API 政策禁止向你的應用外公開 file:// URI。

假設一項包含檔案 file:// URI型別 的 Intent 離開你的應用,應用失敗,並出現 FileUriExposedException 異常,如呼叫系統相機拍照,或裁切照片。

應對策略:若要在應用間共享檔案。能夠傳送 content:// URI型別的Uri,並授予 URI 暫時訪問許可權。 進行此授權的最簡單方式是使用 FileProvider類。 如需有關許可權和共享檔案的很多其它資訊,請參閱共享檔案。


● 給其它應用傳遞 file:// URI 型別的Uri。可能會導致接受者無法訪問該路徑。

因此。在Android7.0中嘗試傳遞 file:// URI 會觸發 FileUriExposedException。

例項問題

呼叫系統相機拍照,裁切照片。 
在Android7.0之前,假設你想呼叫系統相機拍照能夠通過下面程式碼來進行:

File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri imageUri = Uri.fromFile(file);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//設定Action為拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//將拍取的照片儲存到指定URI
startActivityForResult(intent,1006);

在Android7.0上使用上述方式呼叫系統相拍照會丟擲例如以下異常:

android.os.FileUriExposedException: file:////storage/emulated/0/temp/1474956193735.jpg exposed beyond app through Intent.getData()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
at android.net.Uri.checkFileUriExposed(Uri.java:2346)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8933)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
at android.app.Activity.startActivityForResult(Activity.java:4223)
...
at android.app.Activity.startActivityForResult(Activity.java:4182)

閃退截圖例如以下:

這裡寫圖片描寫敘述

這是由於Android7.0運行了“StrictMode API 政策禁”的原因,只是小夥伴們不用操心,上文講到了能夠用FileProvider來解決這一問題, 
如今我們就來一步一步的解決問題。

使用FileProvider 
使用FileProvider的大致過程例如以下:

第一步:在manifest清單檔案裡註冊provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.jph.takephoto.fileprovider"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

心得:exported:要求必須為false,為true則會報安全異常。grantUriPermissions:true,表示授予 URI 暫時訪問許可權。

第二步:指定共享的資料夾

為了指定共享的資料夾我們須要在資源(res)資料夾下建立一個xml資料夾,然後建立一個名為“file_paths”(名字能夠隨便起,僅僅要和在manifest註冊的provider所引用的resource保持一致就可以)的資原始檔。內容例如以下:

<?

xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="camera_photos" /> </paths> </resources>

代表的根資料夾: Context.getFilesDir() 
代表的根資料夾: Environment.getExternalStorageDirectory() 
代表的根資料夾: getCacheDir()

心得:上述程式碼中path=”“,是有特殊意義的,它程式碼根資料夾。也就是說你能夠向其它的應用共享根資料夾及其子資料夾下不論什麼一個檔案了,假設你將path設為path=”pictures”, 
那麼它代表著根資料夾下的pictures資料夾(eg:/storage/emulated/0/pictures),假設你向其它應用分享pictures資料夾範圍之外的檔案是不行的。

第三步:使用FileProvider 
上述準備工作做完之後,如今我們就能夠使用FileProvider了。


還是以呼叫系統相機拍照為例,我們須要將上述拍照程式碼改動為例如以下:

File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri imageUri = FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider", file);//通過FileProvider建立一個content型別的Uri
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //加入這一句表示對目標應用暫時授權該Uri所代表的檔案
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//設定Action為拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//將拍取的照片儲存到指定URI
startActivityForResult(intent,1006);

上述程式碼中主要有兩處改變:

1、將之前Uri的scheme型別為file的Uri改成了有FileProvider建立一個content型別的Uri。 
2、加入了intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);來對目標應用暫時授權該Uri所代表的檔案。

心得:上述程式碼通過FileProvider的Uri getUriForFile (Context context, String authority, File file) 
靜態方法來獲取Uri,該方法中authority引數就是清單檔案裡註冊provider的android:authorities=”com.jph.takephoto.fileprovider”。


對Webserver如tomcat。IIS比較熟悉的小夥伴,都僅僅知道為了站點內容的安全和高效,Webserver都支援為站點內容設定一個虛擬資料夾,事實上FileProvider也有異曲同工之處。

將getUriForFile方法獲取的Uri打印出來例如以下:

content://com.jph.takephoto.fileprovider/camera_photos/temp/1474960080319.jpg。

當中camera_photos就是file_paths.xml中paths的name。

由於上述指定的path為path=”“,所以content://com.jph.takephoto.fileprovider/camera_photos/代表的真實路徑就是根資料夾,即:/storage/emulated/0/。 
content://com.jph.takephoto.fileprovider/camera_photos/temp/1474960080319.jpg代表的真實路徑是:/storage/emulated/0/temp/1474960080319.jpg。

裁切照片程式碼:

File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri outputUri = Uri.fromFile(file);
Uri imageUri=Uri.fromFile(new File("/storage/emulated/0/temp/1474960080319.jpg"));
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent,1008);

和拍照一樣。上述程式碼在Android7.0上相同會引起android.os.FileUriExposedException異常,解決的方法就是上文說說的使用FileProvider。

然後。將上述程式碼改為例如以下就可以:

File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri outputUri = FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider",file);
Uri imageUri=FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider", new File("/storage/emulated/0/temp/1474960080319.jpg");//通過FileProvider建立一個content型別的Uri
Intent intent = new Intent("com.android.camera.action.CROP");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startAct