Json file was not found in Android

I am developing a game for mobile and can’t create a normal save system because when playing on the phone the error is logged: Could not find the file. I use the persistent data path as it was recommended in many answers and this method works well in Editor and Windows but not in Android. Is there something I can do about it? Additional info: I am using IL2CPP as backend. Heard that it might create problem with serialization. Full code:

  using System;
     using System.Collections.Generic;
     using UnityEngine;
     using System.IO;
   
     public class JsonFileWriter : MonoBehaviour
     {
     public string path;
     public JsonData data;
     string jsonDataString;
     void Awake(){
         path = Path.Combine(Application.persistentDataPath, "savegames", "data.json");
         FileInfo file = new FileInfo(path);
          if(!Directory.Exists(path)){
             file.Directory.Create();
          }
         CheckFileExistance(path);
         DeserializeData();
         SerializeData();
     }
     public void SerializeData()
      {
         CheckFileExistance(path);
         jsonDataString = JsonUtility.ToJson(data, true);
          File.WriteAllText(path, jsonDataString);
          Debug.Log("JSon data "+ jsonDataString);
      }
       public void DeserializeData()
      {
          string loadedJsonDataString = File.ReadAllText(path);
          data = JsonUtility.FromJson<JsonData>(loadedJsonDataString);
          Debug.Log("health: " + data.healthBoostAmt.ToString() + "secondChar: " + data.secondCharacterUnlocked.ToString());
      }
      public void WriteData(int hp,int money,int score,int damage, bool secondChar,int moneyAmt,int charSelected){
          data = new JsonData(hp,money,score,damage,secondChar,moneyAmt,charSelected);
          SerializeData();
      }
      #region Getters and seters
      public int GetHealthBoosterAmt(){
          DeserializeData();
          return data.healthBoostAmt;
      }
      public int GetMoneyBoosterAmt(){
          DeserializeData();
          return data.moneyBoostAmt;
      }
      public int GetScoreBoostAmt(){
          DeserializeData();
          return data.scoreBoostAmt;
      }
      public int GetDamageBoostAmt(){
          DeserializeData();
          return data.damageBoostAmt;
      }
      public bool GetSecondCharBought(){
          DeserializeData();
          return data.secondCharacterUnlocked;
      }
      public int GetMoneyAmt(){
          DeserializeData();
          return data.moneyAmt;
      }
      public int GetSelectedCharID(){
          DeserializeData();
          return data.charSelected;
      }
      public string GetJsonDataString(){
          return jsonDataString;
      }
      public void SetJsonDataString(string newValue){
          //int hp,int money,int score,int damage, bool secondChar
          data = JsonUtility.FromJson<JsonData>(newValue);
          WriteData(data.healthBoostAmt,data.moneyBoostAmt,data.scoreBoostAmt,data.damageBoostAmt,data.secondCharacterUnlocked,data.moneyAmt,data.charSelected);
      }
      #endregion
      void CheckFileExistance(string filePath){
          if (!File.Exists(filePath)){
             File.Create(filePath).Close();
          }
      }
}
[Serializable]
  public class JsonData
  {
      public int healthBoostAmt;
      public int moneyBoostAmt;
      public int scoreBoostAmt;
      public int damageBoostAmt;
      public bool secondCharacterUnlocked;
      public int moneyAmt;
      public int charSelected;
      public JsonData (int healthBoostAmt,int moneyBoostAmt,int scoreBoostAmt,int damageBoostAmt,bool secondCharacterUnlocked,int moneyAmt,int charSelected)
      {
          this.healthBoostAmt = healthBoostAmt;
          this.moneyBoostAmt = moneyBoostAmt;
          this.scoreBoostAmt = scoreBoostAmt;
          this.damageBoostAmt = damageBoostAmt;
          this.secondCharacterUnlocked = secondCharacterUnlocked;
          this.moneyAmt = moneyAmt;
          this.charSelected = charSelected;
      }
    
      public JsonData (){}
  }

Stack trace(I deleted the editor log file by accident yesterday but I do have a screenshot)

The most obvious thing is to wrap line 30 in a try/catch so you properly handle the file not existing.

I already did 2 checks(in awake), 1 if directory doesn’t exist and another for file but it still threw an error. The thing is that error persists only in Android and not in editor.

Computer is always right and you’re getting a FileNotFoundException… time for you to puzzle out why it’s not creating the file you think it should when it doesn’t exist in the first place.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: https://discussions.unity.com/t/700551 or this answer for Android: https://discussions.unity.com/t/699654

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

