這篇文章用來記錄學(xué)習(xí)和開發(fā)時(shí)遇到的版本適配問題轰坊,持續(xù)更新
-
全面屏铸董、劉海屏的適配:
Android 9 支持最新的全面屏,其中包含為攝像頭和揚(yáng)聲器預(yù)留空間的屏幕缺口肴沫。
通過 DisplayCutout 類可確定非功能區(qū)域的位置和形狀粟害,這些區(qū)域不應(yīng)顯示內(nèi)容。
要確定這些屏幕缺口區(qū)域是否存在及其位置颤芬,使用 getDisplayCutout()函數(shù)悲幅。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
只有當(dāng)DisplayCutout完全包含在系統(tǒng)欄中時(shí),才允許窗口擴(kuò)展到DisplayCutout區(qū)域站蝠。否則汰具,窗口的布局不會(huì)與顯示剪切區(qū)域重疊。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:
屏幕短邊有cutout菱魔,會(huì)延伸過去留荔;若cutout在長(zhǎng)邊,一定不會(huì)延伸過去澜倦。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
窗口決不允許與顯示剪切區(qū)域重疊聚蝶。
設(shè)置代碼:
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 通過下列變更簡(jiǎn)化通知渠道設(shè)置:
1.)屏蔽渠道組:現(xiàn)在碘勉,用戶可以針對(duì)某個(gè)應(yīng)用在通知設(shè)置中屏蔽整個(gè)渠道組。 您可以使用 isBlocked() 函數(shù)確定何時(shí)屏蔽一個(gè)渠道組桩卵,從而不會(huì)向該組中的渠道發(fā)送任何通知验靡。
此外,您的應(yīng)用可以使用全新的 getNotificationChannelGroup() 函數(shù)查詢當(dāng)前渠道組設(shè)置雏节。
2.)全新的廣播 Intent 類型:現(xiàn)在胜嗓,當(dāng)通知渠道和渠道組的屏蔽狀態(tài)發(fā)生變更時(shí),Android 系統(tǒng)將發(fā)送廣播 Intent矾屯。 擁有已屏蔽的渠道或渠道組的應(yīng)用可以偵聽這些 Intent 并做出相應(yīng)的回應(yīng)兼蕊。
有關(guān)這些 Intent 操作和 extra 的更多信息,請(qǐng)參閱 NotificationManager 參考中更新的常量列表件蚕。 有關(guān)響應(yīng)廣播 Intent 的信息孙技,請(qǐng)參閱廣播。權(quán)限收緊
1.)為了增強(qiáng)用戶隱私排作,Android 9 引入了若干行為變更牵啦,如限制后臺(tái)應(yīng)用訪問設(shè)備傳感器、限制通過 Wi-Fi 掃描檢索到的信息妄痪,以及與通話哈雏、手機(jī)狀態(tài)和 Wi-Fi 掃描相關(guān)的新權(quán)限規(guī)則和權(quán)限組。
無論采用哪一種目標(biāo) SDK 版本,這些變更都會(huì)影響運(yùn)行于 Android 9 上的所有應(yīng)用裳瘪。
2.)Android 9 限制后臺(tái)應(yīng)用訪問用戶輸入和傳感器數(shù)據(jù)的能力土浸。
如果您的應(yīng)用在運(yùn)行 Android 9 設(shè)備的后臺(tái)運(yùn)行,系統(tǒng)將對(duì)您的應(yīng)用采取以下限制:
您的應(yīng)用不能訪問麥克風(fēng)或攝像頭彭羹。
使用連續(xù)報(bào)告模式的傳感器(例如加速度計(jì)和陀螺儀)不會(huì)接收事件黄伊。
使用變化或一次性報(bào)告模式的傳感器不會(huì)接收事件。
如果您的應(yīng)用需要在運(yùn)行 Android 9 的設(shè)備上檢測(cè)傳感器事件派殷,請(qǐng)使用前臺(tái)服務(wù)还最。對(duì)使用非 SDK 接口的限制
目前處理方式掃描代碼,避免使用或者使用類似的替代方法毡惜,如果避免不了拓轻,最粗暴的是try-catch,正統(tǒng)做法是每處都做if-else的Android9適配经伙,如果是第三方庫扶叉,可以屏蔽入口,或者反編譯sdk處理帕膜,
處理起來比較復(fù)雜辜梳,官方受限制名單也還沒完全確定,等待后續(xù)解決辦法吧泳叠。
自定義icon-launcher圖標(biāo)
通知相關(guān)修改
1.)通知渠道組 NotificationChannel,渠道ID channelId的規(guī)則茶宵,大改很多危纫,包括提供了應(yīng)用桌面圖標(biāo)有通知顯示小圓點(diǎn),長(zhǎng)按圖標(biāo)還能進(jìn)行消息操作等
顯示小圓點(diǎn):
channel.setShowBadge(true);
右上角還有一個(gè)數(shù)字角標(biāo):
Notification.Builder builder=new Notification.Builder(this, channelId);
builder.setNumber(10);
通知超時(shí):
Notification.Builder builder=new Notification.Builder(this, channelId);
builder.setTimeoutAfter(timeout*1000)
背景顏色:這個(gè)屬性要生效乌庶,必須是持續(xù)任務(wù)种蝶,且是前臺(tái)服務(wù)
builder.setColorized(true);
builder.setColor(Color.BLACK);
通知清除回調(diào):
系統(tǒng)現(xiàn)在可區(qū)分通知是由用戶清除,還是由應(yīng)用自己移除瞒大。要查看清除通知的方式螃征,應(yīng)實(shí)現(xiàn)NotificationListenerService類的新onNotificationRemoved()方法
AndroidManifest里面不要忘記加配置,這么做透敌,我還是可以保證你沒有任何回調(diào)會(huì)發(fā)生盯滚,因?yàn)檫@里面有個(gè)別別竅,要手動(dòng)開啟通知訪問權(quán)限酗电。來看模擬器上的操作流程魄藕,設(shè)置>應(yīng)用和通知>高級(jí)>特殊應(yīng)用權(quán)限->通知使用權(quán)
通過判斷第三個(gè)參數(shù)reason是REASON_CANCEL還是REASON_LISTENER_CANCEL就可以知道是用戶刪除還是系統(tǒng)刪除了后臺(tái)服務(wù)限制
在后臺(tái)中運(yùn)行的服務(wù)會(huì)消耗系統(tǒng)資源,這可能降低用戶體驗(yàn)撵术。 為了緩解這一問題背率,系統(tǒng)對(duì)這些服務(wù)施加了一些限制。那么什么情況下應(yīng)用被視為處于前臺(tái)?
1.)具有可見Activity(不管該Activity已啟動(dòng)還是已暫停)
2.)具有前臺(tái)服務(wù)
3.)另一個(gè)前臺(tái)應(yīng)用已關(guān)聯(lián)到該應(yīng)用(不管是通過綁定到其中一個(gè)服務(wù)寝姿,還是通過使用其中一個(gè)內(nèi)容提供程序)交排。
例如,如果另一個(gè)應(yīng)用綁定到該應(yīng)用的服務(wù)饵筑,那么該應(yīng)用處于前臺(tái):輸入法埃篓、壁紙服務(wù)、語音等
如果以上條件均不滿足翻翩,應(yīng)用將被視為處于后臺(tái)都许。
startForegroundService方法調(diào)用后,記得5秒鐘內(nèi)調(diào)用startForeground方法嫂冻,否則系統(tǒng)將停止服務(wù)并聲明此應(yīng)用為ANR胶征。具體使用看官方文檔即可,不做贅述桨仿。
4.)后臺(tái)位置限制:
之前說的后臺(tái)服務(wù)限制目前只針對(duì)目標(biāo)SDK版本為8.0的app睛低,但是涉及到后臺(tái)位置限制就是所有版本SDK都要調(diào)整的了,它限制后臺(tái)應(yīng)用每小時(shí)只接收幾次位置更新服傍。
當(dāng)然钱雷,解決的辦法是一樣的,依然是建立前臺(tái)服務(wù)Android 8.0去除“允許未知來源”選項(xiàng)吹零,需手動(dòng)確認(rèn)罩抗。
如果我們的App具備安裝App的功能,那么AndroidManifest文件需要包含REQUEST_INSTALL_PACKAGES權(quán)限灿椅,未聲明此權(quán)限的應(yīng)用將無法安裝其他應(yīng)用套蒂。
我們可以選擇使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES這個(gè)action將用戶引導(dǎo)至安裝未知應(yīng)用權(quán)限界面。
同時(shí)也可以使用 packageManager.canRequestPackageInstalls()查詢此權(quán)限的狀態(tài)
不過最簡(jiǎn)單的辦法就是直接在AndroidManifest中配置一下就行了茫蛹,這樣會(huì)在App調(diào)用安裝界面的同時(shí)操刀,系統(tǒng)會(huì)自動(dòng)詢問用戶完成授權(quán),體驗(yàn)尚可婴洼。懸浮窗相關(guān)
這個(gè)東西篇幅較長(zhǎng)骨坑,使用頻率不高,后面可能會(huì)單獨(dú)一篇文章說這個(gè)柬采,在此便不做贅述欢唾,有興趣的可自行了解。
-
FileProvider
對(duì)于app目標(biāo)版本大于等于24的應(yīng)用粉捻,禁止在應(yīng)用外部公開 file:// URI 匈辱, 如果一項(xiàng)包含文件 URI 的 intent 離開應(yīng)用,則應(yīng)用出現(xiàn) FileUriExposedException 異常杀迹。
要應(yīng)用間共享文件亡脸,您應(yīng)發(fā)送一項(xiàng)content:// URI
押搪,并授予URI
臨時(shí)訪問權(quán)限。進(jìn)行此授權(quán)的最簡(jiǎn)單方式是使用FileProvider
類浅碾。如需了解有關(guān)權(quán)限和共享文件的詳細(xì)信息大州,請(qǐng)參閱官方文檔。
FileProvider 實(shí)際上是 ContentProvider 的一個(gè)子類垂谢,它的作用也比較明顯厦画,file://Uri
不給用,那么換個(gè) Uri 為content://
來替代滥朱。
接下來看下如何使用:
1根暑、定義 FileProvider
我們先在 AndroidManifest 中進(jìn)行注冊(cè)
<!-- 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 中指定其存儲(chǔ)區(qū)域和路徑。
我們先創(chuàng)建一個(gè)名為 res/xml/file_paths_public.xml 的新文件
<paths>
<!--圖片-->
<external-path
name="my_images"
path="Pictures/" />
<!--自定義根目錄-->
<root-path
name="root_path"
path="." />
</paths>
在 paths 節(jié)點(diǎn)內(nèi)部支持以下幾個(gè)子節(jié)點(diǎn)缰犁,分別為:
- root-path:設(shè)備的根目錄 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方式必須加這個(gè)臨時(shí)權(quán)限
Uri uri = FileProvider.getUriForFile(mContext, "com.hccf.fileprovider", file);
intent.setDataAndType(uri, "application/pdf");
mContext.startActivity(intent);
還有一種用法是: 使用Context 的 grantUriPermission() 方法和revokeUriPermission(); 這種比較少用淳地。
至此,FileProvider
的介紹就差不多結(jié)束了帅容。
-
V2簽名
APK signature scheme v2:
Android 7.0 引入一項(xiàng)新的應(yīng)用簽名方案 APK Signature Scheme v2颇象,它能提供更快的應(yīng)用安裝時(shí)間和更多針對(duì)未授權(quán) APK 文件更改的保護(hù)。在默認(rèn)情況下并徘,Android Studio 2.2 和 Android Plugin for Gradle 2.2 會(huì)使用 APK Signature Scheme v2 和傳統(tǒng)簽名方案來簽署您的應(yīng)用遣钳。
使用Android Studio 自帶的打包工具的話,只有勾選了了v2簽名的方式麦乞,app才能在目標(biāo)版本24及以上的7.0系統(tǒng)手機(jī)安裝耍贾。
還有一些其他的比較少遇到的,就不做贅述了路幸,參考文章:
https://blog.csdn.net/qq_17766199/article/details/77404712
-
運(yùn)行時(shí)權(quán)限
Android6.0推出的重大更新,收緊權(quán)限付翁,提高應(yīng)用安全简肴。具體介紹參考文章即可:
https://blog.csdn.net/qq_17766199/article/details/77404712
一般推薦使用一些第三方框架,他們已經(jīng)幫你做好了大部分機(jī)型的適配問題百侧。
本人使用的RxPermissions
使用方式:
// 獲取權(quán)限
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){
// 權(quán)限獲取成功 開始下載
}
}
@Override
public void onError(Throwable e) {
// 獲取權(quán)限失敗砰识,跳轉(zhuǎn)設(shè)置頁面
new PermissionPageUtils(getActivity()).jumpPermissionPage();
ToastUtils.showLongBottom("獲取權(quán)限失敗,請(qǐng)檢查是否開啟所需權(quán)限");
}
@Override
public void onComplete() {
}
});
- 未完待續(xù)