2012-04-20 14 views
5

Per la mia applicazione Android, vorrei utilizzare un database di grandi dimensioni (circa 45 MB).Scaricare il database SQLite da Internet e caricare nell'applicazione Android

Una soluzione sarebbe quella di includere il database (diviso) nella cartella delle risorse e copiarlo nella directory del database al primo avvio.

Ma ciò richiederebbe lo spazio su disco due volte, una volta nella cartella delle risorse in cui il file non può essere eliminato e una volta nella directory del database in cui è stato copiato.

Quindi preferirei scaricare il database da Internet (server web) al primo avvio. Come potrei farlo? Posso scaricare un file SQLite completo e salvarlo nella directory del database? O dovrei piuttosto andare con i file di dati JSON che vengono utilizzati per popolare il database?

risposta

9

Una soluzione sarebbe quella di includere il database (diviso) nella cartella delle risorse e copiarlo nella directory del database al primo avvio.

Non dovrebbe essere diviso, solo ZIPped. Vedi SQLiteAssetHelper per un esempio.

Come posso fare questo?

Utilizzare HttpUrlConnection. In alternativa, utilizzare HttpClient.

È possibile scaricare un file SQLite completo e salvarlo nella directory del database?

Sì. Utilizzare getDatabasePath() per ottenere il percorso locale corretto da utilizzare.

O dovrei preferire i file di dati JSON utilizzati per popolare il database?

Si potrebbe, ma per 45 MB, che sarebbe orribilmente lento.

+0

Grazie! 'SQLiteAssetHelper' sembra interessante, ma tuttavia, la dimensione massima delle risorse è 1 MB, non è vero? Quindi non importa se il file è zippato o no - è questo limite. Quindi consiglieresti di usare 'SQLiteAssetHelper' o il download da un server web? – caw

+2

@MarcoW .: "la dimensione massima delle risorse è 1 MB, vero?" - AFAIK, questa è la dimensione massima di una risorsa che viene compressa da 'aapt'. Puoi avere risorse più grandi, a patto che 'aapt' non li comprima, motivo per cui' SQLiteAssetHelper' utilizza un file ZIP. I tuoi argomenti per il download da un server Web sono ancora molto validi (ad esempio, spazio duplicato) - Stavo semplicemente chiarendo il tuo riferimento "diviso", per chiunque altro incontrasse questa domanda. A proposito, potresti anche utilizzare 'DownloadManager' per scaricare il database, anche se poi lo avresti trasferito nella posizione finale una volta scaricato. – CommonsWare

3

Penso che l'approccio JSON sarebbe un'idea migliore se il database è così grande.

Non sono sicuro al 100%, ma credo che quando si rilascia un aggiornamento alla propria applicazione, il dispositivo scaricherà l'intera applicazione. Se si associa un file 45mb con la propria applicazione, ciò significa che ogni volta che si esegue un aggiornamento, gli utenti saranno bloccati nel download di un file 45mb. Non è una buona idea.

Ciò che è possibile fare è includere il database correttamente strutturato senza dati nell'applicazione. Quando l'utente apre l'applicazione, potrebbe connettersi al server Web e ottenere i dati JSON per popolare il database. In questo modo quando l'utente aggiorna la tua applicazione non si bloccherà con il download di un nuovo file di grandi dimensioni. Gli aggiornamenti non cancellano i database esistenti.

Si potrebbe anche immaginare cose e ottenere parti del database tramite JSON fino a quando l'utente ha tutto. In questo modo, se stai facendo una query sui mostri e perdi la connessione a Internet, non accadrà nulla di troppo brutto.

+0

Grazie! Sicuramente l'aspetto degli aggiornamenti che non dovrebbero includere ogni volta il grande file di database è importante. Quindi dovrei assolutamente andare con una piccola applicazione che scarica tutti i dati necessari alla prima esecuzione. Ma come suggerisce Commonsware, JSON sembra essere più lento del semplice download del file di database SQLite completo. – caw

5

Avendo chiesto a quale soluzione mi sono trovato, ecco il codice che ho usato (approssimativamente). Potrebbe non essere più completo, e non è nemmeno elegante né pulito.Ma forse può esserti d'aiuto.

MyActivity.java

public class MyActivity extends Activity { 

    private static final String SD_CARD_FOLDER = "MyApp"; 
    private static final String DB_DOWNLOAD_PATH = "http://www.example.org/downloads/dictionary.sqlite"; 
    private Database mDB = null; 
    private DatabaseDownloadTask mDatabaseDownloadTask = null; 
    private DatabaseOpenTask mDatabaseOpenTask = null; 

