Bonjour,

Avant d'expliquer le problème, une mise en contexte s'impose. Pour apprendre les bases d'Android Studio (j'ai déjà celle de Java et MySQL), j'ai décidé de faire une application permettant d'enregistrer des animaux dans une base de donnée, stockant seulement le nom avec un identifiant.

Mon problème est que, dès que je tente d'appuyer sur le bouton ajouter, l'application crash et me donne ceci dans LogCat :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 2019-12-20 13:25:18.471 22763-22763/com.example.animal E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.animal, PID: 22763
    java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.example.animal/databases/animal.db
        at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1753)
        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1698)
        at com.example.animal.AnimalManager.addAnimal(AnimalManager.java:45)
        at com.example.animal.MainActivity$3.onClick(MainActivity.java:99)
        at android.view.View.performClick(View.java:6608)
        at android.view.View.performClickInternal(View.java:6585)
        at android.view.View.access$3100(View.java:785)
        at android.view.View$PerformClick.run(View.java:25921)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:201)
        at android.app.ActivityThread.main(ActivityThread.java:6806)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Tout cela étant géré par les classes Java suivantes :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 
package com.example.animal;
 
import android.os.Bundle;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity {
    private Button mSupprimer;
    private Button mRechercher;
    private Button mAjouter;
    private EditText mId;
    private EditText mNom;
    private int id;
    private int  nom;
    private Spinner spinnerListeServeurs;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // données du tableau
 
        //gestion éléments
        id = 0;
        mId = findViewById(R.id.editId);
        mNom = findViewById(R.id.editNom);
        mSupprimer = findViewById(R.id.supprimer);
        mRechercher = findViewById(R.id.rechercher);
        mAjouter = findViewById(R.id.ajouter);
        //Boutons grisés
        mAjouter.setEnabled(false);
        mSupprimer.setEnabled(false);
        //Ouverture de la base de données
        final AnimalManager m = new AnimalManager(this); // gestionnaire de la table "animal"
        m.open(); // ouverture de la table en lecture/écriture
 
        //spinner
 
        //griser le bouton ajouter tant que tout n'est pas rempli
        mId.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                //Peut permettre de garder le dernier caractère effacé ou l'avant dernier caractère noté
            }
 
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mAjouter.setEnabled(s.length() !=0 && nom != 0);
                mSupprimer.setEnabled(s.length() !=0 && nom != 0);
                id = s.length();
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
 
        });
        mNom.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                //Peut permettre de garder le dernier caractère effacé ou l'avant dernier caractère noté
 
            }
 
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mAjouter.setEnabled(s.length() != 0 && id != 0);
                mSupprimer.setEnabled(s.length() != 0 && id != 0);
                nom=s.length();
 
            }
 
            @Override
            public void afterTextChanged(Editable s) {
            }
 
        });
 
        mAjouter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Ajout dans bdd
                String ajoutNom = mNom.getText().toString();
                String ajoutIdS = mId.getText().toString();
                int ajoutId = Integer.valueOf(ajoutIdS);
                //problème add, crash
                m.addAnimal(new Animal(ajoutId, ajoutNom));
 
            }
        });
        mSupprimer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                //Supprimer
            }
        });
        mRechercher.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                //rechercher
            }
        });
        m.close();
    }
    public void onClick(View v) {
        String idR = mId.getText().toString();
        String nomR = mNom.getText().toString();
 
    }
 
 
 
}
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 
//Class AnimalManager
package com.example.animal;
 
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
 
import com.example.animal.Animal;
import com.example.animal.MySQLite;
 
public class AnimalManager {
 
    private static final String TABLE_NAME = "animal";
    public static final String KEY_ID_ANIMAL="id_animal";
    public static final String KEY_NOM_ANIMAL="nom_animal";
    public static final String CREATE_TABLE_ANIMAL = "CREATE TABLE "+TABLE_NAME+
            " (" +
            " "+KEY_ID_ANIMAL+" INTEGER PRIMARY KEY AUTOINCREMENT," +
            " "+KEY_NOM_ANIMAL+" TEXT" +
            ");";
    private MySQLite maBaseSQLite; // notre gestionnaire du fichier SQLite
    private SQLiteDatabase db;
 
    // Constructeur
    public AnimalManager(Context context)
    {
        maBaseSQLite = MySQLite.getInstance(context);
    }
 
    public void open()
    {
        //on ouvre la table en lecture/écriture
        db = maBaseSQLite.getWritableDatabase();
    }
 
    public void close()
    {
        //on ferme l'accès à la BDD
        db.close();
    }
 
    public void addAnimal(Animal animal) {
        // Ajout d'un enregistrement dans la table
 
        db.execSQL("INSERT INTO " + TABLE_NAME + "VALUES ("+ animal.getNom_animal() + ");");
    }
 
