Android 10 適配攻略块差,你適配了嗎?

相比較去年的寫的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需要注意幾點:

  1. 如果使用的是 DayNight 或 Dark Theme 主題启具,則設(shè)置forceDarkAllowed 不生效。

  2. 如果有需要排除適配的部分珊泳,可以在對應(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)注鼓勵一下~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市式廷,隨后出現(xiàn)的幾起案子咐扭,更是在濱河造成了極大的恐慌,老刑警劉巖滑废,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝗肪,死亡現(xiàn)場離奇詭異,居然都是意外死亡蠕趁,警方通過查閱死者的電腦和手機薛闪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俺陋,“玉大人逛绵,你說我怎么就攤上這事【缶拢” “怎么了术浪?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寿酌。 經(jīng)常有香客問我胰苏,道長,這世上最難降的妖魔是什么醇疼? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任硕并,我火速辦了婚禮法焰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倔毙。我一直安慰自己埃仪,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布陕赃。 她就那樣靜靜地躺著卵蛉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪么库。 梳的紋絲不亂的頭發(fā)上傻丝,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音诉儒,去河邊找鬼葡缰。 笑死,一個胖子當著我的面吹牛忱反,可吹牛的內(nèi)容都是我干的泛释。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼温算,長吁一口氣:“原來是場噩夢啊……” “哼胁澳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起米者,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤韭畸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蔓搞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胰丁,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年喂分,在試婚紗的時候發(fā)現(xiàn)自己被綠了锦庸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒲祈,死狀恐怖甘萧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梆掸,我是刑警寧澤扬卷,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站酸钦,受9級特大地震影響怪得,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一徒恋、第九天 我趴在偏房一處隱蔽的房頂上張望蚕断。 院中可真熱鬧,春花似錦入挣、人聲如沸亿乳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葛假。三九已至,卻和暖如春匠璧,著一層夾襖步出監(jiān)牢的瞬間桐款,已是汗流浹背咸这。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工夷恍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人媳维。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓酿雪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親侄刽。 傳聞我的和親對象是個殘疾皇子指黎,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354