    private class DatabaseDownloadTask extends AsyncTask<Context, Integer, Boolean> { 

     @Override 
     protected void onPreExecute() { 
      mProgressDialog = new ProgressDialog(MyActivity.this); 
      mProgressDialog.setTitle(getString(R.string.please_wait)); 
      mProgressDialog.setMessage(getString(R.string.downloading_database)); 
      mProgressDialog.setIndeterminate(false); 
      mProgressDialog.setMax(100); 
      mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 
      mProgressDialog.setCancelable(false); 
      mProgressDialog.show(); 
      getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 
     } 

     @Override 
     protected Boolean doInBackground(Context... params) { 
      try { 
       File dbDownloadPath = new File(Database.getDatabaseFolder()); 
       if (!dbDownloadPath.exists()) { 
        dbDownloadPath.mkdirs(); 
       } 
       HttpParams httpParameters = new BasicHttpParams(); 
       HttpConnectionParams.setConnectionTimeout(httpParameters, 5000); 
       HttpConnectionParams.setSoTimeout(httpParameters, 5000); 
       DefaultHttpClient client = new DefaultHttpClient(httpParameters); 
       HttpGet httpGet = new HttpGet(DB_DOWNLOAD_PATH); 
       InputStream content = null; 
       try { 
        HttpResponse execute = client.execute(httpGet); 
        if (execute.getStatusLine().getStatusCode() != 200) { return null; } 
        content = execute.getEntity().getContent(); 
        long downloadSize = execute.getEntity().getContentLength(); 
        FileOutputStream fos = new FileOutputStream(Database.getDatabaseFolder()+Database.DATABASE_NAME+".sqlite"); 
        byte[] buffer = new byte[256]; 
        int read; 
        long downloadedAlready = 0; 
        while ((read = content.read(buffer)) != -1) { 
         fos.write(buffer, 0, read); 
         downloadedAlready += read; 
         publishProgress((int) (downloadedAlready*100/downloadSize)); 
        } 
        fos.flush(); 
        fos.close(); 
        content.close(); 
        return true; 
       } 
       catch (Exception e) { 
        if (content != null) { 
         try { 
          content.close(); 
         } 
         catch (IOException e1) {} 
        } 
        return false; 
       } 
      } 
      catch (Exception e) { 
       return false; 
      } 
     } 

     protected void onProgressUpdate(Integer... values) { 
      if (mProgressDialog != null) { 
       if (mProgressDialog.isShowing()) { 
        mProgressDialog.setProgress(values[0]); 
       } 
      } 
     } 

     @Override 
     protected void onPostExecute(Boolean result) { 
      if (mProgressDialog != null) { 
       mProgressDialog.dismiss(); 
       mProgressDialog = null; 
      } 
      if (result.equals(Boolean.TRUE)) { 
       Toast.makeText(MyActivity.this, getString(R.string.database_download_success), Toast.LENGTH_LONG).show(); 
       mDatabaseOpenTask = new DatabaseOpenTask(); 
       mDatabaseOpenTask.execute(new Context[] { MyActivity.this }); 
      } 
      else { 
       Toast.makeText(getApplicationContext(), getString(R.string.database_download_fail), Toast.LENGTH_LONG).show(); 
       finish(); 
      } 
     } 

    } 

    private class DatabaseOpenTask extends AsyncTask<Context, Void, Database> { 

     @Override 
     protected Database doInBackground(Context ... ctx) { 
      try { 
       String externalBaseDir = Environment.getExternalStorageDirectory().getAbsolutePath(); 
       // DELETE OLD DATABASE ANFANG 
       File oldFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+SD_CARD_FOLDER); 
       File oldFile = new File(oldFolder, "dictionary.sqlite"); 
       if (oldFile.exists()) { 
        oldFile.delete(); 
       } 
       if (oldFolder.exists()) { 
        oldFolder.delete(); 
       } 
       // DELETE OLD DATABASE ENDE 
       File newDB = new File(Database.getDatabaseFolder()+"dictionary.sqlite"); 
       if (newDB.exists()) { 
        return new Database(ctx[0]); 
       } 
       else { 
        return null; 
       } 
      } 
      catch (Exception e) { 
       return null; 
      } 
     } 

