1. 程式人生 > 程式設計 >AndroidQ沙盒機制之分割槽儲存適配

AndroidQ沙盒機制之分割槽儲存適配

為了讓使用者更好地控制自己的檔案,Android Q更改了應用訪問裝置外部儲存空間中檔案的方式。Android Q用更精細的媒體特定許可權來替換READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE許可權,並且無需特定許可權,應用即可訪問自己在外部儲存裝置的檔案。

1、針對應用私有檔案的隔離儲存沙盒

對於每個應用,Android Q 都會建立一個“隔離儲存沙盒”,以限制其他應用訪問本應用在外部儲存裝置的檔案。常見的外部儲存裝置是/sdcard。此定義具有兩個優點:

①、需要的許可權更少。應用沙盒中的檔案是您應用的私有檔案。因此,您不再需要任何許可權即可在外部儲存裝置中訪問和儲存自己的檔案;

②、相對於裝置上的其他應用,隱私性更強。任何其他應用都無法直接訪問您應用的隔離儲存沙盒中的檔案。藉助此訪問許可權限制,您的應用可以更輕鬆地維護沙盒檔案的隱私性;

在外部儲存裝置儲存檔案的最佳位置是Context.getExternalFilesDir()返回檔案所在的位置,因此此位置的行為方式在所有Android版本中都保持一致。使用此方法時,需要在媒體環境中傳遞我們要建立或開啟的檔案型別對應的檔案。例如,要儲存或訪問應用私有圖片,請呼叫Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)。

2、媒體檔案的共享集合

如果我們的應用建立了屬於相應使用者的檔案,並希望解除安裝該應用時保留此使用者,則將這些檔案儲存在某個通用媒體集合(共享集合)中。共享集合包括:照片、音訊、視訊和下載內容。

3、檢視其它應用的檔案所需許可權

我們的應用無需請求任何許可權,即可在這些共享集合中建立和修改自己的檔案。但是,我們的應用要建立或修改其他應用已建立的檔案,則必須先請求相應許可權:

①、訪問照片和視訊共享集合中其他應用的檔案時,需要READ_MEDIA_IMAGES或READ_MEDIA_VIDEO許可權(具體取決於您的應用需要訪問的檔案型別);

②、訪問音樂共享集合中其他應用的檔案時,需要READ_MEDIA_AUDIO許可權;

4、訪問共享集合

在請求必要的許可權後,我們的應用可以使用MediaStore API訪問這些集合:

①、對於照片和視訊共享集合,請使用MediaStore.Images或MediaStore.Video;

②、對於音樂共享集合,請使用MediaStore.Audio;

③、對於下載內容共享集合,請使用MediaStore.Downloads;

要在原生程式碼中訪問媒體檔案,請使用基於Java或kotlin程式碼的MediaStore來檢索相應檔案,然後對相應檔案描述符傳遞到原生程式碼。詳情請參考從原生程式碼訪問媒體檔案部分。

5、保留應用在共享集合的檔案

預設情況下,在使用者解除安裝應用時,Android Q會清理儲存在沙盒的檔案。要在解除安裝應用時保留這些檔案,請使用儲存訪問框架儲存訪問框架,或將檔案儲存在共享集合中。要保留共享集合的檔案,請在相關的MediaStore集合中新插一行,並使用以下方法:

①、至少應為DISPLAY_NAME和MIME_TYPE列提供值;

②、(可選)您可以使用PRIMARY_DIRECTORY和SECONDARY_DIRECTORY列來影響檔案在磁碟上的儲存位置;

③、保留DATA列不定義。這樣一來,平臺便可以靈活地將檔案保留在沙盒之外;

插入此行後,我們可以使用ContentResolver.openFileDescriptor() 這個API向檔案讀取或寫入資料。

6、訪問照片的位置資訊

一些照片在Exif元資料中包含位置資訊,以便使用者檢視照片的拍攝地點。由於此位置資訊非常敏感,因此預設情況下,Android Q會對此位置資訊進行隱藏。如果我們的應用需要訪問照片的位置資訊,需要呼叫以下方法:

①、將新的ACCESS_MEDIA_LOCATION許可權新增到您應用的清單中;

②、在 MediaStore 物件中,呼叫setRequireOriginal()並傳入照片的 URI;

Java示例程式碼如下:

Uri photoUri = Uri.withAppendedPath(
      MediaStore.Images.Media.EXTERNAL_CONTENT_URI,cursor.getString(idColumnIndex));
 
  final double[] latLong;
  if (BuildCompat.isAtLeastQ()) {
    // When running Android Q,get location data from `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 {@code ExifInterface}.
      stream.close();
    } else {
      // Failed to load the stream,so return the coordinates (0,0).
      latLong = new double[2];
    }
  } else {
    // On devices running Android 9 (API level 28) and lower,use the
    // media store columns.
    latLong = new double[]{
        cursor.getFloat(latitudeColumnIndex),cursor.getFloat(longitudeColumnIndex)
    };
  }

kotlin示例程式碼如下:

val latLong = if (BuildCompat.isAtLeastQ()) {
    // When running Android Q,get location data from `ExifInterface`.
    photoUri = MediaStore.setRequireOriginal(photoUri)
    contentResolver.openInputStream(photoUri).use { stream ->
      ExifInterface(stream).run {
        // If lat/long is null,0).
        latLong ?: doubleArrayOf(0.0,0.0)
      }
    }
  } else {
    // On devices running Android 9 (API level 28) and lower,use the
    // media store columns.
    doubleArrayOf(
      cursor.getFloat(latitudeColumnIndex).toDouble(),cursor.getFloat(longitudeColumnIndex).toDouble()
    )
  }

7、訪問其他應用建立的檔案

要訪問其他應用已儲存在外部儲存裝置的媒體檔案,需要以下步驟:

①、根據包含您要訪問的檔案的共享集合請求必要的許可權;

②、使用ContentResolver物件查詢並開啟該檔案;

8、向其他應用建立的檔案寫入資料

通過將檔案儲存在共享集合,我們的應用成為該檔案的所有者。通常情況下,只有是共享集合的某個檔案所有者時,我們的應用才可以向檔案寫入資料。不過,如果我們的應用是使用者預設的應用,我們可以向其他應用的檔案寫入資料:

①、如果您的應用是使用者的預設照片管理器應用,則可以修改其他應用儲存到照片和視訊共享集合中的圖片檔案;

②、如果您的應用是使用者的預設音樂應用,則可以修改其他應用儲存到音樂共享集合中的音訊檔案;

要修改其他應用儲存在儲存裝置的媒體檔案,需要使用ContentResolver找到相應檔案來修改。

9、標識特定的外部儲存裝置

在Android 9及以下版本,所有儲存裝置上的所有檔案都會顯示單個"external"卷名稱。而Android Q為每個外部儲存裝置提供唯一的卷名稱。此命名系統可以幫助我們高效整理內容並且加入索引,還可以控制儲存內容的位置。要唯一標識外部儲存裝置的特定檔案,我們需要使用卷名稱和ID。例如,主儲存裝置的檔案是content://media/external/images/media/12,而命名為FA23-3E92輔助儲存裝置對應檔案是content://media/FA23-3E92/images/media/12。

10、獲取外部儲存列表

要獲取所有當前可用卷的名稱列表,請呼叫MediaStore.getAllVolumeNames(),如以下程式碼段所示:

Set<String> volumeNames = MediaStore.getAllVolumeNames(context);

到此這篇關於AndroidQ沙盒機制之分割槽儲存適配的文章就介紹到這了,更多相關AndroidQ 分割槽儲存適配內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!