I am trying to implement an offline map feature in an android WebView-based app:
Using DownloadManager
, I download an sqlite database file (of a structure like .mbtiles) from our webserver, which contains blobs of the map tile images (the "offline map") and store it in the android external storage through
public void downloadMap() {
downloadManager = (DownloadManager) getActivity().getSystemService(Activity.DOWNLOAD_SERVICE);
DownloadManager.Request r = new DownloadManager.Request(Uri.parse("http://sry.youtoknow.idontwant/map.sqlite"));
r.setDestinationInExternalFilesDir(getContext(), "map", "map.sqlite");
downloadManager.enqueue(r);
}
The Download seems to work perfectly well.
I register a BroadcastReceiver
to receive DownloadManager.ACTION_DOWNLOAD_COMPLETE
and store the path to "map.sqlite" using SharedPreferences
.
To access the database from JavaScript for using the tile images in the WebView
, I intercept XHRequests by checking for a custom fictive schema name customhttp:
in the URL:
webView.setWebViewClient(new WebViewClient() {
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
if (!url.startsWith("customhttp:")) {
return null;
} else {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
SQLiteDatabase db = SQLiteDatabase.openDatabase(preferences.getString("map_uri", Environment.getExternalStorageDirectory().getPath() + "berlin.sqlite"), null, SQLiteDatabase.OPEN_READONLY);
}
}
});
My problem is that I seem to implement openDatabase()
in a wrong way as I get the following unpleasant error with the corresponding line, but can't figure out why:
E/SQLiteLog(#####): (14) cannot open file at line ##### of [##########]
E/SQLiteLog(#####): (14) os_unix.c:#####: (2) open(//file:///storage/emulated/0/Android/data/idontwant.youtoknow.sry/files/map/map.sqlite) -
E/SQLiteDatabase(#####): Failed to open database 'file:///storage/emulated/0/Android/data/idontwant.youtoknow.sry/files/map/map.sqlite'.
E/SQLiteDatabase(#####): android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
[...]
The file exists, is not corrupt and its path seems to be correct. I was able to open the file using an SQLite viewing app.
Question: Why does my Code not work; how can I make it work?
I am aware that there exist at least three very similar questions with the above error message here, but they are not that specific, detailed and/or do not exactly fit my needs. I also didn't manage to solve the problem after reading this article:
http://blog.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/.
Thank you very much for your help!
UPDATE:
I tried creating a new database using SQLiteDatabase.openOrCreateDatabase()
in external storage in order to compare it to the original database. It was not created - instead I surprisingly confronted the same error message (could not open database).
Then I tried again to open a new database, this time in RAM, using SQLiteDatabase.openOrCreateDatabase(":memory:", null)
and it did not throw any error.
Thus I begin to think that the problem is some access restriction to external storage or a mistake when addressing external storage rather than the database file itself.
We can retrieve anything from database using an object of the Cursor class. We will call a method of this class called rawQuery and it will return a resultset with the cursor pointing to the table. We can move the cursor forward and retrieve the data. This method return the total number of columns of the table.
The Android SDK provides dedicated APIs that allow developers to use SQLite databases in their applications. The SQLite files are generally stored on the internal storage under /data/data/<packageName>/databases.
I finally managed to find the mistake, a quite sobering one.
I received DownloadManager.ACTION_DOWNLOAD_COMPLETE
and stored the URI to the downloaded database in SharedPreferences
for later reference inside the shouldInterceptRequest
method using:
...
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
if (c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
Editor editor = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
editor.putString("map_uri", c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
editor.commit();
}
}
c.close();
In shouldInterceptRequest
I then called
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
SQLiteDatabase db = SQLiteDatabase.openDatabase(preferences.getString("map_uri", Environment.getExternalStorageDirectory().getPath() + "map.sqlite"), null, SQLiteDatabase.OPEN_READONLY);
and thereby passed the URI to openDatabase
, not the Path alone.
I now Uri.parse(preferences.getString("map_uri", ...)).getPath()
and everything works. The map loads.
Thank you very much for your help!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With