Autore Topic: Salvare e reperire immagini da database  (Letto 558 volte)

Offline Ilgard

  • Utente junior
  • **
  • Post: 64
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Xperia M
  • Sistema operativo:
    Ubuntu 14.04
Salvare e reperire immagini da database
« il: 05 Dicembre 2014, 18:25:44 CET »
0
Ciao a tutti, sono un po' di giorni che litigo con SQLlite per capire come usarlo.
Quello che voglio fare è creare un certo numero di tabelle (per ora mi limito alla tabella Immagini), ognuna delle quali contiene delle associazioni (indice, Bitmap).
Per la creazione della tabella faccio così:
Codice (Java): [Seleziona]
static class Constants {  // i metadati della tabella, accessibili ovunque
                static final String NAME_TABLE = "immagini";
                static final String ID = "_id";
                static final String ELEMENT_NAME_KEY = "indice";
                static final String ELEMENT_IMAGE_KEY = "bitmap";
}

private static final String PRODUCTS_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS "  //codice sql di creazione della tabella
                        + Constants.NAME_TABLE + " ("
                        + Constants.ID+ " integer primary key autoincrement, "
                        + Constants.ELEMENT_NAME_KEY + " integer not null, "
                        + Constants.ELEMENT_IMAGE_KEY + " blob not null);";


        @Override
        public void onCreate(SQLiteDatabase db) {
                //Eseguiamo la query
                db.execSQL(PRODUCTS_TABLE_CREATE);
        }

Inserisco i dati tramite bottone e le immagini le prendo da un array di resources. L'indice lo prendo da una EditText (non deve essere maggiore di 10).
Codice (Java): [Seleziona]
if (i >= 10 ) return;
ContentValues contentValues = new ContentValues();
 
contentValues.put("indice", Integer.parseInt(mEditIndice.getText().toString()));
Bitmap bmp = (Bitmap) BitmapFactory.decodeResource(getResources(), riferimenti[i]);
               
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.PNG, 100, bos);
                byte[] bArray = bos.toByteArray();
                contentValues.put("bitmap", bArray);
               
                i++;
               
                //Accedo al database in scrittura
                SQLiteDatabase db = mMioDbHelper.getWritableDatabase();
                db.insert("immagini", null, contentValues);    //Inserisco i dati

Poi li accedo così tutti insieme, sempre tramite un bottone e con un AsyncTask:
Codice (Java): [Seleziona]
Cursor cursor = dataBase.getReadableDatabase().rawQuery("SELECT * FROM immagini", new String[] {});
                        int l = cursor.getCount();
                        cursor.moveToFirst();
                        int i = 0;
                        for(i = 0; i < l; i++){
                                ImageElement el = new ImageElement();
                                el.setIndice(cursor.getInt(i));
                                byte[] b = cursor.getBlob(i);
                                el.setImg(BitmapFactory.decodeByteArray(b, 0, b.length));
                                publishProgress(el);
                        }
Solo che byte[] b = cursor.getBlob(i); non gli va bene e non penso di aver capito bene come sono strutturate le tabelle SQL.

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Salvare e reperire immagini da database
« Risposta #1 il: 05 Dicembre 2014, 18:33:55 CET »
0
A prima vista vedo un problema concettuale che di sicuro non va bene.

L'indice che usi dentro getInt() e getBlob() non è il numero progressivo di elementi del cursor, è il column index, cioè il progressivo di colonna associato al campo blob.

Il cursor lo scorri con la moveToNext(), non con un indice. Riguardati rapidamente un qualsiasi tutorial sui Cursor e vedi che risolvi in un baleno.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline Ilgard

  • Utente junior
  • **
  • Post: 64
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Xperia M
  • Sistema operativo:
    Ubuntu 14.04
Re:Salvare e reperire immagini da database
« Risposta #2 il: 07 Dicembre 2014, 15:12:52 CET »
0
Sì, ho cercato un po' in giro ed ho scoperto che avevo inteso male la struttura dei cursor.
Pensavo di poterli accedere come un array, invece devo accedere per colonna e da lì prendere via via le righe corrispondenti.

