Dans l'une de mes applications Android, j'utilise une base de données pré-remplie que je copie lors de la première utilisation. Cela fonctionne bien sur toutes les versions d'Android jusqu'à présent, mais avec l'API 28, cela échoue. Il copie la base de données et peut se connecter à la base de données par la suite, mais pour une raison quelconque, aucune des tables n'est accessible. J'obtiens l'erreur que la table n'existe pas.
J'ai vérifié la base de données en la téléchargeant depuis l'émulateur et il n'y a pas de problème avec la base de données. Une autre application où je crée la base de données en code fonctionne très bien (la base de données est créée et les tables peuvent être trouvées après).
Le code que j'utilise pour copier la base de données:
public void copyDatabase() throws IOException{ InputStream myInput = mContext.getAssets().open(mDbSource); // Path to the just created empty db String outFileName = getDbPath() + mDbName; //Open the empty db as the output stream OutputStream myOutput = new FileOutputStream(outFileName); //transfer bytes from the inputfile to the outputfile byte[] buffer = new byte[1024]; int length; while ((length = myInput.read(buffer))>0){ myOutput.write(buffer, 0, length); } //Close the streams myOutput.flush(); myOutput.close(); myInput.close(); } private String getDbPath(){ return "/data/data/"+mContext.getResources().getString(R.string.package_name)+"/databases/"; }
3 Réponses :
Je ne peux pas être sûr tant que je n'ai pas vu le logcat. Cependant, je pense que vous pourriez avoir une autorisation manquante lors de l'accès à votre base de données.
Je voudrais demander à vérifier si vous avez le fournisseur suivant dans votre fichier AndroidManifest.xml
pour donner l'autorisation à votre application d'accéder à la base de données. Voici un exemple de fichier manifeste ayant le fournisseur .
import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; public class DatabaseContentProvider extends ContentProvider { @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null; } @Override public boolean onCreate() { return true; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } }
Et vous avez besoin d'une classe DatabaseContentProvider
comme celle-ci.
<provider android:name=".DatabaseContentProvider" android:authorities="your.application.package.name" android:exported="false" />
J'ai un exemple de code dans Github pour les opérations de base de données sous Android où vous pouvez également jeter un coup d'œil.
Le problème avec l'API 28 est que WAL (Write-Ahead Logging) est activé par défaut. Si, comme c'est souvent le cas, la base de données est ouverte, souvent lors de la vérification pour voir si la base de données existe, alors deux fichiers les fichiers -wal et -shm sont créés.
par exemple dans votre code, cela semble indiquer que c'est ce que vous faites: -
/** * Check if the database already exists. NOTE will create the databases folder is it doesn't exist * @return true if it exists, false if it doesn't */ public static boolean checkDataBase(Context context, String dbname) { File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish App if (db.exists()) return true; // If it exists then return doing nothing // Get the parent (directory in which the database file would be) File dbdir = db.getParentFile(); // If the directory does not exits then make the directory (and higher level directories) if (!dbdir.exists()) { db.getParentFile().mkdirs(); dbdir.mkdirs(); } return false; }
Lorsque la base de données existante est remplacée par la copie, les fichiers -wal et -shm restent .
Lorsque la base de données est finalement tentée d'être ouverte en tant que SQLiteDatabase, alors une discordance entre la base de données et les fichiers -wal et -shm est soulevée. La base de données SQLite sous-jacente ouverte décide alors que la base de données ne peut pas être ouverte et ainsi, afin de fournir une base de données utilisable, supprime et recrée la base de données en vidant efficacement la base de données et donc la situation se présente.
Il y a trois façons de contourner cela
Je suggérerais que 2 ou 3 sont les meilleurs moyens comme: -
Tout ce qui précède est couvert dans Ship application Android avec base de données pré-remplie .
Le code de la solution incorpore en fait à la fois 2 et 3 (bien que le correctif 3 ne devrait pas être requis (car le correctif 2 n'ouvre pas la base de données en tant que SQLiteDatabase)).
Dire que je crois que le code suivant pour vérifier si la base de données existe (correction 2) est meilleur que son équivalent dans la réponse ci-dessus: -
// Path to the just created empty db String outFileName = getDbPath() + mDbName;
Oui, avec Android 9, il y a deux fichiers supplémentaires qui font partie de la base de données SQLite, et si vous copiez une base de données superposant une base de données existante et ne supprimez pas ces deux autres fichiers en premier, la base de données se considérera comme corrompue . Consultez cette réponse: Expédier une application Android avec une base de données pré-remplie a >
Mais @MikeT devrait avoir le mérite, car la réponse liée était la sienne au départ !!
Très vrai. Au début, sa réponse n'était pas encore là, c'est donc votre commentaire qui m'a aidé à résoudre le problème. Cependant, j'ai maintenant accepté la sienne comme la bonne réponse. Néanmoins, merci pour l'aide!
Oui, avec Android 9, il y a deux fichiers supplémentaires qui font partie de la base de données SQLite, et si vous copiez une base de données superposant une base de données existante et ne supprimez pas ces deux autres fichiers en premier, la base de données se considérera comme corrompue. Voyez cette réponse. stackoverflow.com / questions / 56008907 /…
Google a vraiment besoin d'ajouter la prise en charge de l'API de sauvegarde à ses liaisons sqlite android.
@MichaelDougan c'était bien ça! Avec quelques ajustements pour mon application spécifique, cela fonctionne maintenant. Merci, si vous le postez comme réponse, je peux le marquer comme réponse acceptée