У меня была аналогичная проблема. Тем не менее, я целенаправленно удалял текущую базу данных как часть восстановления.
Я думаю, что SQLite помещает базу данных как прочитанную только для обеспечения защиты от file unlinked while open:
.
После восстановления любые обновления сбой attempt to write a readonly database (code 1032)
.
Мое решение состояло в том, чтобы восстановить DBHelper. Я сделал это, добавив метод reopen
, который я вызываю.
, например.
public static void reopen(Context context) {
instance = new DBHelper(context);
}
Затем я вызываю / вызываю это с помощью
if(copytaken && origdeleted && restoredone) {
DBHelper.reopen(context);
DBHelper.getHelper(context).expand(null,true);
}
. Вызов метода expand - это мой эквивалент / getaround для onUpgrade / versions. Он добавляет таблицы и столбцы в соответствии с псевдосхемой, сравниваемой с фактической базой данных.
Полный DBHelper: -
/**
* DBHelper
*/
@SuppressWarnings("WeakerAccess")
class DBHelper extends SQLiteOpenHelper {
private static final String LOGTAG = "SW-DBHelper";
private static final String DBNAME = DBConstants.DATABASE_NAME;
private static final String dbcreated =
"001I Database " + DBNAME + " created.";
private static final String dbunusable =
"002E Database " + DBNAME +
" has been set as unusable (according to schema).";
private static final String dbexpanded =
"003I Database " + DBNAME + " expanded.";
private static final String dbexpandskipped =
"004I Database " + DBNAME + " expand skipped - nothing to alter.";
private static final String dbbuildskipped =
"005I Database" + DBNAME + " build skipped - no tables to add";
public static final String THISCLASS = DBHelper.class.getSimpleName();
/**
* Consrtuctor
*
* @param context activity context
* @param name database name
* @param factory cursorfactory
* @param version database version
*/
DBHelper(Context context, @SuppressWarnings("SameParameterValue") String name, @SuppressWarnings("SameParameterValue") SQLiteDatabase.CursorFactory factory, @SuppressWarnings("SameParameterValue") int version) {
super(context, name, factory, version);
}
/**
* Instantiates a new Db helper.
*
* @param context the context
*/
DBHelper(Context context) {
super(context, DBConstants.DATABASE_NAME, null, 1);
}
private static DBHelper instance;
/**
* Gets helper.
*
* @param context the context
* @return the helper
*/
static synchronized DBHelper getHelper(Context context) {
if(instance == null) {
instance = new DBHelper(context);
}
return instance;
}
@Override
public void onCreate(SQLiteDatabase db) {
expand(db, false);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
}
/**
* expand create database tables
*
* @param db SQLIte Database, if null then instance is used
* @param buildandexpand to attempt both create and expand
*/
void expand(SQLiteDatabase db, boolean buildandexpand) {
String mode = "Create Mode.";
if (buildandexpand) {
mode = "Expand Mode.";
}
String msg = mode;
String methodname = new Object(){}.getClass().getEnclosingMethod().getName();
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname);
// if no database has been passed then get the database
if(db == null) {
db = instance.getWritableDatabase();
}
// Build Tables to reflect schema (SHOPWISE) only if schema is usable
if(DBConstants.SHOPWISE.isDBDatabaseUsable()) {
// Check to see if any tables need to be added
ArrayList<String> buildsql = DBConstants.SHOPWISE.generateDBBuildSQL(db);
if (!buildsql.isEmpty()) {
DBConstants.SHOPWISE.actionDBBuildSQL(db);
msg = dbcreated + buildsql.size() + " tables added.";
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname);
} else {
msg = dbbuildskipped;
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname);
}
if(buildandexpand) {
ArrayList<String> altersql = DBConstants.SHOPWISE.generateDBAlterSQL(db);
if(!altersql.isEmpty()) {
msg = dbexpanded + altersql.size() + " columns added.";
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname);
DBConstants.SHOPWISE.actionDBAlterSQL(db);
} else {
msg = dbexpandskipped;
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname);
}
}
} else {
msg = dbunusable + "\n" +
DBConstants.SHOPWISE.getAllDBDatabaseProblemMsgs();
LogMsg.LogMsg(LogMsg.LOGTYPE_ERROR,LOGTAG,msg,THISCLASS,methodname);
}
}
public static void reopen(Context context) {
instance = new DBHelper(context);
}
}
Типа микса:
Установите его в вашем файле макета :-
<CheckBox android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="new checkbox"
android:background="@drawable/checkbox_background"
android:button="@drawable/checkbox" />
где @drawable/checkbox будет выглядеть так:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:state_focused="true"
android:drawable="@drawable/checkbox_on_background_focus_yellow" />
<item android:state_checked="false" android:state_focused="true"
android:drawable="@drawable/checkbox_off_background_focus_yellow" />
<item android:state_checked="false"
android:drawable="@drawable/checkbox_off_background" />
<item android:state_checked="true"
android:drawable="@drawable/checkbox_on_background" />
</selector>
Это может быть достигнуто при помощи AppCompatCheckBox
. Можно использовать app:buttonCompat="@drawable/selector_drawable"
для изменения селектора.
Это работает с PNGs, но я не нашел, что путь к нему работал с Векторным Drawables.
Альтернативой может быть использование drawable / textview вместо флажка и соответствующее управление им. Я использовал этот метод, чтобы иметь собственные отмеченные и непроверенные изображения для приложения-задачи.