3
votes

Android Unity - Charger un fichier sur le fil d'arrière-plan

J'ai besoin de charger un fichier dans mon application et comme il est gros (environ 250 Mo), je dois effectuer ce chargement depuis le thread principal. De plus, comme les ressources sur Android ne sont pas stockées dans un répertoire normal, mais dans un fichier jar, je dois utiliser la classe WWW ou UnityWebRequest.

J'ai fini avec une méthode d'aide comme celle-ci:

public static byte[] ReadAllBytes(string filePath)
{

    if (Application.platform == RuntimePlatform.Android)
    {
        var reader = new WWW(filePath);
        while (!reader.isDone) { }

        return reader.bytes;
    }
    else
    {
        return File.ReadAllBytes(filePath);
    }
}

Le problème est que je ne peux pas l'utiliser sur le thread d'arrière-plan - Unity ne me permettra pas de créer un objet WWW là-bas. Comment puis-je créer une méthode comme celle-ci, qui lira ces octets sur le fil actuel?


0 commentaires

3 Réponses :


1
votes

il suffit de mettre votre boucle while dans un CoRoutine et tant que votre requête n'est pas faite à un return return. une fois terminé, appelez une méthode dans laquelle vous souhaitez utiliser vos données:

IEnumerator MyMethod()
{
    var reader = new WWW(filePath);
    while (!reader.isDone) 
    { 
        yield return; // <- use endofFrame or Wait For ore something else if u want
    }
    LoadingDoneDoData(reader.bytes);
}

void LoadingDoneDoData(bytes[] data)
{
    // your Code here
}


3 commentaires

Hé, je sais comment le faire dans une coroutine. Le fait est que j'aimerais avoir une méthode simple qui la charge maintenant. Tout comme celui ci-dessus, sauf que le mien ne fonctionnera pas sur un fil d'arrière-plan.


Ensuite, vous utilisez un thread différent de celui principal, vous devez les synchroniser. Ou que voulez-vous dire avec le fil d'arrière-plan et le charger maintenant?


J'ai abandonné et j'ai décidé de le charger en partie dans une coroutine. J'ai fait une sorte de mélange d'opérations coroutines et asynchrones. Gona le poster dans une seconde.



1
votes

Je pense que vous pouvez utiliser quelque chose comme

TheClass.ReadAllBytes(
    "a/file/path/",

    // What shall be done as soon as you have the byte[]
    (bytes) =>
    {
        // What you want to use the bytes for
    }
);

( Source ) p>

Je suppose que vous pouvez l'utiliser comme

public static async void ReadAllBytes(string filePath, Action<byte[]> successCallback)
{
    byte[] result;
    using (FileStream stream = File.Open(filePath, FileMode.Open))
    {
        result = new byte[stream.Length];
        await stream.ReadAsync(result, 0, (int)stream.Length);
    }

    // Now pass the byte[] to the callback
    successCallback.Invoke();
}

Je ne suis pas un expert en multi-threading mais ici et aussi ici vous pouvez trouver plus d'exemples et comment faire pour async - wait avec Unity3d.


Alternativement aussi le nouveau Unity Jobsystem pourrait vous intéresser.


10 commentaires

Salut, je me suis retrouvé avec quelque chose de similaire, mais j'ai peur que votre solution ne fonctionne pas. Il n'est pas possible de lire un fichier à partir d'actifs en streaming sur Android avec des opérations de fichier brut car il est stocké dans un fichier jar ...


@ MichałPowłoka oh je n'ai pas vu que vous vouliez le lire à partir de StreamingAssets. Vous devriez être capable de lire les octets de toute façon ... Je ne sais pas ce que vous voulez en faire exactement mais vous devrez décompresser le contenu, etc.


Je sais ... Les documents Unity recommandent d'utiliser dans un tel cas la classe WWW ou UnityWebRequest. Mon problème était que je ne suis pas autorisé à créer une instance de ceux-ci sur un thread qui n'est pas le thread principal, sinon l'exception est levée.


@ MichałPowłoka Mais pourquoi ne pouvez-vous pas utiliser le FileStream pour cela? il vous renvoie les octets bruts comme le ferait WWW ... mais à la différence que FileStream peut être utilisé async / threaded


Je n'ai pas essayé FileStream tbh. J'ai essayé File.ReadAllBytes. Je vais vérifier.


Tbh je ne peux pas le vérifier pour le moment, je devrais comprendre trop de choses: /


@ MichałPowłoka la différence est que File ne contient aucune méthode asynchrone pour lire les octets alors que le FileStream les a


Je sais, mais le problème avec l'utilisation de System.IO, en général, est qu'ils ne parviennent pas à trouver un fichier, quel que soit le fil utilisé. Ils ne peuvent pas ouvrir un fichier, car il est stocké dans une archive .jar.


Oui, j'ai compris mais comment l'utilisation du WWW ou de UnityWebRequest change-t-elle cela? Vous obtiendrez de toute façon le byte [] qui était le sujet de votre exemple de code.


Non. Encore une fois, le problème avec l'utilisation de File ou FileStream est qu'il ne peut pas gérer l'ouverture de fichiers placés dans une archive .jar. Cela ne fonctionnera pas, File.Exists () est faux.



0
votes

J'ai fini avec la solution suivante. Je n'ai pas trouvé de moyen de charger le thread principal du fichier brut, mais j'ai réussi à le charger dans Coroutine et à effectuer d'autres opérations (également lourdes) sur ce fichier sur un thread d'arrière-plan. J'ai créé une méthode statique comme celle-ci:

    ResourcesUtils.ReadAllBytesInCoroutine(monoBehavior, filePath, (bytes) => {

        //here I run an async method which takes bytes as parameter

    });

ReadBytesInCoroutineResult est ma classe de données simple et personnalisée:

public class ReadBytesInCoroutineResult
{
    public readonly bool successful;
    public readonly byte[] data;
    public readonly Exception reason;

    private ReadBytesInCoroutineResult(bool successful, byte[] data, Exception reason)
    {
        this.successful = successful;
        this.data = data;
        this.reason = reason;
    }

    public static ReadBytesInCoroutineResult Success(byte[] data)
    {
        return new ReadBytesInCoroutineResult(true, data, null);
    }

    public static ReadBytesInCoroutineResult Failure(Exception reason)
    {
        return new ReadBytesInCoroutineResult(true, null, reason);
    }

}

De cette façon, j'ai un mécanisme pour afin de charger un fichier dans coroutine n'importe où (tant qu'il est sur le thread principal). Un fichier est chargé de manière synchrone, mais il ne bloque pas le thread principal, à cause de la coroutine. Plus tard, j'invoque cette fonction et je prends les octets acquis sur un thread séparé, où j'effectue des calculs lourds dessus.

public static void ReadAllBytesInCoroutine(MonoBehaviour context, string filePath, Action<ReadBytesInCoroutineResult> onComplete)
{
    context.StartCoroutine(ReadFileBytesAndTakeAction(filePath, onComplete));
}

private static IEnumerator ReadFileBytesAndTakeAction(string filePath, Action<ReadBytesInCoroutineResult> followingAction)
{
    WWW reader = null;

    try
    {
        reader = new WWW(filePath);
    }
    catch(Exception exception)
    {
        followingAction.Invoke(ReadBytesInCoroutineResult.Failure(exception));
    }

    while (reader != null && !reader.isDone)
    {
        yield return null;
    }
    followingAction.Invoke(ReadBytesInCoroutineResult.Success(reader.bytes));
}


0 commentaires