     @Override 
     protected void onPreExecute() { 
      mProgressDialog = ProgressDialog.show(MainActivity.this, getString(R.string.please_wait), "Loading the database! This may take some time ...", true); 
     } 

     @Override 
     protected void onPostExecute(Database newDB) { 
      if (mProgressDialog != null) { 
       mProgressDialog.dismiss(); 
       mProgressDialog = null; 
      } 
      if (newDB == null) { 
       mDB = null; 
       AlertDialog.Builder downloadDatabase = new AlertDialog.Builder(MyActivity.this); 
       downloadDatabase.setTitle(getString(R.string.downloadDatabase)); 
       downloadDatabase.setCancelable(false); 
       downloadDatabase.setMessage(getString(R.string.wantToDownloadDatabaseNow)); 
       downloadDatabase.setPositiveButton(getString(R.string.download), new DialogInterface.OnClickListener() { 
        public void onClick(DialogInterface dialog, int which) { 
         dialog.dismiss(); 
         mDatabaseDownloadTask = new DatabaseDownloadTask(); 
         mDatabaseDownloadTask.execute(); 
        } 
       }); 
       downloadDatabase.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { 
        public void onClick(DialogInterface dialog, int which) { 
         dialog.dismiss(); 
         finish(); 
        } 
       }); 
       downloadDatabase.show(); 
      } 
      else { 
       mDB = newDB; 
      } 
     } 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     if (mDatabaseDownloadTask != null) { 
      if (mDatabaseDownloadTask.getStatus() != AsyncTask.Status.FINISHED) { 
       mDatabaseDownloadTask.cancel(true); 
      } 
     } 
     if (mDatabaseOpenTask != null) { 
      if (mDatabaseOpenTask.getStatus() != AsyncTask.Status.FINISHED) { 
       mDatabaseOpenTask.cancel(true); 
      } 
     } 
     if (mProgressDialog != null) { 
      mProgressDialog.dismiss(); 
      mProgressDialog = null; 
     } 
     if (mDB != null) { 
      mDB.close(); 
      mDB = null; 
     } 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 
      Toast.makeText(getApplicationContext(), getString(R.string.sd_card_not_found), Toast.LENGTH_LONG).show(); 
      finish(); 
     } 
     mDatabaseOpenTask = new DatabaseOpenTask(); 
     mDatabaseOpenTask.execute(new Context[] { this }); 
    } 

} 

Database.java

public class Database estende SQLiteOpenHelper {

private static final String DATABASE_NAME = "dictionary"; 
private String DATABASE_PATH = null; 
private static final int DATABASE_VERSION = 1; 
private static final String PACKAGE_NAME = "com.my.package"; 
private SQLiteDatabase db; 

public Database(Context context) { 
    super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    DATABASE_PATH = getDatabaseFolder()+DATABASE_NAME+".sqlite"; 
    db = getWritableDatabase(); 
} 

public static String getDatabaseFolder() { 
    return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Android/data/"+PACKAGE_NAME+"/databases/"; 
} 

@Override 
public synchronized SQLiteDatabase getWritableDatabase() { 
    try { 
     if (db != null) { 
      if (db.isOpen()) { 
       return db; 
      } 
     } 
     return SQLiteDatabase.openDatabase(DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS); 
    } 
    catch (Exception e) { 
     return null; 
    } 
} 

@Override 
public synchronized void close() { 
    if (db != null) { 
     db.close(); 
     db = null; 
    } 
    super.close(); 
} 

@Override 
public void onCreate(SQLiteDatabase db) { } 

@Override 
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } 

}

+1

Qualche motivo speciale per rinominare il db? File tempFile = new File (Database.getDatabaseFolder() + "temp.sqlite"); tempFile.renameTo (nuovo file (Database.getDatabaseFolder() + "dictionary.sqlite")); –

+0

@LomaRoma Hai ragione, ora è superfluo. Tuttavia, se non si rinomina il file, è necessario modificare il nome durante la scrittura del file di database sul disco. Ho modificato la risposta. Si prega di notare che ci sono molti altri pezzi in quel frammento che possono essere migliorati o addirittura superflui. – caw

+0

Sto riscontrando problemi con il download di un db sqlite e l'utilizzo nell'app. Fondamentalmente, SQLite genera un errore che il db è corrotto, il che non è il caso. Quando ci penso, sembra che l'errore di corruzione venga generato quando il db viene modificato senza essere aperto (ad esempio cancella il vecchio file db + scarica quello nuovo) Potrebbe essere? Hai avuto questi problemi? –

Problemi correlati