You must find a way to get the information you need in order to reason about what the problem is.

1 Like

Thank you for comprehensive answer on how to catch a problem, I know most of these but repetition is the mother of learning. You mentioned that I should wrap line 30 in try-catch statement. What would you do in the catch statement, recommend me to do?
should I create file again by writing File.Create(filePath).Close(); repopulate it and read again?

First I’d eliminate all the above code and make a fresh script and a blank scene that:

  • creates a file if it doesn’t exist
  • reads from the file

Get that working. Don’t do ANYTHING else until that works.

Since we can be fairly sure file writing hasn’t magically stopped working, we know the above will work.

If it does not, then we can start to look at why.

  • permissions?
  • drive space?
  • bad filename?
  • this code isn’t even running?
  • something else?

etc

Once you have it running trivially, bisect back to where it stops running and find where you’re going wrong.

Thanks I will do it and get back to you. So create a fresh script where I will create and write a file and scene, run it on android.

I am back and everything works on test file. Don’t know why it works here but threw error on other code
Full code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

public class TestIO : MonoBehaviour
{
   [SerializeField] TMPro.TextMeshProUGUI debugtext;
    public TestData testData;
    string jsonString;
    string path;
    string msg;
   void Awake(){
        path = Path.Combine(Application.persistentDataPath, "savegames", "testData.json");
        FileInfo file = new FileInfo(path);
        Debug.Log("Persistent path " + Application.persistentDataPath);
         if(!Directory.Exists(path)){
             Debug.Log("Directory doesn't exist and must be created");
            msg+= $"  Directory doesn't exist and must be created";
            file.Directory.Create();
            try{
                Debug.Log("dir created at "+path);
                msg+= $"  dir created at {path}";
            }
            catch{
                Debug.Log("dir was not created");
                msg +=  $" dir was not created";
            }
         }
         else{
              Debug.Log("dir  exists");
             msg+= $" dir exists";
         }
         if (!File.Exists(path)){
             Debug.Log("File doesn't exist and must be created");
            msg += $" File doesn't exist and must be created";
            File.Create(path).Close();
            try{
                Debug.Log("file created at "+path);
                msg += $" File created at {path}";
            }catch{
                Debug.Log("file was not created ");
                msg += $" File was not created ";
            }
         }
         else{
             Debug.Log("File  exists "+path);
             msg += $"File exists {path}";
         }
     
        Write();
        Read();
        debugtext.text = msg;
   }
    void Write(){
        testData = new TestData(1,2);
        jsonString = JsonUtility.ToJson(testData, true);
         File.WriteAllText(path, jsonString);
         try{
            msg += $" Write succesful at {path}";
            Debug.Log("Write succesful at "+ path);
         }
         catch{
              msg += " Write failed";
             Debug.Log("Write failed");
         }
    }
    void Read(){
        try{
            string temp =  File.ReadAllText(path);
            testData = JsonUtility.FromJson<TestData>(temp);
            msg += " Read succesful";
        }
        catch{
            Debug.Log("Can't read ");
            msg += " Can't read";
        }
    }

 




   [Serializable]
   public class TestData{
        public int one;
        public int two;
        public TestData(int one,int two){
            this.one = one;
            this.two = two;
        }
        public TestData(){}
   }
}

I am super confused

Bump

Welcome to the wide wonderful world of debugging! You have just moved onto the next step.

See my post above…

8067053--1042142--debugging.png

Your post is great advice and I tried laser focus Debug.Log(),creating a new scene with fresh script,even tried to save to SD card instead of internal device however unfortunately for now I don’t know what I am supposed to do, but I will investigate further. Can’t really understand how same code in same machine in same game can perform differently though. It must create the file and it must read it, there is no other way around it, watched tutorials and answers, everyone had similar code.
Just got frustrated

Solution found. I think. Although I don’t know why it works or is better than a previous code that I wrote. Maybe it will help someone and my suffering won’t be for nothing

Here is a code:

