72 votes

Opérations sur les fichiers dans le NDK Android

J'utilise le NDK Android pour créer une application principalement en C pour des raisons de performance, mais il semble que les opérations de fichiers telles que fopen ne fonctionnent pas correctement dans Android. Chaque fois que j'essaie d'utiliser ces fonctions, l'application se bloque.

Comment créer/écrire dans un fichier avec le NDK Android ?

1 votes

Désolé, j'ai oublié de mettre à jour cette page. La racine du problème était l'impossibilité d'obtenir les permissions de lecture/écriture. Un problème similaire peut également être causé par le fait que la carte SD soit montée et que l'on essaie ensuite d'ouvrir un fichier sur cette carte.

67voto

Ita Points 652

Les autres réponses sont correctes. Vous pouvez ouvrir un fichier via le NDK en utilisant FILE y fopen mais n'oubliez pas d'en demander l'autorisation.

Dans le manifeste Android :

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

1 votes

0 votes

Merci, ça marche ! J'ai aussi découvert que les mises à jour du fichier manifeste ne prennent effet qu'après la réinstallation de l'application. Parfois, l'éclipse ne réinstalle pas l'application pour vous. Je supprime l'application sur mon téléphone et je la réinstalle avec eclipse.

65voto

Tim Kryger Points 7584

Les entrées-sorties de fichiers fonctionnent bien sur Android avec JNI. Peut-être essayez-vous d'ouvrir un fichier avec un mauvais chemin et ne vérifiez-vous pas le code de retour ? J'ai modifié l'exemple hello-jni pour démontrer qu'il est en effet possible d'ouvrir un fichier et d'y écrire. J'espère que cela vous aidera.

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>
#include <stdio.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                              jobject thiz )
{
    FILE* file = fopen("/sdcard/hello.txt","w+");

    if (file != NULL)
    {
        fputs("HELLO WORLD!\n", file);
        fflush(file);
        fclose(file);
    }

    return (*env)->NewStringUTF(env, "Hello from JNI (with file io)!");
}

Voici le résultat après l'avoir exécuté sur mon téléphone (avec une carte SD) :

$ adb -d shell cat /sdcard/hello.txt
HELLO WORLD!

0 votes

Merci, ça marche ! Je pense que mon problème était que je n'utilisais pas un chemin complet pour ma commande fopen.

2 votes

Serait-il possible d'ouvrir un fichier résidant dans les actifs en mode lecture seule ?

1 votes

L'ouverture d'actifs à l'aide du NDK est plus compliquée, mais je décris les étapes nécessaires. ici .

24voto

niko20 Points 91

Veillez à utiliser l'option Java getExternalStorageDirectory() pour obtenir le chemin réel de la carte SD, car les appareils les plus récents ne la font pas simplement correspondre à "/sdcard". Dans ce cas, l'utilisation d'un emplacement codé en dur "/sdcard" échouera.

3 votes

C'est en effet très important

1 votes

Courir adb shell "ls / -l | grep sdcard" pour savoir à quel endroit votre appareil fait correspondre le répertoire /sdcard (à des fins de débogage)

0 votes

Au lieu d'utiliser getExternalStorageDirectory J'ai utilisé getenv("EXTERNAL_STORAGE") mais cela n'a pas fonctionné. Bien que dans le shell adb $EXTERNAL_STORAGE es /sdcard/ . J'utilise Android 7.1. Dois-je obtenir le chemin en utilisant getExternalStorageDirectory ?

22voto

SomeCallMeTim Points 1803

Je peux également vérifier que fopen() fonctionne correctement, mais pas si vous essayez d'accéder à un fichier dans le dossier des ressources ou des actifs de l'application. Pour ne pas avoir à réinventer la roue, je vous recommande de placer tous les fichiers que vous souhaitez livrer avec votre application dans le dossier assets, où ils seront empaquetés pour être distribués.

Dans le cas du dossier assets, vous devez faire l'une des deux choses suivantes, selon que le fichier a été compressé ou non par l'empaqueteur. Les deux utilisent les méthodes AssetManager, et vous pouvez obtenir l'AssetManager à partir du contexte/app. Les noms de fichiers sont toujours relatifs au dossier assets, btw : si vous avez un fichier "foo.png" directement dans le dossier assets, vous ouvrirez "foo.png," pas quelque chose comme "assets/foo.png".

  1. Si le fichier n'a pas été compressé (c'est-à-dire s'il s'agit d'une des extensions qui ne sont pas compressées, comme .png), vous pouvez obtenir un descripteur de fichier à partir de AssetManager.openFd() et le transmettre à C++. Vous pouvez ensuite utiliser fdopen(dup(fd), "r") ; pour ouvrir le fichier en tant que FILE*. Notez que vous doit fseek() à l'offset, et garder une trace de la longueur du fichier. En réalité, vous obtenez un gestionnaire de fichier pour l'ensemble des actifs, et le fichier qui vous intéresse n'en est qu'une petite partie.

  2. Si votre fichier est compressé, vous devez utiliser le lecteur de flux Java : AssetManager.open() vous donne un InputStream que vous pouvez utiliser pour lire le fichier. Je lance une étape de prétraitement sur mon dossier d'actifs qui génère une liste de tous les fichiers avec leurs tailles respectives afin que je puisse savoir, par exemple, quelle taille de tampon allouer.

Si votre fichier est une ressource, il se peut que vous deviez passer par la classe Resource pour y accéder, bien qu'il semble que les ressources soient également regroupées dans le même paquet d'actifs. La classe Resource possède un appel openRawResource() pour obtenir l'InputStream et un appel openRawResourceFd() pour obtenir le descripteur de fichier, comme ci-dessus.

Bonne chance.

1 votes

Note de suivi : Le "get a file descriptor from AssetManager.openFd()" s'avère être une mauvaise forme -- il ne fait pas partie du SDK béni. Il se trouve que cela fonctionne sur tous les téléphones actuels, mais il n'est pas garanti que cela continue à fonctionner. Obtenir le chemin vers le fichier .APK ne s'avère pas difficile, et est laissé comme un exercice pour le lecteur qui se soucie d'avoir un code NDK complètement kasher.

2 votes

... laissée à l'appréciation du lecteur ? Comment suis-je censé faire mon travail si je ne peux pas copier-coller le code de stack overflow ?

0 votes

...en copiant le code d'une autre réponse ? Ou en effectuant vous-même les recherches. Cette réponse date de sept ans. Il est probablement bon de construire quelque chose à partir de votre propre code testé, puisque j'étais probablement en train de développer pour Android 2.x quand j'ai écrit cette réponse.

0voto

Rafael Sabino Points 98

J'aimerais ajouter mon grain de sel aux réponses données ici. En plus de définir les autorisations correctes spécifiées dans la réponse à cette question, assurez-vous de donner à votre application la permission d'accéder au stockage dans le système d'exploitation. Le menu des autorisations peut changer d'un téléphone à l'autre, mais un moyen facile d'y accéder est d'aller dans le menu Paramètres et de chercher "Autorisations". Cela vous permettra de donner à votre application la permission d'accéder au stockage (c'est-à-dire au répertoire sdcard) à partir du code NDK.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X