Offline Ilgard

  • Utente junior
  • **
  • Post: 64
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Xperia M
  • Sistema operativo:
    Ubuntu 14.04
Re:Salvare e reperire immagini da database
« Risposta #3 il: 09 Dicembre 2014, 17:48:05 CET »
0
Mi è venuta in mente un'altra problematica, di natura teorica in questo caso.
Nella mia applicazione userò il database con 3 thread su una lista di immagini, perché quando viene selezionata una bitmap devo mostrare quella e nel frattempo salvo su database le precedenti e le successive.
Ho dunque un paio di dubbi:
1) SQLite come gestisce il multi-threading? Devo fare io a mano i controlli per la mutua esclusione oppure ci pensa lui?
2) La chiave principale si aggiorna da sola, dunque, a meno che non venga selezionata dalla lista l'immagine di indice 0, le immagini saranno messe in modo un po' incasinato nel database. Questo ovviamente è un problema perché io voglio che le immagini siano mostrate nello stesso ordine in cui si presentano nella lista. A questo scopo ho inserito il campo "indice" nel database. Tuttavia interrogare il cursor facendogli ogni volta cercare la riga con un certo indice mi sembra piuttosto inefficiente. C'è un modo per manipolare manualmente l'ordine con cui i dati sono inseriti nel database?

Offline Ilgard

  • Utente junior
  • **
  • Post: 64
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Xperia M
  • Sistema operativo:
    Ubuntu 14.04
Re:Salvare e reperire immagini da database
« Risposta #4 il: 17 Dicembre 2014, 12:29:12 CET »
0
Ho alterato il codice in modo da salvare su scheda SD le immagini e tenermi i riferimenti nel database, solo che ora ottengo di nuovo OutOfMemory oltre a problemi di accesso alle immagini.
Il database lo creo così:
Codice (Java): [Seleziona]
    private static final int DB_VERSIONE = 1;
    private String tableName = null;
    private String sql = null;

    @Override
    public void onCreate(SQLiteDatabase db) {
        //Eseguiamo la query
        if (sql != null) db.execSQL(sql);
    }

    static class Constants {  // i metadati della tabella, accessibili ovunque
        static final String ID = "_id";
        static final String ELEMENT_NAME_KEY = "indice";
        static final String ELEMENT_IMAGE_KEY = "bitmap";
    }

    public DBHelper(Context context, String dbName) {
        super(context, dbName, null, DB_VERSIONE);
        tableName = dbName;
        sql = "CREATE TABLE IF NOT EXISTS " + tableName;
        sql += "(cartella TEXT PRIMARY KEY,";
        sql += "indice INTEGER NOT NULL,";
        sql += "bitmap TEXT);";
    }

Per l'ottenimento delle immagini uso un thread che cicla diverse volte eseguendo questo codice:

Codice (Java): [Seleziona]
private String SaveImage(Bitmap finalBitmap, int n) {

        String root = Environment.getExternalStorageDirectory().toString();
        File myDir = new File(root + "/" + nomeCartella);    
        myDir.mkdirs();
       
        String fname = "Image-"+ n +".jpg";
        File file = new File (myDir, fname);
        if (file.exists ()) file.delete ();
        try {
               FileOutputStream out = new FileOutputStream(file);
               finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
               out.flush();
               out.close();

        } catch (Exception e) {
               e.printStackTrace();
        }
        return fname;
    }
   
    public class Fetcher implements Runnable{
        public void run(){
           
            DataOutputStream out;
            DataInputStream in;
            int i = 0;
            try {
                for(i = 0; i < l; i++){
                    Socket s = ConnectionClass.createSocket();
                   
                    out = new DataOutputStream(s.getOutputStream());
                    in = new DataInputStream(s.getInputStream());
                   
                    /*Devo mandare di seguito i due indici e ricevere un'immagine.
                     * Quell'immagine verrà inserita */

                    out.writeUTF(richiestaImmagine);
                    out.flush();
                    out.writeInt(indiceCartella);
                    out.flush();

                    out.writeInt(i);
                    out.flush();
                   
                    temp = in.readInt();

                    buffer = new byte[temp];
                    in.readFully(buffer);
                   
                    Bitmap b = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
                                       
                    ContentValues contentValues = new ContentValues();
                    contentValues.put("indice", i);
                    String name = SaveImage(b, i);
                    contentValues.put("bitmap", name);
                   
                    b.recycle();
                    b = null;
                    in.close();
                    out.close();
                   
                    db.insert(nomeCartella, null, contentValues);    //Inserisco i dati
                }
                cursor = db.rawQuery("SELECT * FROM "+ nomeCartella +" ORDER BY indice", null);
                cursor.moveToFirst();
                mPagerAdapter.setCursor(cursor);
               
            } catch (IOException e) {
                    e.printStackTrace();
            };
        }
Il cursor è una variabile globale, così come il database. L'errore lo da con la "Bitmap b = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);" ed è:
Codice: [Seleziona]
12-17 05:53:04.408: E/AndroidRuntime(1125): java.lang.OutOfMemoryError
12-17 05:53:04.408: E/AndroidRuntime(1125):     at android.graphics.BitmapFactory.nativeDecodeByteArray(Native Method)
12-17 05:53:04.408: E/AndroidRuntime(1125):     at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:500)
12-17 05:53:04.408: E/AndroidRuntime(1125):     at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:523)
12-17 05:53:04.408: E/AndroidRuntime(1125):     at com.example.testclient.ShowImage$Fetcher.run(ShowImage.java:141)
12-17 05:53:04.408: E/AndroidRuntime(1125):     at java.lang.Thread.run(Thread.java:841)

