相比較去年的寫的Android 9適配,這次Android 10的內(nèi)容有點多憨闰。沒想到寫了我整整兩天状蜗,吐血中。鹉动。轧坎。
老規(guī)矩,首先將我們項目中的targetSdkVersion改為 29泽示。
1缸血、Scoped Storage(分區(qū)存儲)
說明
在Android 10之前的版本上,我們在做文件的操作時都會申請存儲空間的讀寫權(quán)限械筛。但是這些權(quán)限完全被濫用捎泻,造成的問題就是手機的存儲空間中充斥著大量不明作用的文件,并且應(yīng)用卸載后它也沒有刪除掉埋哟。
為了解決這個問題笆豁,Android 10 中引入了Scoped Storage 的概念,通過添加外部存儲訪問限制來實現(xiàn)更好的文件管理赤赊。
首先明確一個概念闯狱,外部儲存和內(nèi)部儲存。
內(nèi)部儲存:/data 目錄抛计。一般我們使用getFilesDir() 或 getCacheDir() 方法獲取本應(yīng)用的內(nèi)部儲存路徑哄孤,讀寫該路徑下的文件不需要申請儲存空間讀寫權(quán)限,且卸載應(yīng)用時會自動刪除爷辱。
外部儲存:/storage 或 /mnt 目錄。一般我們使用getExternalStorageDirectory()方法獲取的路徑來存取文件朦肘。
因為不同廠商饭弓、系統(tǒng)版本的原因,所以上述的方法并沒有一個固定的文件路徑媒抠。了解了上面的概念弟断,那我們所說的外部儲存訪問限制,可以認為是針對getExternalStorageDirectory()路徑下的文件趴生。
具體的規(guī)則如下表:
上圖將外部存儲空間分為了三部分:
特定目錄(App-specific)阀趴,使用getExternalFilesDir()或 getExternalCacheDir()方法訪問。無需權(quán)限苍匆,且卸載應(yīng)用時會自動刪除刘急。
照片、視頻浸踩、音頻這類媒體文件叔汁。使用MediaStore 訪問,訪問其他應(yīng)用的媒體文件時需要READ_EXTERNAL_STORAGE權(quán)限。
其他目錄据块,使用存儲訪問框架SAF(Storage Access Framwork)
https://developer.android.google.cn/guide/topics/providers/document-provider?hl=zh_cn
所以在Android 10上即使你擁有了儲存空間的讀寫權(quán)限码邻,也無法保證可以正常的進行文件的讀寫操作。
適配
最簡單粗暴的方法就是在AndroidManifest.xml中添加android:requestLegacyExternalStorage="true"來請求使用舊的存儲模式另假。
但是我不推薦此方法像屋。
因為在下一個版本的Android中,此條配置將會失效边篮,將強制采用外部儲存限制己莺。其實早在Android Q Beta 3之前都是強制的,但為了給開發(fā)者適配的時間才沒有強制執(zhí)行苟耻。所以如果你不抓住這段時間去適配篇恒,那么今年下半年出了Android 11。凶杖。胁艰。直接開花~~
如果你已經(jīng)適配Android 10,這里有個現(xiàn)象要注意一下:
如果應(yīng)用通過升級安裝智蝠,那么還會使用以前的儲存模式(Legacy View)腾么。只有通過首次安裝或是卸載重新安裝才能啟用新模式(Filtered View)。
所以在適配時杈湾,我們的判斷代碼如下:
// 使用Environment.isExternalStorageLegacy()來檢查APP的運行模式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
!Environment.isExternalStorageLegacy()) {
}
這樣的好處是你可以在用戶升級后解虱,能方便的將用戶的數(shù)據(jù)移動至應(yīng)用的特定目錄。
否則你只能通過SAF去移動漆撞,這樣會非常麻煩殴泰。如果你要移動數(shù)據(jù)注意只適用于Android 10下,所以現(xiàn)在適配反而是一個好時機浮驳。
當然如果你不需要遷移數(shù)據(jù)悍汛,那適配會更省事。
下面就說說推薦適配方案:
對于應(yīng)用中涉及的文件操作至会,修改一下你的文件路徑离咐。
以前我們習慣使用Environment.getExternalStorageDirectory()方法,那么現(xiàn)在可以使用getExternalFilesDir()方法(包括下載的安裝包這類的文件)奉件。如果是緩存類型文件宵蛀,可以放到getExternalCacheDir()路徑下。
或者使用MediaStore县貌,將文件存至對應(yīng)的媒體類型中(圖片:MediaStore.Images 术陶,視頻:MediaStore.Video,音頻:MediaStore.Audio)煤痕,不過僅限于多媒體文件瞳别。
下面代碼將圖片保存到公共目錄下征候,返回Uri:
public static Uri createImageUri(Context context) {
ContentValues values = new ContentValues();
// 需要指定文件信息時,非必須
values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.TITLE, "Image.png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
對于媒體資源的訪問:比如圖片選擇器這類的場景祟敛。無法直接使用File疤坝,而應(yīng)使用Uri。否則報錯如下:
java.io.FileNotFoundException: open failed: EACCES (Permission denied)
比如我在適配項目中使用的圖片選擇器時馆铁,首先修改了Glide 通過加載File的方式顯示圖片吃度。改為加載Uri的方式咽笼,否則圖片無法顯示出來。
Uri的獲取方式還是使用MediaStore:
String id = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
其次為了便于不影響之前選擇圖片返回File的邏輯(因為一般都是上傳File,沒有直接上傳Uri的操作)挎春,所以我將最終選擇的文件又轉(zhuǎn)存進了getExternalFilesDir()痴晦,主要代碼如下:
File imgFile = this.getExternalFilesDir("image");
if (!imgFile.exists()){
imgFile.mkdir();
}
try {
File file = new File(imgFile.getAbsolutePath() + File.separator +
System.currentTimeMillis() + ".jpg");
// 使用openInputStream(uri)方法獲取字節(jié)輸入流
InputStream fileInputStream = getContentResolver().openInputStream(uri);
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int byteRead;
while (-1 != (byteRead = fileInputStream.read(buffer))) {
fileOutputStream.write(buffer, 0, byteRead);
}
fileInputStream.close();
fileOutputStream.flush();
fileOutputStream.close();
// 文件可用新路徑 file.getAbsolutePath()
} catch (Exception e) {
e.printStackTrace();
}
如果你要獲取圖片中的地理位置信息矩乐,需要申請ACCESS_MEDIA_LOCATION權(quán)限璧函,并使用MediaStore.setRequireOriginal()獲取。下面是官方的示例代碼:
Uri photoUri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
cursor.getString(idColumnIndex));
final double[] latLong;
// 從ExifInterface類獲取位置信息
photoUri = MediaStore.setRequireOriginal(photoUri);
InputStream stream = getContentResolver().openInputStream(photoUri);
if (stream != null) {
ExifInterface exifInterface = new ExifInterface(stream);
double[] returnedLatLong = exifInterface.getLatLong();
// If lat/long is null, fall back to the coordinates (0, 0).
latLong = returnedLatLong != null ? returnedLatLong : new double[2];
// Don't reuse the stream associated with the instance of "ExifInterface".
stream.close();
} else {
// Failed to load the stream, so return the coordinates (0, 0).
latLong = new double[2];
}
這樣下來勋桶,一個圖片選擇器就基本適配完了脱衙。
補充
應(yīng)用在卸載后,會將App-specific目錄下的數(shù)據(jù)刪除例驹,如果在AndroidManifest.xml中聲明:android:hasFragileUserData="true"用戶可以選擇是否保留捐韩。
對于SAF的使用,可以查看我之前寫的SAF使用攻略鹃锈,這里就不展開說了荤胁。
https://weilu.blog.csdn.net/article/details/104199446
最后這里有一個介紹Scoped Storage的視頻,推薦觀看:
https://www.bilibili.com/video/av77198618
2屎债、權(quán)限變化
從6.0開始仅政,基本每次都會有權(quán)限方面變動,這次也不例外盆驹。
(前幾天發(fā)布了Android 11的預(yù)覽版圆丹,看來也有權(quán)限方面的變化。召娜。运褪。單次權(quán)限即將到來)
1惊楼、在后臺運行時訪問設(shè)備位置信息需要權(quán)限
Android 10 引入了 ACCESS_BACKGROUND_LOCATION 權(quán)限(危險權(quán)限)玖瘸。
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
該權(quán)限允許應(yīng)用程序在后臺訪問位置。如果請求此權(quán)限檀咙,則還必須請求ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION權(quán)限雅倒。只請求此權(quán)限無效果。
在Android 10的設(shè)備上弧可,如果你的應(yīng)用的 targetSdkVersion < 29蔑匣,則在請求ACCESS_FINE_LOCATION 或ACCESS_COARSE_LOCATION權(quán)限時,系統(tǒng)會自動同時請求ACCESS_BACKGROUND_LOCATION。
在請求彈框中裁良,選擇“始終允許”表示同意后臺獲取位置信息凿将,選擇“僅在應(yīng)用使用過程中允許”或"拒絕"選項表示拒絕授權(quán)。
如果你的應(yīng)用的 targetSdkVersion >= 29价脾,則請求ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION權(quán)限表示在前臺時擁有訪問設(shè)備位置信息的權(quán)牧抵。在請求彈框中,選擇“始終允許”表示前后臺都可以獲取位置信息侨把,選擇“僅在應(yīng)用使用過程中允許”只表示擁有前臺的權(quán)限犀变。
總結(jié)一下就是下圖:
其實官方不推薦你使用申請后臺訪問權(quán)的方式,因為這樣的結(jié)果無非就是多請求一個權(quán)限秋柄,那么這像變更還有什么意義获枝?申請過多的權(quán)限,也會造成用戶的反感骇笔。所以官方推薦使用前臺服務(wù)來實現(xiàn)省店,在前臺服務(wù)中獲取位置信息。
1. 首先在清單中對應(yīng)的service中添加 android:foregroundServiceType="location":
<service
android:name="MyNavigationService"
android:foregroundServiceType="location" ... >
...
</service>
2. 啟動前臺服務(wù)前檢查是否具有前臺的訪問權(quán)限:
boolean permissionApproved = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
if (permissionApproved) {
// 啟動前臺服務(wù)
} else {
// 請求前臺訪問位置權(quán)限
}
如此一來就可以在Service中獲取位置信息蜘拉。
2萨西、一些電話、藍牙和WLAN的API需要精確位置權(quán)限
下面列舉了Android 10中必須具有 ACCESS_FINE_LOCATION 權(quán)限才能使用類和方法:
電話
TelephonyManager
- getCellLocation()
- getAllCellInfo()
- requestNetworkScan()
- requestCellInfoUpdate()
- getAvailableNetworks()
- getServiceState()
- TelephonyScanManager
- requestNetworkScan()
- TelephonyScanManager.NetworkScanCallback
- onResults()
- PhoneStateListener
- onCellLocationChanged()
- onCellInfoChanged()
- onServiceStateChanged()
WLAN
- WifiManager
- startScan()
- getScanResults()
- getConnectionInfo()
- getConfiguredNetworks()
- WifiAwareManager
- WifiP2pManager
- WifiRttManager
藍牙
- BluetoothAdapter
- startDiscovery()
- startLeScan()
- BluetoothAdapter.LeScanCallback
- BluetoothLeScanner
- startScan()
我們可以根據(jù)上面提供的具體類和方法旭旭,在適配項目中檢查是否有使用到并及時處理谎脯。
3、ACCESS_MEDIA_LOCATION
Android 10新增權(quán)限持寄,上面有提到源梭,不贅述了。
4稍味、PROCESS_OUTGOING_CALLS
Android 10上該權(quán)限已廢棄废麻。
3、后臺啟動 Activity 的限制
簡單解釋就是應(yīng)用處于后臺時模庐,無法啟動Activity烛愧。
比如點開一個應(yīng)用會進入啟動頁或者廣告頁,一般會有幾秒的延時再跳轉(zhuǎn)至首頁掂碱。如果這期間你退到后臺怜姿,那么你將無法看到跳轉(zhuǎn)過程。而在之前的版本中疼燥,會強制彈出頁面至前臺沧卢。
既然是限制,那么肯定有不受限的情況醉者,主要有以下幾點:
- 應(yīng)用具有可見窗口但狭,例如前臺 Activity披诗。
- 應(yīng)用在前臺任務(wù)的返回棧中已有的 Activity。
- 應(yīng)用在 Recents 上現(xiàn)有任務(wù)的返回棧中已有的 Activity立磁。Recents 就是我們的任務(wù)管理列表呈队。
- 應(yīng)用收到系統(tǒng)的 PendingIntent 通知。
- 應(yīng)用收到它應(yīng)該在其中啟動界面的系統(tǒng)廣播唱歧。示例包括 ACTION_NEW_OUTGOING_CALL 和 SECRET_CODE_ACTION掂咒。應(yīng)用可在廣播發(fā)送幾秒鐘后啟動 Activity。
用戶已向應(yīng)用授予 SYSTEM_ALERT_WINDOW 權(quán)限迈喉,或是在應(yīng)用權(quán)限頁開啟后臺彈出頁面的開關(guān)绍刮。
因為此項行為變更適用于在 Android 10 上運行的所有應(yīng)用,所以這一限制導(dǎo)致最明顯的問題就是點擊推送信息時挨摸,有些應(yīng)用無法進行正常的跳轉(zhuǎn)(具體的實現(xiàn)問題導(dǎo)致)孩革。所以針對這類問題,可以采取PendingIntent的方式得运,發(fā)送通知時使用setContentIntent方法膝蜈。
當然你也可以申請相應(yīng)權(quán)限或者白名單:
不過申請白名單這種方法受各種手機廠商所限,很麻煩熔掺。感覺還不如引導(dǎo)用戶手動開啟權(quán)限饱搏。。置逻。
對于全屏 intent推沸,注意設(shè)置最高優(yōu)先級和添加USE_FULL_SCREEN_INTENT權(quán)限,這是一個普通權(quán)限券坞。比如微信來語音或者視頻通話時鬓催,彈出的接聽頁面就是使用這一功能。
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
Intent fullScreenIntent = new Intent(this, CallActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Incoming call")
.setContentText("(919) 555-1234")
.setPriority(NotificationCompat.PRIORITY_HIGH) // <--- 高優(yōu)先級
.setCategory(NotificationCompat.CATEGORY_CALL)
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true); // <--- 全屏 intent
Notification incomingCallNotification = notificationBuilder.build();
注意:在部分手機上恨锚,直接設(shè)置setPriority無效(或者說以渠道優(yōu)先級為準)宇驾。所以需要創(chuàng)建通知渠道時將重要性設(shè)置為IMPORTANCE_HIGH。
NotificationChannel channel = new NotificationChannel(channelId, "xxx", NotificationManager.IMPORTANCE_HIGH);
后臺啟動 Activity 的限制的目的是為了減少對用戶操作的中斷猴伶。如果你有要彈出的頁面课舍,推薦你先彈出通知,讓用戶自己選擇接下來的操作他挎,而不是一股腦的強制彈出筝尾。(如果你的全屏intent都讓用戶反感,那他也可以關(guān)掉你的通知雇盖,不至于任你擺布忿等。)
4栖忠、深色主題
Android 10 新增了一個系統(tǒng)級的深色主題(在系統(tǒng)設(shè)置中開啟)崔挖。雖然深色主題并不是強制適配項贸街,但是它可以帶給用戶更好的體驗:
可大幅減少耗電量。OLED 屏幕中每個像素都是自主發(fā)光狸相,所以在顯示深色元素時像素所消耗的電流更低薛匪,尤其在純黑顏色時像素點可以完全關(guān)閉來達到省電的效果。
為弱視以及對強光敏感的用戶提高可視性脓鹃。深色可以降低屏幕的整體視覺亮度逸尖,減少對眼睛的視覺壓力。
讓所有人都可以在光線較暗的環(huán)境中更輕松地使用設(shè)備瘸右。
適配方法有兩種:
1娇跟、手動適配(資源替換)
官方文檔中提到的繼承Theme.AppCompat.DayNight 或者 Theme.MaterialComponents.DayNight的方法,但這只是將我們使用的各種View的默認樣式進行了適配太颤,并不太適用于實際項目的適配苞俘。因為具體的項目中的View都按照設(shè)計的風格進行了重定義。
其實適配的方法很簡單龄章,類似屏幕適配吃谣、國際化的操作,并不需要繼承上面的主題做裙。比如你要修改顏色岗憋,就在res 下新建 values-night目錄,創(chuàng)建對應(yīng)的colors.xml文件锚贱。將具體要修改的色值定義在里面仔戈。圖標之類的也是一個思路,創(chuàng)建對應(yīng)的 drawable-night目錄拧廊。
只要你之前的代碼不是硬編碼且代碼規(guī)范杂穷,那么適配起來還是很輕松。
2卦绣、自動適配(Force Dark)
Android 10 提供 Force Dark 功能耐量。一如其名,此功能可讓開發(fā)者快速實現(xiàn)深色主題背景滤港,而無需明確設(shè)置 DayNight 主題背景廊蜒。
如果您的應(yīng)用采用淺色主題背景,則 Force Dark 會分析應(yīng)用的每個視圖溅漾,并在相應(yīng)視圖在屏幕上顯示之前山叮,自動應(yīng)用深色主題背景。有些開發(fā)者會混合使用 Force Dark 和本機實現(xiàn)添履,以縮短實現(xiàn)深色主題背景所需的時間屁倔。
應(yīng)用必須選擇啟用 Force Dark,方法是在其主題背景中設(shè)置 android:forceDarkAllowed="true"暮胧。
此屬性會在所有系統(tǒng)及 AndroidX 提供的淺色主題背景(例如 Theme.Material.Light)上設(shè)置锐借。使用 Force Dark 時问麸,您應(yīng)確保全面測試應(yīng)用,并根據(jù)需要排除視圖钞翔。
如果您的應(yīng)用使用Dark Theme主題(例如Theme.Material)严卖,則系統(tǒng)不會應(yīng)用 Force Dark。同樣布轿,如果應(yīng)用的主題背景繼承自 DayNight 主題(例如Theme.AppCompat.DayNight)哮笆,則系統(tǒng)不會應(yīng)用 Force Dark,因為會自動切換主題背景汰扭。
您可以通過 android:forceDarkAllowed 布局屬性或 setForceDarkAllowed(boolean) 在特定視圖上控制 Force Dark稠肘。
上述內(nèi)容我直接照搬文檔的說明。
總結(jié)一下萝毛,使用Force Dark需要注意幾點:
如果使用的是 DayNight 或 Dark Theme 主題启具,則設(shè)置forceDarkAllowed 不生效。
如果有需要排除適配的部分珊泳,可以在對應(yīng)的View上設(shè)置forceDarkAllowed為false鲁冯。
這里說說我實際使用此方法的感受:整體還是不錯的,設(shè)置的色值會自動取反色查。但也因此顏色不受控制薯演,能否達到預(yù)期效果是個需要注意的問題。追求快速適配可以采取此方案秧了。
手動切換主題
使用 AppCompatDelegate.setDefaultNightMode(@NightMode int mode)方法跨扮,其中參數(shù)mode有以下幾種:
- 淺色 - MODE_NIGHT_NO
- 深色 - MODE_NIGHT_YES
- 由省電模式設(shè)置 - MODE_NIGHT_AUTO_BATTERY
- 系統(tǒng)默認 - MODE_NIGHT_FOLLOW_SYSTEM
下面的代碼是官方Demo中的使用示例:
public class ThemeHelper {
public static final String LIGHT_MODE = "light";
public static final String DARK_MODE = "dark";
public static final String DEFAULT_MODE = "default";
public static void applyTheme(@NonNull String themePref) {
switch (themePref) {
case LIGHT_MODE: {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
break;
}
case DARK_MODE: {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
break;
}
default: {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
}
break;
}
}
}
}
通過AppCompatDelegate.getDefaultNightMode()方法,可以獲取到當前的模式验毡,這樣便于代碼中去適配衡创。
監(jiān)聽深色主題是否開啟
首先在清單文件中給對應(yīng)的Activity配置 android:configChanges="uiMode":
<activity
android:name=".MyActivity"
android:configChanges="uiMode" />
這樣在onConfigurationChanged方法中就可以獲取:
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
// 關(guān)閉
break;
case Configuration.UI_MODE_NIGHT_YES:
// 開啟
break;
default:
break;
}
}
詳細的內(nèi)容你可以參看官方文檔和官方Demo晶通。
https://developer.android.google.cn/guide/topics/ui/look-and-feel/darktheme
https://github.com/android/user-interface-samples/tree/master/DarkTheme
判斷深色主題是否開啟
其實和上面onConfigurationChanged方法同理:
public static boolean isNightMode(Context context) {
int currentNightMode = context.getResources().getConfiguration().uiMode &
Configuration.UI_MODE_NIGHT_MASK;
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
5璃氢、標識符和數(shù)據(jù)
對不可重置的設(shè)備標識符實施了限制
受影響的方法包括:
Build
- getSerial()
TelephonyManager
- getImei()
- getDeviceId()
- getMeid()
- getSimSerialNumber()
- getSubscriberId()
從 Android 10 開始,應(yīng)用必須具有 READ_PRIVILEGED_PHONE_STATE 特許權(quán)限才能正常使用以上這些方法狮辽。
如果你的應(yīng)用沒有該權(quán)限一也,卻仍然使用了以上的方法,則返回的結(jié)果會因目標 SDK 版本而異:
- 如果應(yīng)用以** Android 10 或更高版本為目標平臺**喉脖,則會發(fā)生 SecurityException椰苟。
- 如果應(yīng)用以 Android 9(API 級別 28)或更低版本為目標平臺,則相應(yīng)方法會返回 null 或占位符數(shù)據(jù)(如果應(yīng)用具有 READ_PHONE_STATE 權(quán)限)树叽。否則舆蝴,會發(fā)生 SecurityException。
這項改動表示第三方應(yīng)用無法獲取Device ID這類唯一標識。如果你需要唯一標識符洁仗,請參閱文檔:唯一標識符的最佳做法层皱。
https://developer.android.google.cn/training/articles/user-data-ids
當然你也可以試試移動安全聯(lián)盟(MSA)聯(lián)合多家廠商共同開發(fā)的統(tǒng)一補充設(shè)備標識調(diào)用SDK。據(jù)說還有點不穩(wěn)定京痢,因為我暫時還沒有嘗試過,所以不做評價篷店。
http://msalliance.icoc.bz/col.jsp?id=120
限制了對剪貼板數(shù)據(jù)的訪問權(quán)限
除非您的應(yīng)用是默認輸入法 (IME) 或是目前處于焦點的應(yīng)用祭椰,否則它無法訪問 Android 10 或更高版本平臺上的剪貼板數(shù)據(jù)。
對啟用和停用 WLAN 實施了限制
以 Android 10 或更高版本為目標平臺的應(yīng)用無法啟用或停用 WLAN疲陕。WifiManager.setWifiEnabled()方法始終返回 false方淤。
如果您需要提示用戶啟用或停用 WLAN,請使用設(shè)置面板蹄殃。
https://developer.android.google.cn/about/versions/10/features#settings-panels
6携茂、其他
Android10上對折疊屏設(shè)備有了更好的支持,對于有折疊屏適配的需求诅岩,可以參看為可折疊設(shè)備構(gòu)建應(yīng)用 和 華為折疊屏應(yīng)用開發(fā)指導(dǎo)讳苦。
https://developer.android.google.cn/guide/topics/ui/foldables
https://developer.huawei.com/consumer/cn/doc/90101
以上內(nèi)容只是Android 10中比較大的幾項變化,完整的內(nèi)容可以查看官方文檔吩谦。
https://developer.android.google.cn/about/versions/10/behavior-changes-all
最后鸳谜,點個贊關(guān)注鼓勵一下~~