2012-04-20 13 views
5

私のアンドロイドアプリケーションでは、大きなデータベース(約45 MB)を使用したいと思います。インターネットからSQLiteデータベースをダウンロードしてAndroidアプリケーションにロード

解決策の1つは、(分割された)データベースをassetsフォルダに含めて、最初の起動時にデータベースディレクトリにコピーすることです。

しかし、これはディスクスペースを2回消費します。ファイルを削除できないアセットフォルダでは1回、コピー先のデータベースディレクトリでは1回です。

私は最初の起動時にインターネット(Webサーバー)からデータベースをダウンロードしたいと思っています。どうすればこのことができますか?完全なSQLiteファイルをダウンロードしてデータベースディレクトリに保存できますか?または、データベースにデータを格納するために使用されるJSONデータファイルを使用する必要がありますか?

答えて

9

解決策の1つは、(分割された)データベースをassetsフォルダに含めて、最初の起動時にデータベースディレクトリにコピーすることです。

分割する必要はありません。ちょうどZIPpedします。例はSQLiteAssetHelperを参照してください。

どうすればいいですか?

使用HttpUrlConnection。または、HttpClientを使用します。

完全なSQLiteファイルをダウンロードしてデータベースディレクトリに保存できますか?

はい。 getDatabasePath()を使用して、正しいローカルパスを使用してください。

または、データベースを作成するために使用するJSONデータファイルを使用する必要がありますか?

45MBの場合、それは非常に遅くなる可能性があります。

+0

を拡張していただきありがとうございます! 'SQLiteAssetHelper'は面白そうですが、資産の最大サイズは1MBですね。したがって、ファイルが圧縮されているかどうかは関係ありません。これはこの制限です。ですから、 'SQLiteAssetHelper'やWebサーバーからダウンロードすることをお勧めしますか? – caw

+2

@ MarcoW: "資産の最大サイズは1MBですね。" - AFAIK、それは 'aapt'によって圧縮される資産の最大サイズです。 'aapt'が圧縮しない限り、より大きなアセットを持つことができます。そのため、' SQLiteAssetHelper'はZIPファイルを使用します。 Webサーバーからダウンロードするあなたの議論は依然として非常に有効です(例えば、スペースの重複) - 私はこの質問に遭遇した他の人のために、あなたの "分割された"参照を明確にしていました。ところで、DownloadManagerを使ってデータベースをダウンロードすることもできますが、一旦ダウンロードすれば最終的な位置に移動します。 – CommonsWare

3

あなたのデータベースがそれほど大きければJSONアプローチが良いアイデアだと思います。

私は100%確実ではありませんが、アプリケーションのアップデートをリリースすると、デバイスはアプリケーション全体をダウンロードします。 45メガバイトのファイルをアプリケーションにバンドルしている場合、アップデートするたびに45メガバイトのファイルをダウンロードすることになります。いい考えではない。

あなたのできることは、アプリケーションにデータを持たない適切に構造化されたデータベースを含めることです。ユーザーがアプリケーションを開くと、Webサーバーに接続してJSONデータを取得してデータベースに取り込むことができます。ユーザーがアプリケーションを更新すると、新しい大きなファイルをダウンロードすることができなくなります。更新プログラムは既存のデータベースを抹消しません。

ユーザーがすべてを持っている限り、あなたは思ったことをしてJSONを通じてデータベースの一部を取得することもできます。あなたがモンスターの質問をしていて、インターネットへの接続が失われたとしても、このように悪いことは起こりません。

+0

ありがとう!大規模なデータベースファイルを毎回含むべきではない更新の側面は、確かに重要です。だから私は間違いなく最初の実行時に必要なすべてのデータをダウンロードする小さなアプリケーションに行く必要があります。しかしCommonswareが示唆しているように、JSONは完全なSQLiteデータベースファイルをダウンロードするよりも遅いようです。 – caw

5

私がどのような解決策を得たか尋ねられましたが、私が使用したコード(大まかに)はここにあります。もはや完全ではないかもしれないし、エレガントで清潔でもない。しかし、おそらくそれはあなたに役立つことができます。

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

パブリッククラスデータベースは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

データベースの名前を変更する特別な理由はありますか?ファイルtempFile =新しいファイル(Database.getDatabaseFolder()+ "temp.sqlite"); tempFile.renameTo(新しいファイル(Database.getDatabaseFolder()+ "dictionary.sqlite")); –

+0

@LomaRomaあなたが正しいです、これは現在余分です。ただし、ファイルの名前を変更しない場合は、データベースファイルをディスクに書き込むときに名前を変更する必要があります。私は答えを編集しました。そのスニペットには、改善されているかもしれないし、余分な部分がいくつかあることに注意してください。 – caw

+0

私はsqliteデータベースをダウンロードし、それをアプリで使用する際に問題があります。基本的に、SQLiteはdbが壊れているというエラーを投げますが、そうではありません。私はそれについて考えると、dbが開かずに変更されたときに破損エラーがスローされたように見えます(古いdbファイルの削除+新しいファイルのダウンロード)。これらの問題はありましたか? –

関連する問題