using System;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class JsonFileWriter : MonoBehaviour
{
    public string path;
    public JsonData data;
    string jsonDataString;
    [SerializeField] PlayGames playGames;
    [SerializeField] UnityEngine.UI.Text debugText;

    void Awake(){
        path = Path.Combine(Application.persistentDataPath, "savegames", "data.json");
        CheckFileExistance(path);
        DeserializeData();
        SerializeData();
    }
    public void SerializeData()
     {
        try{
             jsonDataString = JsonUtility.ToJson(data, true);
             File.WriteAllText(path, jsonDataString);
         }
         catch(Exception exception){
             Debug.LogError("Exception "+ exception);
             if(debugText != null){
                 debugText.text = "Exception "+ exception;
             }
             CheckFileExistance(path);
             jsonDataString = JsonUtility.ToJson(data, true);
              File.WriteAllText(path, jsonDataString);
         }
         Debug.Log("JSon data "+ jsonDataString);
     }
      public void DeserializeData()
      {
        string loadedJsonDataString;
         try{
            loadedJsonDataString = File.ReadAllText(path);
            data = JsonUtility.FromJson<JsonData>(loadedJsonDataString);
         }
         catch(Exception exception){
             Debug.LogError("Exception "+ exception);
             if(debugText != null){
                 debugText.text = "Exception "+ exception;
             }
              CheckFileExistance(path);
              loadedJsonDataString = File.ReadAllText(path);
              data = JsonUtility.FromJson<JsonData>(loadedJsonDataString);
         }
         Debug.Log("health: " + data.healthBoostAmt.ToString() + "secondChar: " + data.secondCharacterUnlocked.ToString());
     }

     public void WriteData(int hp,int money,int score,int damage, bool secondChar,int moneyAmt,int charSelected){
         data = new JsonData(hp,money,score,damage,secondChar,moneyAmt,charSelected);
         SerializeData();
     }
     #region Getters and seters
     public int GetHealthBoosterAmt(){
         DeserializeData();
         return data.healthBoostAmt;
     }
     public int GetMoneyBoosterAmt(){
         DeserializeData();
         return data.moneyBoostAmt;
     }
     public int GetScoreBoostAmt(){
         DeserializeData();
         return data.scoreBoostAmt;
     }
     public int GetDamageBoostAmt(){
         DeserializeData();
         return data.damageBoostAmt;
     }
     public bool GetSecondCharBought(){
         DeserializeData();
         return data.secondCharacterUnlocked;
     }
     public int GetMoneyAmt(){
         DeserializeData();
         return data.moneyAmt;
     }
     public int GetSelectedCharID(){
         DeserializeData();
         return data.charSelected;
     }
     public string GetJsonDataString(){
         return jsonDataString;
     }
     public void SetJsonDataString(string newValue){
         //int hp,int money,int score,int damage, bool secondChar
         data = JsonUtility.FromJson<JsonData>(newValue);
         WriteData(data.healthBoostAmt,data.moneyBoostAmt,data.scoreBoostAmt,data.damageBoostAmt,data.secondCharacterUnlocked,data.moneyAmt,data.charSelected);
     }
     #endregion
     void CheckFileExistance(string filePath){
      
         if(!Directory.Exists(filePath)){
            FileInfo file = new FileInfo(filePath);
            file.Directory.Create();
         }
         if (!File.Exists(filePath)){ 
            try{
                File.Create(filePath).Close();
                if(playGames != null){      
                    SetJsonDataString(playGames.GetSaveString());
                }
            }
            catch (UnauthorizedAccessException){
                Debug.LogError("Unable to access file(no permission)");
                if(debugText != null){
                        debugText.text = "Unable to access file(no permission";
                    }
                FileAttributes attr = (new FileInfo(filePath)).Attributes;
                if ((attr & FileAttributes.ReadOnly) > 0){
                    Debug.LogError("File read only");
                    if(debugText != null){
                        debugText.text = "File read only";
                    }
                }
            }
            catch(Exception exception){
                Debug.LogError("Exception "+exception);
                 if(debugText != null){
                        debugText.text = "Exception "+exception;
                    }
            }
         }
         else{
             return;
         }
     }
}

[Serializable]
public class JsonData
{
     public int healthBoostAmt;
     public int moneyBoostAmt;
     public int scoreBoostAmt;
     public int damageBoostAmt;
     public bool secondCharacterUnlocked;
     public int moneyAmt;
     public int charSelected;
     public JsonData (int healthBoostAmt,int moneyBoostAmt,int scoreBoostAmt,int damageBoostAmt,bool secondCharacterUnlocked,int moneyAmt,int charSelected)
     {
         this.healthBoostAmt = healthBoostAmt;
         this.moneyBoostAmt = moneyBoostAmt;
         this.scoreBoostAmt = scoreBoostAmt;
         this.damageBoostAmt = damageBoostAmt;
         this.secondCharacterUnlocked = secondCharacterUnlocked;
         this.moneyAmt = moneyAmt;
         this.charSelected = charSelected;
     }
    
     public JsonData (){}
}