這篇文章用來記錄學習和開發(fā)時遇到的版本適配問題,持續(xù)更新
-
全面屏、劉海屏的適配:
Android 9 支持最新的全面屏,其中包含為攝像頭和揚聲器預留空間的屏幕缺口。
通過 DisplayCutout 類可確定非功能區(qū)域的位置和形狀,這些區(qū)域不應顯示內(nèi)容。
要確定這些屏幕缺口區(qū)域是否存在及其位置,使用 getDisplayCutout()函數(shù)。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
只有當DisplayCutout完全包含在系統(tǒng)欄中時,才允許窗口擴展到DisplayCutout區(qū)域。否則,窗口的布局不會與顯示剪切區(qū)域重疊。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:
屏幕短邊有cutout,會延伸過去;若cutout在長邊,一定不會延伸過去。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
窗口決不允許與顯示剪切區(qū)域重疊。
設置代碼:
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
getWindow().setAttributes(lp);
通知功能的變更
Android 8.0 引入了通知渠道,允許您為要顯示的每種通知類型創(chuàng)建可由用戶自定義的渠道。 Android 9 通過下列變更簡化通知渠道設置:
1.)屏蔽渠道組:現(xiàn)在,用戶可以針對某個應用在通知設置中屏蔽整個渠道組。 您可以使用 isBlocked() 函數(shù)確定何時屏蔽一個渠道組,從而不會向該組中的渠道發(fā)送任何通知。
此外,您的應用可以使用全新的 getNotificationChannelGroup() 函數(shù)查詢當前渠道組設置。
2.)全新的廣播 Intent 類型:現(xiàn)在,當通知渠道和渠道組的屏蔽狀態(tài)發(fā)生變更時,Android 系統(tǒng)將發(fā)送廣播 Intent。 擁有已屏蔽的渠道或渠道組的應用可以偵聽這些 Intent 并做出相應的回應。
有關這些 Intent 操作和 extra 的更多信息,請參閱 NotificationManager 參考中更新的常量列表。 有關響應廣播 Intent 的信息,請參閱廣播。權限收緊
1.)為了增強用戶隱私,Android 9 引入了若干行為變更,如限制后臺應用訪問設備傳感器、限制通過 Wi-Fi 掃描檢索到的信息,以及與通話、手機狀態(tài)和 Wi-Fi 掃描相關的新權限規(guī)則和權限組。
無論采用哪一種目標 SDK 版本,這些變更都會影響運行于 Android 9 上的所有應用。
2.)Android 9 限制后臺應用訪問用戶輸入和傳感器數(shù)據(jù)的能力。
如果您的應用在運行 Android 9 設備的后臺運行,系統(tǒng)將對您的應用采取以下限制:
您的應用不能訪問麥克風或攝像頭。
使用連續(xù)報告模式的傳感器(例如加速度計和陀螺儀)不會接收事件。
使用變化或一次性報告模式的傳感器不會接收事件。
如果您的應用需要在運行 Android 9 的設備上檢測傳感器事件,請使用前臺服務。對使用非 SDK 接口的限制
目前處理方式掃描代碼,避免使用或者使用類似的替代方法,如果避免不了,最粗暴的是try-catch,正統(tǒng)做法是每處都做if-else的Android9適配,如果是第三方庫,可以屏蔽入口,或者反編譯sdk處理,
處理起來比較復雜,官方受限制名單也還沒完全確定,等待后續(xù)解決辦法吧。
自定義icon-launcher圖標
通知相關修改
1.)通知渠道組 NotificationChannel,渠道ID channelId的規(guī)則,大改很多,包括提供了應用桌面圖標有通知顯示小圓點,長按圖標還能進行消息操作等
顯示小圓點:
channel.setShowBadge(true);
右上角還有一個數(shù)字角標:
Notification.Builder builder=new Notification.Builder(this, channelId);
builder.setNumber(10);
通知超時:
Notification.Builder builder=new Notification.Builder(this, channelId);
builder.setTimeoutAfter(timeout*1000)
背景顏色:這個屬性要生效,必須是持續(xù)任務,且是前臺服務
builder.setColorized(true);
builder.setColor(Color.BLACK);
通知清除回調(diào):
系統(tǒng)現(xiàn)在可區(qū)分通知是由用戶清除,還是由應用自己移除。要查看清除通知的方式,應實現(xiàn)NotificationListenerService類的新onNotificationRemoved()方法
AndroidManifest里面不要忘記加配置,這么做,我還是可以保證你沒有任何回調(diào)會發(fā)生,因為這里面有個別別竅,要手動開啟通知訪問權限。來看模擬器上的操作流程,設置>應用和通知>高級>特殊應用權限->通知使用權
通過判斷第三個參數(shù)reason是REASON_CANCEL還是REASON_LISTENER_CANCEL就可以知道是用戶刪除還是系統(tǒng)刪除了后臺服務限制
在后臺中運行的服務會消耗系統(tǒng)資源,這可能降低用戶體驗。 為了緩解這一問題,系統(tǒng)對這些服務施加了一些限制。那么什么情況下應用被視為處于前臺?
1.)具有可見Activity(不管該Activity已啟動還是已暫停)
2.)具有前臺服務
3.)另一個前臺應用已關聯(lián)到該應用(不管是通過綁定到其中一個服務,還是通過使用其中一個內(nèi)容提供程序)。
例如,如果另一個應用綁定到該應用的服務,那么該應用處于前臺:輸入法、壁紙服務、語音等
如果以上條件均不滿足,應用將被視為處于后臺。
startForegroundService方法調(diào)用后,記得5秒鐘內(nèi)調(diào)用startForeground方法,否則系統(tǒng)將停止服務并聲明此應用為ANR。具體使用看官方文檔即可,不做贅述。
4.)后臺位置限制:
之前說的后臺服務限制目前只針對目標SDK版本為8.0的app,但是涉及到后臺位置限制就是所有版本SDK都要調(diào)整的了,它限制后臺應用每小時只接收幾次位置更新。
當然,解決的辦法是一樣的,依然是建立前臺服務Android 8.0去除“允許未知來源”選項,需手動確認。
如果我們的App具備安裝App的功能,那么AndroidManifest文件需要包含REQUEST_INSTALL_PACKAGES權限,未聲明此權限的應用將無法安裝其他應用。
我們可以選擇使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES這個action將用戶引導至安裝未知應用權限界面。
同時也可以使用 packageManager.canRequestPackageInstalls()查詢此權限的狀態(tài)
不過最簡單的辦法就是直接在AndroidManifest中配置一下就行了,這樣會在App調(diào)用安裝界面的同時,系統(tǒng)會自動詢問用戶完成授權,體驗尚可。懸浮窗相關
這個東西篇幅較長,使用頻率不高,后面可能會單獨一篇文章說這個,在此便不做贅述,有興趣的可自行了解。
-
FileProvider
對于app目標版本大于等于24的應用,禁止在應用外部公開 file:// URI , 如果一項包含文件 URI 的 intent 離開應用,則應用出現(xiàn) FileUriExposedException 異常。
要應用間共享文件,您應發(fā)送一項content:// URI,并授予URI臨時訪問權限。進行此授權的最簡單方式是使用FileProvider類。如需了解有關權限和共享文件的詳細信息,請參閱官方文檔。
FileProvider 實際上是 ContentProvider 的一個子類,它的作用也比較明顯,file://Uri不給用,那么換個 Uri 為content://來替代。
接下來看下如何使用:
1、定義 FileProvider
我們先在 AndroidManifest 中進行注冊
<!-- provider -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.hccf.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths_public" />
</provider>
2、指定可分享的文件路徑
FileProvider 只能為指定的目錄中的文件生成內(nèi)容 URI。要指定目錄,就必須使用 <paths> 元素的子元素在 XML 中指定其存儲區(qū)域和路徑。
我們先創(chuàng)建一個名為 res/xml/file_paths_public.xml 的新文件
<paths>
<!--圖片-->
<external-path
name="my_images"
path="Pictures/" />
<!--自定義根目錄-->
<root-path
name="root_path"
path="." />
</paths>
在 paths 節(jié)點內(nèi)部支持以下幾個子節(jié)點,分別為:
- root-path:設備的根目錄 new File("/")
- files-path:context.getFileDir()
- cache-path:context.getCacheDir()
- external-path:Environment.getExternalStorageDirectory()
- external-files-path:context.getExternalFilesDirs()
- external-cache-path:getExternalCacheDirs()
代碼中的常見用法:
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // FileProvider方式必須加這個臨時權限
Uri uri = FileProvider.getUriForFile(mContext, "com.hccf.fileprovider", file);
intent.setDataAndType(uri, "application/pdf");
mContext.startActivity(intent);
還有一種用法是: 使用Context 的 grantUriPermission() 方法和revokeUriPermission(); 這種比較少用。
至此,FileProvider的介紹就差不多結束了。
-
V2簽名
APK signature scheme v2:
Android 7.0 引入一項新的應用簽名方案 APK Signature Scheme v2,它能提供更快的應用安裝時間和更多針對未授權 APK 文件更改的保護。在默認情況下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 會使用 APK Signature Scheme v2 和傳統(tǒng)簽名方案來簽署您的應用。
使用Android Studio 自帶的打包工具的話,只有勾選了了v2簽名的方式,app才能在目標版本24及以上的7.0系統(tǒng)手機安裝。
還有一些其他的比較少遇到的,就不做贅述了,參考文章:
https://blog.csdn.net/qq_17766199/article/details/77404712
-
運行時權限
Android6.0推出的重大更新,收緊權限,提高應用安全。具體介紹參考文章即可:
https://blog.csdn.net/qq_17766199/article/details/77404712
一般推薦使用一些第三方框架,他們已經(jīng)幫你做好了大部分機型的適配問題。
本人使用的RxPermissions
使用方式:
// 獲取權限
RxPermissions rxPermissions = new RxPermissions(getActivity());
rxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean aBoolean) {
if (aBoolean){
// 權限獲取成功 開始下載
}
}
@Override
public void onError(Throwable e) {
// 獲取權限失敗,跳轉(zhuǎn)設置頁面
new PermissionPageUtils(getActivity()).jumpPermissionPage();
ToastUtils.showLongBottom("獲取權限失敗,請檢查是否開啟所需權限");
}
@Override
public void onComplete() {
}
});
- 未完待續(xù)