Xamarin : Android 取得媒體儲存裝置 retrieve emulated or removable storage path
這是一個很觀念的問題,其實android對於儲存位置的概念跟Windows是不一樣的。
我們所知的 Windows 的儲存媒體,通常都會是以『磁碟機』(Device)概念呈現的,每個 Device 都會被賦予一個『磁碟機代號』(Device Letter),像是 『C:\』『 D:\』 等等。
但是除了 Windows 以外的世界,基本上會是用『路徑』來呈現,由其 Android 的 Dalvik 系統本身就是 java 的支線,所以儲存空間會像是 『/Storage/dev0』『/Storage/dev1』之類的呈現法。
瞭解了以上的概念之後,再來瞭解 Android 對於儲存空間的認定。
一般我們在看儲存位置的時候,直覺上會把手機的儲存空間分為『內建』『內部』『外部』『擴充』等等的用法,因為 Android 有分為內建記憶空間和一個可以擴充的 SD 卡(或是TF卡)。
而我們程式在開發時相關文件都會去 Google 找類似 Internal 或是 External 類的單字搜尋,但實際上找到的文件範例基本上都會牛頭不對馬嘴,最大的原因應該會出在 Android 開發團隊對於這種一般人認定的常規式用法式有很大出入的。
Android 對於內建記憶空間和擴充的SD卡認定如下:
Emulated - 內部記憶空間,用的單字是『仿真』,而不是 Internal (內部),是因為 Android 真正內部記憶體指的是ROM,也就是作業系統OS存在的地方,而ROM本身不能經常寫入,因為有寫入壽命限制,但是作為一個作業系統難免會有許多設定或變更或紀錄,那這樣要存在哪裡呢?事實上, Android 手機真正可以讀寫的內建空間其實也是一個 SD 卡,指是手機製造商會在手機裡面直接燒上SD的儲存晶片作為內建儲存空間,凡是OS以外的程式APP或是紀錄都會存在此處,所以把這種內建 SD 記憶體稱為『仿真記憶體』。
Removable - 擴充的記憶體(SD卡或TF卡),這被稱為『可移除式』媒體,名字簡而易瞭,代表這記憶體可以被移除或是安插。
External - 而這個『外部』的意思就是泛指除了 OS 的 ROM 之外的都會稱為 External,所以上面兩個『Emulated』、『Removable』都算在 External 下面。而 External 下只有一個 storages,從這個 storages 下才會開始區分各類儲存空間。
所以記憶體歸類會像下面結構:
先看看程式樣本,再來解說:
首先,我們可以用 GetExternalFilesDirs 來取得所有可用路徑,這個功能可以讓APP取得所有可用的儲存路徑,這也包括了內部和外部的儲存空間
然後,再利用
來判斷屬於哪一種類型的儲存位置
當然這儲存位置會包含APP的儲存路徑,
例如:/storage/emulated/0/Android/data/path.YourAppName/files
最後把這路徑去除即可得到實際儲存空間的起始位置
我們所知的 Windows 的儲存媒體,通常都會是以『磁碟機』(Device)概念呈現的,每個 Device 都會被賦予一個『磁碟機代號』(Device Letter),像是 『C:\』『 D:\』 等等。
但是除了 Windows 以外的世界,基本上會是用『路徑』來呈現,由其 Android 的 Dalvik 系統本身就是 java 的支線,所以儲存空間會像是 『/Storage/dev0』『/Storage/dev1』之類的呈現法。
瞭解了以上的概念之後,再來瞭解 Android 對於儲存空間的認定。
一般我們在看儲存位置的時候,直覺上會把手機的儲存空間分為『內建』『內部』『外部』『擴充』等等的用法,因為 Android 有分為內建記憶空間和一個可以擴充的 SD 卡(或是TF卡)。
而我們程式在開發時相關文件都會去 Google 找類似 Internal 或是 External 類的單字搜尋,但實際上找到的文件範例基本上都會牛頭不對馬嘴,最大的原因應該會出在 Android 開發團隊對於這種一般人認定的常規式用法式有很大出入的。
Android 對於內建記憶空間和擴充的SD卡認定如下:
Emulated - 內部記憶空間,用的單字是『仿真』,而不是 Internal (內部),是因為 Android 真正內部記憶體指的是ROM,也就是作業系統OS存在的地方,而ROM本身不能經常寫入,因為有寫入壽命限制,但是作為一個作業系統難免會有許多設定或變更或紀錄,那這樣要存在哪裡呢?事實上, Android 手機真正可以讀寫的內建空間其實也是一個 SD 卡,指是手機製造商會在手機裡面直接燒上SD的儲存晶片作為內建儲存空間,凡是OS以外的程式APP或是紀錄都會存在此處,所以把這種內建 SD 記憶體稱為『仿真記憶體』。
Removable - 擴充的記憶體(SD卡或TF卡),這被稱為『可移除式』媒體,名字簡而易瞭,代表這記憶體可以被移除或是安插。
External - 而這個『外部』的意思就是泛指除了 OS 的 ROM 之外的都會稱為 External,所以上面兩個『Emulated』、『Removable』都算在 External 下面。而 External 下只有一個 storages,從這個 storages 下才會開始區分各類儲存空間。
所以記憶體歸類會像下面結構:
先看看程式樣本,再來解說:
using Android.App;
using Android.Widget;
using Android.OS;
using System;
namespace StoragePath
{
[Activity(Label = "StoragePath", MainLauncher = true)]
public class MainActivity : Activity
{
TextView tv1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
//取得顯示視窗
tv1 = FindViewById<TextView>(Resource.Id.textView1);
//讓TextView可以Scroll(要一併設定 TextView 的 ScrollBar 和 MaxLines 屬性)
tv1.MovementMethod = new Android.Text.Method.ScrollingMovementMethod();
tv1.Text = "AbsolutePath:\r\n";
//顯示實際取得的路徑
//這種取得路徑樣式皆為 [StorageMediaPath]/Android/data/path.YourAppName/files
Java.IO.File[] dirs = Application.Context.GetExternalFilesDirs(null);
foreach(Java.IO.File path in dirs)
{
tv1.Text += path.AbsolutePath + "\r\n";
}
tv1.Text += "\r\n== AbsolutePath ==\r\n";
tv1.Text += "內建媒體:\r\n";
string[] Storages = GetStorageDirectory(false);
foreach(string path in Storages)
{
tv1.Text += path + "\r\n";
}
tv1.Text += "\r\n可移除媒體:\r\n";
Storages = GetStorageDirectory(true);
foreach (string path in Storages)
{
tv1.Text += path + "\r\n";
}
}
public string[] GetStorageDirectory(bool getRemovable)
{
int Index = 0;
string[] Result = { };
//取得應用程式可以使用的所有路徑
//利用這功能可以取得所有可存取的媒體位置
Java.IO.File[] dirs = Application.Context.GetExternalFilesDirs(null);
foreach (Java.IO.File path in dirs)
{
//判斷是內建還是可移除媒體
bool IsEmulated = Android.OS.Environment.InvokeIsExternalStorageEmulated(path);
bool IsRemovable = Android.OS.Environment.InvokeIsExternalStorageRemovable(path);
//如果指定查出可移除媒體儲存位置
if (getRemovable)
{
if (IsRemovable && !IsEmulated)
{
Array.Resize<string>(ref Result, Index + 1);
//去除路徑中不必要的元素
Result[Index] = path.AbsolutePath.Substring(0, path.AbsolutePath.IndexOf(@"/Android/data"));
Index++;
}
}
else
{
//取得內建媒體儲存位置
if (IsEmulated)
{
Array.Resize<string>(ref Result, Index + 1);
//去除路徑中不必要的元素
Result[Index] = path.AbsolutePath.Substring(0, path.AbsolutePath.IndexOf(@"/Android/data"));
Index++;
}
}
}
return Result;
}
}
}
首先,我們可以用 GetExternalFilesDirs 來取得所有可用路徑,這個功能可以讓APP取得所有可用的儲存路徑,這也包括了內部和外部的儲存空間
Java.IO.File[] dirs = Application.Context.GetExternalFilesDirs(null);
然後,再利用
InvokeIsExternalStorageEmulated
和
InvokeIsExternalStorageRemovable
來判斷屬於哪一種類型的儲存位置
當然這儲存位置會包含APP的儲存路徑,
例如:/storage/emulated/0/Android/data/path.YourAppName/files
最後把這路徑去除即可得到實際儲存空間的起始位置
path.AbsolutePath.Substring(0, path.AbsolutePath.IndexOf(@"/Android/data"))
留言