Search Unity

Writing to SD card on Android

Discussion in 'Android' started by giano574, Jan 22, 2015.

  1. giano574

    giano574

    Joined:
    Nov 28, 2013
    Posts:
    76
    What is the proper way of writing to the SD card on an Android device? I simply cannot get this working.
    I have set Write Access in the Player Settings to "External (SDCard)" but Application.persistentDataPath returns "/mnt/sdcard/Android/data/com.x.x/files" which is the internal storage.

    What is the universal solution for obtaining the correct path for the SD card on Android?
     
  2. hdxpete2

    hdxpete2

    Joined:
    Dec 18, 2012
    Posts:
    5
    that is the "External storage" look at the documentation on google's site. where the user removable SD card mount location is different depending on manufacturer.
     
  3. giano574

    giano574

    Joined:
    Nov 28, 2013
    Posts:
    76
    It is the "primary external storage" which is just a partition on the internal storage. I am looking for a way to get the path of the SD card.
    Do you have a link to the Google documentation?
     
  4. mushroomrisotto

    mushroomrisotto

    Joined:
    Dec 9, 2013
    Posts:
    24
  5. giano574

    giano574

    Joined:
    Nov 28, 2013
    Posts:
    76
    "Note: don't be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer."

    Again this is the "primary external storage" which is, at least on "newer" models, a part of the internal storage.
    It really seems like Google don't want developers accessing an external SD card?
     
  6. hdxpete2

    hdxpete2

    Joined:
    Dec 18, 2012
    Posts:
    5
    where the user removable SD card mount location is different depending on manufacturer.
     
  7. giano574

    giano574

    Joined:
    Nov 28, 2013
    Posts:
    76
    Yes I understand that, but is there no way to get the path regardless of what phone is running the application?
     
  8. arnebp

    arnebp

    Joined:
    Oct 15, 2013
    Posts:
    22
    You need to call native Android methods from your c# scripts to get the paths to internal and external storage within the Android Context of your running app. These methods should do just that.

    Be sure to read the documentation for this if interested in what lies underneath.

    Code (CSharp):
    1.  
    2. private String GetAndroidContextExternalFilesDir()
    3. {
    4.   string path = "";
    5.  
    6.   if (Application.platform == RuntimePlatform.Android)
    7.   {
    8.     try
    9.     {
    10.       using (AndroidJavaClass ajc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    11.       {
    12.         using (AndroidJavaObject ajo = ajc.GetStatic<AndroidJavaObject>("currentActivity"))
    13.         {
    14.           path = ajo.Call<AndroidJavaObject>("getExternalFilesDir", null).Call<string>("getAbsolutePath");
    15.         }
    16.       }
    17.     }
    18.     catch (Exception e)
    19.     {
    20.       Debug.LogWarning("Error fetching native Android external storage dir: " + e.Message);
    21.     }
    22.   }
    23.   return path;
    24. }
    25.  
    26. private String GetAndroidContextInternalFilesDir()
    27. {
    28.   string path = "";
    29.  
    30.   if (Application.platform == RuntimePlatform.Android)
    31.   {
    32.     try
    33.     {
    34.       using (AndroidJavaClass ajc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    35.       {
    36.         using (AndroidJavaObject ajo = ajc.GetStatic<AndroidJavaObject>("currentActivity"))
    37.         {
    38.           path = ajo.Call<AndroidJavaObject>("getFilesDir").Call<string>("getAbsolutePath");
    39.         }
    40.       }
    41.     }
    42.     catch (Exception e)
    43.     {
    44.       Debug.LogWarning("Error fetching native Android internal storage dir: " + e.Message);
    45.     }
    46.   }
    47.   return path;
    48. }
    49.  
     
  9. MarkHenryC

    MarkHenryC

    Joined:
    Nov 30, 2009
    Posts:
    67
    Excellent work. Thanks.

    (A shame that - at least on Galaxy - you can't actually write to the returned external address.)
     
  10. AuriMoogle

    AuriMoogle

    Joined:
    Jun 4, 2018
    Posts:
    15
    The upper answer is just the half solution. "GetExternalFilesDir" will give you one random external storage. In most cases it's the path to the emulated external storage, which is on the internal storage. If you want the path to the sd card you have to get all paths to all available external storages with "GetExternalFilesDirs" and then distinguish the sd card with its properties. Details and explanation can be found here: http://anja-haumann.de/unity-how-to-save-on-sd-card/

    To get reliably the sd card path use this:

    Code (CSharp):
    1. private static string GetAndroidExternalFilesDir()
    2. {
    3.      using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    4.      {
    5.           using (AndroidJavaObject context = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
    6.           {
    7.                // Get all available external file directories (emulated and sdCards)
    8.                AndroidJavaObject[] externalFilesDirectories = context.Call<AndroidJavaObject[]>("getExternalFilesDirs", null);
    9.                AndroidJavaObject emulated = null;
    10.                AndroidJavaObject sdCard = null;
    11.  
    12.                for (int i = 0; i < externalFilesDirectories.Length; i++)
    13.                {
    14.                     AndroidJavaObject directory = externalFilesDirectories[i];
    15.                     using (AndroidJavaClass environment = new AndroidJavaClass("android.os.Environment"))
    16.                     {
    17.                         // Check which one is the emulated and which the sdCard.
    18.                         bool isRemovable = environment.CallStatic<bool ("isExternalStorageRemovable", directory);
    19.                         bool isEmulated = environment.CallStatic<bool> ("isExternalStorageEmulated", directory);
    20.                         if (isEmulated)
    21.                             emulated = directory;
    22.                         else if (isRemovable && isEmulated == false)
    23.                             sdCard = directory;
    24.                     }
    25.                }
    26.                // Return the sdCard if available
    27.                if (sdCard != null)
    28.                     return sdCard.Call<string>("getAbsolutePath");
    29.                else
    30.                     return emulated.Call<string>("getAbsolutePath");
    31.             }
    32.       }
    33. }
     
    Xarbrough likes this.
  11. MarcoChavez1940

    MarcoChavez1940

    Joined:
    Mar 27, 2018
    Posts:
    1

    Thanks so much!, I needed this.
     
  12. thieberson

    thieberson

    Joined:
    Mar 6, 2015
    Posts:
    5
    Hi, AuriAuri. I am trying to get the sdcard from a Galaxy GT-I8262B (Android 4.1.2)with your code, but I am having the error :

    java.lang.NoSuchMethodError: no non-static method with name='getExternalFilesDirs' signature='(Ljava/lang/Object; [Ljava/lang/Object;' in class Ljava.lang.Object.

    I am looking for a solution for days, without success.

    There is some thing I am doing wrong?

    Thanks.

     
    Last edited: Aug 20, 2018
  13. phileday

    phileday

    Joined:
    Feb 8, 2014
    Posts:
    123
    I'm also having this same problem. It worked for me before but now it doesn't.

    This is my error.

    06-25 12:07:32.021: E/Unity(4018): AndroidJavaException: java.lang.NoSuchMethodError: no non-static method with name='getExternalFilesDirs' signature='()[Ljava/lang/Object;' in class Ljava.lang.Object;

    It's surpising how little info about getting an Android external SD location is.

    Any help would be greatly appreciated.

    Phil
     
  14. phileday

    phileday

    Joined:
    Feb 8, 2014
    Posts:
    123
    I managed to get in touch with the developer who made the original code and they found an issue on 2019. They've corrected it on their website now which you can checkout here:
    http://anja-haumann.de/unity-how-to-save-on-sd-card/

    I hope this helps anyone else whos having problems with this.