我有一个字典应用程序使用我提供的数据库完全脱机工作。为了能够使用数据库,我应用了我在开发人员博客上看到的解决方案。您可以从此链接参考它。该解决方案基本上是从资产文件夹中复制数据库并使用它。它在大多数设备上都可以正常工作,但有些用户在尝试查询数据库时遇到了崩溃。我已附上我收到的堆栈跟踪以及从华硕 ZenFone 5 (ZE620KL) (ASUS_X00QD) 发送的崩溃报告。我的问题是:我正在应用的做法是否有问题(使用位于 assets 文件夹中的现有数据库)?我能做些什么来避免这个异常?
at android.database.sqlite.SQLiteConnection.nativePrepareStatement (Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement (SQLiteConnection.java:903)
at android.database.sqlite.SQLiteConnection.prepare (SQLiteConnection.java:514)
at android.database.sqlite.SQLiteSession.prepare (SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init> (SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init> (SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query (SQLiteDirectCursorDriver.java:46)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory (SQLiteDatabase.java:1408)
at android.database.sqlite.SQLiteDatabase.queryWithFactory (SQLiteDatabase.java:1255)
at android.database.sqlite.SQLiteDatabase.query (SQLiteDatabase.java:1126)
at android.database.sqlite.SQLiteDatabase.query (SQLiteDatabase.java:1294)
at com.tur_cirdictionary.turkish_circassiandictionary.data.WordProvider.query (WordProvider.java:52)
at android.content.ContentProvider.query (ContentProvider.java:1064)
at android.content.ContentProvider.query (ContentProvider.java:1156)
at android.content.ContentProvider$Transport.query (ContentProvider.java:241)
at android.content.ContentResolver.query (ContentResolver.java:809)
at android.content.ContentResolver.query (ContentResolver.java:758)
at com.tur_cirdictionary.turkish_circassiandictionary.MainActivity.showSuggestionsForQuery (MainActivity.java:245)
at com.tur_cirdictionary.turkish_circassiandictionary.MainActivity.access$000 (MainActivity.java:37)
at com.tur_cirdictionary.turkish_circassiandictionary.MainActivity$2.onQueryTextChange (MainActivity.java:178)
at android.widget.SearchView.onTextChanged (SearchView.java:1250)
at android.widget.SearchView.access$2100 (SearchView.java:98)
at android.widget.SearchView$10.onTextChanged (SearchView.java:1776)
at android.widget.TextView.sendOnTextChanged (TextView.java:9784)
at android.widget.TextView.handleTextChanged (TextView.java:9881)
at android.widget.TextView$ChangeWatcher.onTextChanged (TextView.java:12539)
at android.text.SpannableStringBuilder.sendTextChanged (SpannableStringBuilder.java:1263)
at android.text.SpannableStringBuilder.replace (SpannableStringBuilder.java:575)
at android.text.SpannableStringBuilder.replace (SpannableStringBuilder.java:506)
at android.text.SpannableStringBuilder.replace (SpannableStringBuilder.java:36)
at android.view.inputmethod.BaseInputConnection.replaceText (BaseInputConnection.java:843)
at android.view.inputmethod.BaseInputConnection.commitText (BaseInputConnection.java:197)
at com.android.internal.widget.EditableInputConnection.commitText (EditableInputConnection.java:183)
at com.android.internal.view.IInputConnectionWrapper.executeMessage (IInputConnectionWrapper.java:341)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage (IInputConnectionWrapper.java:85)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:198)
at android.app.ActivityThread.main (ActivityThread.java:6732)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)
WordContract.java
package com.tur_cirdictionary.turkish_circassiandictionary.data;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.BaseColumns;
public final class WordContract {
public static final String CONTENT_AUTHORITY =
"com.tur_cirdictionary.turkish_circassiandictionary";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_WORDS = "words";
private WordContract() {}
public static class WordEntry implements BaseColumns {
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_WORDS);
public static final String CONTENT_LIST_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" +
CONTENT_AUTHORITY + PATH_WORDS;
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" +
CONTENT_AUTHORITY + "/" + PATH_WORDS;
public static final String TABLE_NAME = "words";
public static final String _ID = BaseColumns._ID;
public static final String COLUMN_NAME_CIRCASSIAN = "circassian";
public static final String COLUMN_NAME_TURKISH = "turkish";
}
}
WordDbHelper.java
package com.tur_cirdictionary.turkish_circassiandictionary.data;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.tur_cirdictionary.turkish_circassiandictionary.data.WordContract.WordEntry;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class WordDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static String DATABASE_PATH
= "/data/data/com.tur_cirdictionary.turkish_circassiandictionary/databases/";
private static final String DATABASE_NAME = "Cir_Tur.sqlite";
private SQLiteDatabase database;
private final Context context;
public WordDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
}
public void createDatabase() {
boolean dbExist = checkDataBase();
if (dbExist) {
//Cool. Don't do anything.
} else {
try {
this.getReadableDatabase();
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
String path = DATABASE_PATH + DATABASE_NAME;
checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
//Database does not exist
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null;
}
private void copyDataBase() throws IOException {
InputStream inputStream = context.getAssets().open(DATABASE_NAME);
String outFileName = DATABASE_PATH + DATABASE_NAME;
OutputStream outputStream = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
public SQLiteDatabase openDatabase() {
String path = DATABASE_PATH + DATABASE_NAME;
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
return database;
}
@Override
public synchronized void close() {
if (database != null) {
database.close();
}
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
private static final String SQL_CREATE_WORDS =
"CREATE TABLE " + WordEntry.TABLE_NAME + " ("
+ WordEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ WordEntry.COLUMN_NAME_CIRCASSIAN + " TEXT, "
+ WordEntry.COLUMN_NAME_TURKISH + " TEXT)";
private static final String SQL_DELETE_WORDS =
"DROP TABLE IF EXISTS " + WordContract.WordEntry.TABLE_NAME;
}
WordProvider.java
package com.tur_cirdictionary.turkish_circassiandictionary.data;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.tur_cirdictionary.turkish_circassiandictionary.data.WordContract.WordEntry;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class WordDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static String DATABASE_PATH
= "/data/data/com.tur_cirdictionary.turkish_circassiandictionary/databases/";
private static final String DATABASE_NAME = "Cir_Tur.sqlite";
private SQLiteDatabase database;
private final Context context;
public WordDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
}
public void createDatabase() {
boolean dbExist = checkDataBase();
if (dbExist) {
//Cool. Don't do anything.
} else {
try {
this.getReadableDatabase();
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
String path = DATABASE_PATH + DATABASE_NAME;
checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
//Database does not exist
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null;
}
private void copyDataBase() throws IOException {
InputStream inputStream = context.getAssets().open(DATABASE_NAME);
String outFileName = DATABASE_PATH + DATABASE_NAME;
OutputStream outputStream = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
public SQLiteDatabase openDatabase() {
String path = DATABASE_PATH + DATABASE_NAME;
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
return database;
}
@Override
public synchronized void close() {
if (database != null) {
database.close();
}
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
private static final String SQL_CREATE_WORDS =
"CREATE TABLE " + WordEntry.TABLE_NAME + " ("
+ WordEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ WordEntry.COLUMN_NAME_CIRCASSIAN + " TEXT, "
+ WordEntry.COLUMN_NAME_TURKISH + " TEXT)";
private static final String SQL_DELETE_WORDS =
"DROP TABLE IF EXISTS " + WordContract.WordEntry.TABLE_NAME;
}
MainActivity.java
package com.tur_cirdictionary.turkish_circassiandictionary.data;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.tur_cirdictionary.turkish_circassiandictionary.data.WordContract.WordEntry;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class WordDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static String DATABASE_PATH
= "/data/data/com.tur_cirdictionary.turkish_circassiandictionary/databases/";
private static final String DATABASE_NAME = "Cir_Tur.sqlite";
private SQLiteDatabase database;
private final Context context;
public WordDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
}
public void createDatabase() {
boolean dbExist = checkDataBase();
if (dbExist) {
//Cool. Don't do anything.
} else {
try {
this.getReadableDatabase();
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
String path = DATABASE_PATH + DATABASE_NAME;
checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
//Database does not exist
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null;
}
private void copyDataBase() throws IOException {
InputStream inputStream = context.getAssets().open(DATABASE_NAME);
String outFileName = DATABASE_PATH + DATABASE_NAME;
OutputStream outputStream = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
public SQLiteDatabase openDatabase() {
String path = DATABASE_PATH + DATABASE_NAME;
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
return database;
}
@Override
public synchronized void close() {
if (database != null) {
database.close();
}
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
private static final String SQL_CREATE_WORDS =
"CREATE TABLE " + WordEntry.TABLE_NAME + " ("
+ WordEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ WordEntry.COLUMN_NAME_CIRCASSIAN + " TEXT, "
+ WordEntry.COLUMN_NAME_TURKISH + " TEXT)";
private static final String SQL_DELETE_WORDS =
"DROP TABLE IF EXISTS " + WordContract.WordEntry.TABLE_NAME;
}
如有必要,她是整个项目的链接。