    public int modAnimal(Animal animal) {
        // modification d'un enregistrement
        // valeur de retour : (int) nombre de lignes affectées par la requête
 
        ContentValues values = new ContentValues();
        values.put(KEY_NOM_ANIMAL, animal.getNom_animal());
 
        String where = KEY_ID_ANIMAL+" = ?";
        //Remplace le ?
        String[] whereArgs = {animal.getId_animal()+""};
 
        return db.update(TABLE_NAME, values, where, whereArgs);
    }
 
    public int supAnimal(Animal animal) {
        // suppression d'un enregistrement
        // valeur de retour : (int) nombre de lignes affectées par la clause WHERE, 0 sinon
 
        String where = KEY_ID_ANIMAL+" = ?";
        String[] whereArgs = {animal.getId_animal()+""};
 
        return db.delete(TABLE_NAME, where, whereArgs);
    }
 
    public Animal getAnimal(int id) {
        // Retourne l'animal dont l'id est passé en paramètre
 
        Animal a=new Animal(0,"");
 
        Cursor c = db.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+KEY_ID_ANIMAL+"="+id, null);
        if (c.moveToFirst()) {
            a.setId_animal(c.getInt(c.getColumnIndex(KEY_ID_ANIMAL)));
            a.setNom_animal(c.getString(c.getColumnIndex(KEY_NOM_ANIMAL)));
            c.close();
        }
 
        return a;
    }
 
    public Cursor getAnimaux() {
        // sélection de tous les enregistrements de la table
        return db.rawQuery("SELECT * FROM "+TABLE_NAME, null);
    }
 
} // class AnimalManager
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
package com.example.animal;
 
public class Animal {
 
    private int id_animal;
    private String nom_animal;
 
    // Constructeur
    public Animal(int id,String nom) {
        this.id_animal=id;
        this.nom_animal=nom;
    }
 
    public int getId_animal() {
        return id_animal;
    }
 
    public void setId_animal(int id) {
        this.id_animal = id;
    }
 
    public String getNom_animal() {
        return nom_animal;
    }
 
    public void setNom_animal(String nom) {
        this.nom_animal = nom;
    }
 
} // class Animal
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
package com.example.animal;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
 
public class MySQLite extends SQLiteOpenHelper {
 
    private static final String DATABASE_NAME = "animal.db";
    private static final int DATABASE_VERSION = 1;
    private static MySQLite sInstance;
 
    public static synchronized MySQLite getInstance(Context context) {
        if (sInstance == null) { sInstance = new MySQLite(context); }
        return sInstance;
    }
 
    private MySQLite(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
 
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        // Création de la base de données
        // on exécute ici les requêtes de création des tables
        sqLiteDatabase.execSQL(AnimalManager.CREATE_TABLE_ANIMAL); // création table "animal"
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {
        // Mise à jour de la base de données
        // méthode appelée sur incrémentation de DATABASE_VERSION
        // on peut faire ce qu'on veut ici, comme recréer la base :
        sqLiteDatabase.execSQL("DROP TABLE " + AnimalManager.CREATE_TABLE_ANIMAL + ";");
        onCreate(sqLiteDatabase);
    }
 
} // class MySQLite
et le code XML au cas où :

Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center">
    <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnCount="1"
        android:rowCount="2"
        android:layout_centerHorizontal="true"
        android:id="@+id/tableau"
        >
        <TableRow>
            <EditText
                android:id="@+id/editId"
                android:layout_width="350dp"
                android:layout_height="match_parent"
                android:hint="ID"
                android:inputType="number" />
        </TableRow>
        <TableRow>
            <EditText
                android:id="@+id/editNom"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:hint="nom"
                android:inputType="textPersonName"
 
                />
        </TableRow >
    </TableLayout>
    <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="350dp"
        android:layout_height="wrap_content"
        android:columnCount="3"
        android:rowCount="1"
        android:id="@+id/tableauB"
        android:padding="10dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tableau"
        >
        <!--mettre les entrées au dessus des boutons -->
 
        <TableRow>
            <Button
                android:id="@+id/supprimer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Supprimer"
                android:layout_toRightOf="@id/ajouter"
                />
 
            <Button
                android:id="@+id/ajouter"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ajouter"
                android:layout_toRightOf="@id/rechercher"
                />
            <Button
                android:id="@+id/rechercher"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="rechercher" />
        </TableRow>
    </TableLayout>
 
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:layout_below="@id/idListe"
        />
 
    <Spinner
        android:id="@+id/idListe"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tableauB"/>
 
 
</RelativeLayout>

ps : je sais pas du tout comment on fait pour utiliser correctement les balises code donc si vous pouviez me le dire en passant, ce serait pas de refus