2012-04-20 203 views
5

對於我的android應用程序,我想使用一個大型數據庫(大約45 MB)。從互聯網下載SQLite數據庫並加載到Android應用程序

一種解決方案是將資源文件夾中包含(拆分)的數據庫並將其複製到第一次啓動時的數據庫目錄。

但是,這會消耗磁盤空間兩次 - 一次在資產文件夾中文件無法刪除,一次在數據庫目錄中被複制到的位置。

所以我寧願在第一次啓動時從互聯網(網絡服務器)下載數據庫。我怎麼能這樣做?我可以下載完整的SQLite文件並將其保存到數據庫目錄嗎?還是應該使用用於填充數據庫的JSON數據文件?

回答

9

一個解決方案是將(拆分的)數據庫包含在資產文件夾中,並將其複製到第一次啓動時的數據庫目錄。

它不會被拆分,只是ZIPped。一個例子見SQLiteAssetHelper

我該怎麼辦?

使用HttpUrlConnection。或者,使用HttpClient

我可以下載完整的SQLite文件並將其保存到數據庫目錄嗎?

是。使用getDatabasePath()獲取正確的本地路徑以供使用。

還是應該用JSON數據文件來填充數據庫?

你可以,但是對於45MB,這將是可怕的緩慢。

+0

謝謝! 'SQLiteAssetHelper'看起來很有趣,但是,資產的最大規模是1MB,不是嗎?所以文件是否壓縮並不重要 - 這是限制。那麼你會推薦使用'SQLiteAssetHelper'還是從網絡服務器下載? – caw 2012-04-21 11:34:34

+2

@ MarcoW .:「資產的最大規模是1MB,不是嗎?」 - AFAIK,這是通過'aapt'壓縮的資產的最大大小。你可以擁有更大的資產,只要'aapt'不會壓縮它們,這就是爲什麼'SQLiteAssetHelper'使用ZIP文件的原因。您從Web服務器下載的參數仍然非常有效(例如,重複的空間) - 我只是澄清您的「分割」引用,對於其他任何遇到此問題的人。順便說一句,你也可以使用'DownloadManager'來下載數據庫,但是一旦你下載了,你就可以把它移到最終位置。 – CommonsWare 2012-04-21 11:43:27

3

我認爲如果你的數據庫很大,JSON方法會更好。

我不是百分百肯定的,但我相信當你發佈更新到你的應用程序時,你的設備將下載整個應用程序。如果您將45MB文件與您的應用程序捆綁在一起,這意味着每次推送更新時,用戶都會被迫下載45MB文件。不是一個好主意。

你可以做的是包括適當的結構化數據庫,在你的應用程序中沒有數據。當用戶打開應用程序時,它可以連接到您的Web服務器並獲取JSON數據來填充數據庫。通過這種方式,當用戶更新您的應用程序時,他們不會因下載新的大文件而停滯不前。更新不會清除現有的數據庫。

你甚至可以看到東西並通過JSON獲取數據庫的一部分,直到用戶擁有了所有東西。這樣,如果你正在做一個怪物查詢,並且他們失去了與互聯網的連接,那麼不會有太糟糕的事情發生。

+0

謝謝你!當然,每次不應包含大型數據庫文件的更新方面都很重要。所以我絕對應該使用一個小應用程序,在第一次運行時下載所有必要的數據。但正如Commonsware所說,JSON似乎比下載完整的SQLite數據庫文件要慢。 – caw 2012-04-21 11:25:37

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

重命名數據庫的任何特殊原因? File tempFile = new File(Database.getDatabaseFolder()+「temp.sqlite」); tempFile.renameTo(new File(Database.getDatabaseFolder()+「dictionary.sqlite」)); – 2014-12-01 15:49:34

+0

@LomaRoma你說得對,現在這是多餘的。但是,如果不重命名文件,則在將數據庫文件寫入磁盤時必須更改名稱。我已經編輯了答案。請注意,該片段中還有其他幾個片斷可能會改進或者甚至是多餘的。 – caw 2014-12-01 23:25:36

+0

我在下載sqlite數據庫時遇到問題,並在應用程序中使用它。基本上,SQLite會拋出數據庫損壞的錯誤,但情況並非如此。當我想起它時,它看起來像是在沒有打開數據庫的情況下更改數據庫時發生了錯誤(例如,刪除舊的數據庫文件並下載新數據庫)可能是這樣嗎?你有這些問題嗎? – 2014-12-02 10:34:31