Inoltre la "FileOutputStream out = new FileOutputStream(file);" da questo errore:
Codice: [Seleziona]
12-17 05:53:02.048: W/System.err(1125): java.io.FileNotFoundException: /storage/sdcard/CH/Image-5.jpg: open failed: EACCES (Permission denied)
12-17 05:53:02.048: W/System.err(1125):     at libcore.io.IoBridge.open(IoBridge.java:409)
12-17 05:53:02.048: W/System.err(1125):     at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
12-17 05:53:02.058: W/System.err(1125):     at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
12-17 05:53:02.058: W/System.err(1125):     at com.example.testclient.ShowImage.SaveImage(ShowImage.java:100)
12-17 05:53:02.058: W/System.err(1125):     at com.example.testclient.ShowImage.access$3(ShowImage.java:90)
12-17 05:53:02.058: W/System.err(1125):     at com.example.testclient.ShowImage$Fetcher.run(ShowImage.java:146)
12-17 05:53:02.058: W/System.err(1125):     at java.lang.Thread.run(Thread.java:841)
12-17 05:53:02.058: W/System.err(1125): Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
12-17 05:53:02.058: W/System.err(1125):     at libcore.io.Posix.open(Native Method)
12-17 05:53:02.058: W/System.err(1125):     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
12-17 05:53:02.058: W/System.err(1125):     at libcore.io.IoBridge.open(IoBridge.java:393)
12-17 05:53:02.058: W/System.err(1125):     ... 6 more

Nel manifest i permessi sono:
Codice: [Seleziona]
  <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Dov'è che sbaglio?

Offline Ilgard

  • Utente junior
  • **
  • Post: 64
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Xperia M
  • Sistema operativo:
    Ubuntu 14.04
Re:Salvare e reperire immagini da database
« Risposta #5 il: 23 Dicembre 2014, 15:35:09 CET »
0
Nessuno che sappia aiutarmi? Inizio a pensare che l'errore OutOfMemory derivi dal fatto che, non riuscendo a scrivere su memoria, conservi i riferimenti delle bitmap. Solo che non capisco come mai non riesca a scrivere in memoria.

EDIT: ho provato con un altro progetto che esegue più o meno lo stesso codice (le parti presenti qui le esegue, solo che fa solo questo), in questo caso continuo ad avere EACCESS ma non più OutOfMemory. Inizio a pensare che il problema non riguardi la memoria heap ma il buffer. Solo che quella parte di codice è identica :|

EDIT2: EACCESS me lo dava perché l'emulatore non aveva una memoria esterna -.-'
Ora però non capisco come mai solo in uno dei due progetti mi dia OutOfMemory.
« Ultima modifica: 23 Dicembre 2014, 18:00:15 CET da Ilgard »