从Internet下载SQLite数据库并加载到Android应用程序中
对于我的android应用程序,我希望使用一个大型数据库(大约45MB) 一种解决方案是在资产文件夹中包含(拆分的)数据库,并在第一次启动时将其复制到数据库目录 但这将消耗两次磁盘空间—一次在无法删除文件的资产文件夹中,一次在复制到的数据库目录中 因此,我宁愿在第一次启动时从Internet(Web服务器)下载数据库。我怎么能这样做?我可以下载完整的SQLite文件并将其保存到数据库目录吗?或者我应该使用用于填充数据库的JSON数据文件吗 一种解决方案是在资产文件夹中包含(拆分的)数据库,并在第一次启动时将其复制到数据库目录 它不需要分开,只要拉上拉链就行了。有关示例,请参见 我怎么能这样做 使用从Internet下载SQLite数据库并加载到Android应用程序中,android,sqlite,download,Android,Sqlite,Download,对于我的android应用程序,我希望使用一个大型数据库(大约45MB) 一种解决方案是在资产文件夹中包含(拆分的)数据库,并在第一次启动时将其复制到数据库目录 但这将消耗两次磁盘空间—一次在无法删除文件的资产文件夹中,一次在复制到的数据库目录中 因此,我宁愿在第一次启动时从Internet(Web服务器)下载数据库。我怎么能这样做?我可以下载完整的SQLite文件并将其保存到数据库目录吗?或者我应该使用用于填充数据库的JSON数据文件吗 一种解决方案是在资产文件夹中包含(拆分的)数据库,并在第
HttpUrlConnection
。或者,使用HttpClient
我可以下载完整的SQLite文件并将其保存到数据库目录吗
对。用于获取要使用的正确本地路径
或者我应该使用用于填充数据库的JSON数据文件吗
您可以,但对于45MB来说,这将是非常缓慢的。如果您的数据库那么大,那么JSON方法将是一个更好的主意 我不是100%确定,但我相信当你发布应用程序更新时,你的设备将下载整个应用程序。如果您将45mb文件与应用程序捆绑在一起,这意味着每次推送更新时,您的用户都将不得不下载45mb文件。这不是个好主意 您可以做的是在应用程序中包含结构正确的数据库,而不包含任何数据。当用户打开应用程序时,它可以连接到web服务器并获取JSON数据来填充数据库。这样,当用户更新您的应用程序时,他们就不会因为下载新的大文件而陷入困境。更新不会删除现有数据库
您甚至可以想象一下,通过JSON获取部分数据库,直到用户拥有所有内容。这样,如果你正在执行一个monster查询,而他们失去了与internet的连接,那么不会发生太糟糕的事情。有人问我最终得到了什么解决方案,下面是我使用的代码(大致)。它可能不再完整,也不再优雅或干净。但也许这对你有帮助 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 });
}
}
}谢谢你!当然,每次更新时不应包含大型数据库文件的方面很重要。因此,我绝对应该使用一个小应用程序,它可以在第一次运行时下载所有必要的数据。但正如Commonware所建议的,JSON似乎比下载完整的SQLite数据库文件要慢。谢谢
SQLiteAssetHelper
看起来很有趣,但是,资产的最大大小是1MB,不是吗?因此,文件是否压缩并不重要——这是一个限制。因此,您建议使用SQLiteAssetHelper
还是从Web服务器下载?@MarcoW.:“资产的最大大小是1MB,不是吗?”--AFAIK,这是通过aapt
压缩的资产的最大大小。您可以拥有更大的资产,只要aapt
不会压缩它们,这就是SQLiteAssetHelper
使用ZIP文件的原因。您关于从Web服务器下载的论点仍然非常有效(例如,重复空间)——对于遇到此问题的任何其他人,我只是澄清您的“拆分”引用。顺便说一句,您也可以使用DownloadManager
下载数据库,不过下载后您可以自己将其移动到最终位置。@Commonware,您介意提供一个示例,将数据库文件下载到SD卡中,然后使用sqliteAssetPer
复制并使用它吗?感谢you@blueware:SQLiteAssetHelper
用于在assets/
中打包数据库。它不是用来下载数据库文件的。@commonware,是的,这是正确的,但如果您能帮我,我将不胜感激。至于SQLiteAssetHelper
我压缩了一个数据库(9MB),它的大小降到了(3MB)->放入了Assets
但是应用程序的大小增加了(9MB),而不是(3MB),你知道吗?重命名数据库有什么特别的原因吗?File tempFile=新文件(Database.getDatabaseFolder()+“temp.sqlite”);tempFile.renameTo(新文件(Database.getDatabaseFolder()+“dictionary.sqlite”)@你说得对,这是多余的。但是,如果不重命名文件,则在将数据库文件写入磁盘时必须更改名称。我已经编辑了答案。请注意,该代码段中还有其他几部分可能需要改进,甚至是多余的。我在下载sqlite db并在应用程序中使用它时遇到了问题。。基本上,SQLite会抛出一个错误,即数据库已损坏,但事实并非如此。当我思考这个问题时,它看起来像是在数据库未被打开就被更改时抛出了损坏错误(例如删除旧的数据库文件+下载新的数据库文件),可能是这样吗?你有这些问题吗?
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) { }