Tôi tin rằng vấn đề của bạn là bạn không sử dụng một trong SQLiteOpenHelper của các phương thức ( getWrictDatabse hoặc getReadableDatabase ) để mở cơ sở dữ liệu.
Thay vì bạn sử dụng SQLiteDatabase của OPEN phương pháp. Cái nào không kiểm tra và thiết lập phiên bản.
Nếu tôi bỏ ghi chú this.getReadableDatabase () trong phương thức khởi tạo của mình thì phương thứconUpgrade được gọi nhưng tôi không thể truy vấn dữ liệu và lỗi trả về.
Giới thiệu getReadableDatabase , sau đó kiểm tra và cố gắng sao chép phiên bản mới hơn, nhưng phiên bản cũ hơn đang mở và do đó bạn có thể gặp xung đột.
Bạn có thể tìm thấy câu trả lời cho câu hỏi Phương pháp nào có thể được sử dụng để quản lý các phiên bản khác nhau của cơ sở dữ liệu đã có từ trước? hữu ích (có lẽ là thứ 2).
Ví dụ làm việc
Ví dụ làm việc sau đây sử dụng mã từ câu trả lời được liên kết nhưng dựa trên mã và cơ sở dữ liệu từ câu hỏi (càng chắc chắn càng tốt).
Lớp cơ bản cốt lõi là DatabaseAssetHandler.java , bao gồm một số phương thức tĩnh để kiểm tra và sao chép cơ sở dữ liệu, tất cả đều đáng chú ý dưới dạng tệp thay vì cơ sở dữ liệu SQLite.
Một lớp con của SQLiteOpenHelper, dựa trên DatabaseHelper.java của câu hỏi lớp nhưng sử dụng các phương thức DatabaseAssethandler để kiểm tra phiên bản được mã hóa cùng với phiên bản tệp cơ sở dữ liệu (phủ nhận nhu cầu sử dụng phương thức onUpgrade và cả phương thức onCreate)
Cuối cùng, ví dụ bao gồm một Activity, MainActivity.java như bạn thường thấy. Lớp này cũng trích xuất tất cả các hàng từ cơ sở dữ liệu và kết xuất Con trỏ vào nhật ký.
DatabaseAssetManager.java
public class DatabaseAssetHandler {
static final String[] tempfiles = new String[]{"-journal","-wal","-shm"}; // temporary files to rename
public static final String backup = "-backup"; //value to be appended to file name when renaming (psuedo delete)
public static final int OUCH = -666666666;
/**
* Check if the database already exists. NOTE will create the databases folder is it doesn't exist
* @return true if it exists, false if it doesn't
*/
public static boolean checkDataBase(Context context, String dbname) {
File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database
Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish App
if (db.exists()) return true; // If it exists then return doing nothing
// Get the parent (directory in which the database file would be)
File dbdir = db.getParentFile();
// If the directory does not exits then make the directory (and higher level directories)
if (!dbdir.exists()) {
db.getParentFile().mkdirs();
dbdir.mkdirs();
}
return false;
}
/**
* Copy database file from the assets folder
* (long version caters for asset file name being different to the database name)
* @param context Context is needed to get the applicable package
* @param dbname name of the database file
* @param assetfilename name of the asset file
* @param deleteExistingDB true if an existing database file should be deleted
* note will delete journal and wal files
* note doen't actually delete the files rater it renames
* the files by appended -backup to the file name
* SEE/USE clearForceBackups below to delete the renamed files
*/
public static void copyDataBase(Context context, String dbname, String assetfilename, boolean deleteExistingDB, int version) {
checkpointIfWALEnabled(context,dbname);
final String TAG = "COPYDATABASE";
int stage = 0, buffer_size = 4096, blocks_copied = 0, bytes_copied = 0;
File f = new File(context.getDatabasePath(dbname).toString());
InputStream is;
OutputStream os;
/**
* If forcing then effectively delete (rename) current database files
*/
if (deleteExistingDB) {
f.renameTo(context.getDatabasePath(dbname + backup));
for (String s: tempfiles) {
File tmpf = new File(context.getDatabasePath(dbname + s).toString());
if (tmpf.exists()) {
tmpf.renameTo(context.getDatabasePath(dbname + s + backup));
}
}
}
//Open your local db as the input stream
Log.d(TAG,"Initiated Copy of the database file " + assetfilename + " from the assets folder."); //TODO remove if publishing
try {
is = context.getAssets().open(assetfilename); // Open the Asset file
stage++;
Log.d(TAG, "Asset file " + assetfilename + " found so attmepting to copy to " + f.getPath()); //TODO remove if publishing
os = new FileOutputStream(f);
stage++;
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[buffer_size];
int length;
while ((length = is.read(buffer)) > 0) {
blocks_copied++;
Log.d(TAG, "Attempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes."); //TODO remove if publishing
os.write(buffer, 0, length);
bytes_copied += length;
}
stage++;
Log.d(TAG,
"Finished copying Database " + dbname +
" from the assets folder, to " + f.getPath() +
String.valueOf(bytes_copied) + "were copied, in " +
String.valueOf(blocks_copied) + " blocks of size " +
String.valueOf(buffer_size) + "."
); //TODO remove if publishing
//Close the streams
os.flush();
stage++;
os.close();
stage++;
is.close();
Log.d(TAG, "All Streams have been flushed and closed.");
if (version > 0) {
setVersion(context,dbname,version);
}
} catch (IOException e) {
String exception_message = "";
e.printStackTrace();
switch (stage) {
case 0:
exception_message = "Error trying to open the asset " + dbname;
break;
case 1:
exception_message = "Error opening Database file for output, path is " + f.getPath();
break;
case 2:
exception_message = "Error flushing written database file " + f.getPath();
break;
case 3:
exception_message = "Error closing written database file " + f.getPath();
break;
case 4:
exception_message = "Error closing asset file " + f.getPath();
}
throw new RuntimeException("Unable to copy the database from the asset folder." + exception_message + " see starck-trace above.");
}
}
/**
* Copy the databsse from the assets folder where asset name and dbname are the same
* @param context
* @param dbname
* @param deleteExistingDB
*/
public static void copyDataBase(Context context, String dbname, boolean deleteExistingDB, int version) {
copyDataBase(context, dbname,dbname,deleteExistingDB, version);
}
/**
* Get the SQLite_user_vesrion from the DB in the asset folder
*
* @param context needed to get the appropriate package assets
* @param assetfilename the name of the asset file (assumes/requires name matches database)
* @return the version number as stored in the asset DB
*/
public static int getVersionFromDBInAssetFolder(Context context, String assetfilename) {
InputStream is;
try {
is = context.getAssets().open(assetfilename);
} catch (IOException e) {
return OUCH;
}
return getDBVersionFromInputStream(is);
}
/**
* Get the version from the database itself without opening the database as an SQliteDatabase
* @param context Needed to ascertain package
* @param dbname the name of the dataabase
* @return the version number extracted
*/
public static int getVersionFromDBFile(Context context, String dbname) {
InputStream is;
try {
is = new FileInputStream(new File(context.getDatabasePath(dbname).toString()));
} catch (IOException e) {
return OUCH;
}
return getDBVersionFromInputStream(is);
}
/**
* Get the Database Version (user_version) from an inputstream
* Note the inputstream is closed
* @param is The Inputstream
* @return The extracted version number
*/
private static int getDBVersionFromInputStream(InputStream is) {
int rv = -1, dbversion_offset = 60, dbversion_length = 4 ;
byte[] dbfileheader = new byte[64];
byte[] dbversion = new byte[4];
try {
is.read(dbfileheader);
is.close();
} catch (IOException e) {
e.printStackTrace();
return rv;
}
for (int i = 0; i < dbversion_length; i++ ) {
dbversion[i] = dbfileheader[dbversion_offset + i];
}
return ByteBuffer.wrap(dbversion).getInt();
}
/**
* Check to see if the asset file exists
*
* @param context needed to get the appropriate package
* @param assetfilename the name of the asset file to check
* @return true if the asset file exists, else false
*/
public static boolean ifAssetFileExists(Context context, String assetfilename) {
try {
context.getAssets().open(assetfilename);
} catch (IOException e) {
return false;
}
return true;
}
/**
* Delete the backup
* @param context
* @param dbname
*/
public static void clearForceBackups(Context context, String dbname) {
String[] fulllist = new String[tempfiles.length + 1];
for (int i = 0;i < tempfiles.length; i++) {
fulllist[i] = tempfiles[i];
}
fulllist[tempfiles.length] = ""; // Add "" so database file backup is also deleted
for (String s: fulllist) {
File tmpf = new File(context.getDatabasePath(dbname + s + backup).toString());
if (tmpf.exists()) {
tmpf.delete();
}
}
}
/**
*
* @param context The context so that the respective package is used
* @param dbname The name of the database (the old will have -backup appended)
* @param table The table from which to copy the data
*/
public static void restoreTable(Context context, String dbname, String table) {
ContentValues cv = new ContentValues();
SQLiteDatabase dbnew = SQLiteDatabase.openDatabase(context.getDatabasePath(dbname).toString(), null,SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase dbold = SQLiteDatabase.openDatabase(context.getDatabasePath(dbname + backup).toString(),null,SQLiteDatabase.OPEN_READONLY);
Cursor csr = dbold.query(table,null,null,null,null,null,null);
dbnew.beginTransaction();
while (csr.moveToNext()) {
cv.clear();
int offset = 0;
for (String column: csr.getColumnNames()) {
switch (csr.getType(offset++)){
case Cursor.FIELD_TYPE_NULL:
break;
case Cursor.FIELD_TYPE_INTEGER:
cv.put(column,csr.getLong(csr.getColumnIndex(column)));
break;
case Cursor.FIELD_TYPE_FLOAT:
cv.put(column,csr.getFloat(csr.getColumnIndex(column)));
break;
case Cursor.FIELD_TYPE_STRING:
cv.put(column,csr.getString(csr.getColumnIndex(column)));
break;
case Cursor.FIELD_TYPE_BLOB:
cv.put(column,csr.getBlob(csr.getColumnIndex(column)));
}
}
dbnew.insert(DatabaseHelper.TABLE_BOOKMARK,null,cv);
}
dbnew.setTransactionSuccessful();
dbnew.endTransaction();
csr.close();
dbnew.close();
dbold.close();
}
private static void checkpointIfWALEnabled(Context context, String dbname) {
final String TAG = "WALCHKPNT";
Cursor csr;
int wal_busy = -99, wal_log = -99, wal_checkpointed = -99;
if (!new File(context.getDatabasePath(dbname).getPath()).exists()) {
return;
}
SQLiteDatabase db = SQLiteDatabase.openDatabase(context.getDatabasePath(dbname).getPath(),null,SQLiteDatabase.OPEN_READWRITE);
csr = db.rawQuery("PRAGMA journal_mode",null);
if (csr.moveToFirst()) {
String mode = csr.getString(0);
//Log.d(TAG, "Mode is " + mode);
if (mode.toLowerCase().equals("wal")) {
csr = db.rawQuery("PRAGMA wal_checkpoint",null);
if (csr.moveToFirst()) {
wal_busy = csr.getInt(0);
wal_log = csr.getInt(1);
wal_checkpointed = csr.getInt(2);
}
//Log.d(TAG,"Checkpoint pre checkpointing Busy = " + String.valueOf(wal_busy) + " LOG = " + String.valueOf(wal_log) + " CHECKPOINTED = " + String.valueOf(wal_checkpointed) );
csr = db.rawQuery("PRAGMA wal_checkpoint(TRUNCATE)",null);
csr.getCount();
csr = db.rawQuery("PRAGMA wal_checkpoint",null);
if (csr.moveToFirst()) {
wal_busy = csr.getInt(0);
wal_log = csr.getInt(1);
wal_checkpointed = csr.getInt(2);
}
//Log.d(TAG,"Checkpoint post checkpointing Busy = " + String.valueOf(wal_busy) + " LOG = " + String.valueOf(wal_log) + " CHECKPOINTED = " + String.valueOf(wal_checkpointed) );
}
}
csr.close();
db.close();
}
private static void setVersion(Context context, String dbname, int version) {
SQLiteDatabase db = SQLiteDatabase.openDatabase(context.getDatabasePath(dbname).getPath(),null,SQLiteDatabase.OPEN_READWRITE);
db.setVersion(version);
db.close();
}
}
- Lưu ý rằng không phải tất cả các phương pháp đều được sử dụng.
- Đã sửa đổi ghi chú (2019-05-08) để đặt cụ thể số phiên bản sau khi bản sao của tệp nội dung gọi đến setVersion mới phương pháp.
- Đã sửa đổi ghi chú (2019-05-08) để gọi phương thức checkpointIfWALEnabled mới trước bản sao, để cơ sở dữ liệu được kiểm tra.
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "dictionary.db";
private static final int DB_VERSION = 1;
//private String DB_PATH = null; //<<<<<<<<<< NOT NEEDED
public static final String TABLE_DICTIONARY = "dictionary";
public static final String TABLE_BOOKMARK= "bookmark";
public static final String COL_ID = "id";
public static final String COL_WORD = "word";
public static final String COL_DEFINITION = "definition";
public Context mcontext;
public SQLiteDatabase mDatabase;
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.mcontext = context;
Log.d("DBVERSION","The Database Version (as hard coded) is " + String.valueOf(DB_VERSION));
int dbversion = DatabaseAssetHandler.getVersionFromDBFile(context,DB_NAME);
Log.d("DBVERSION","The Database Version (as per the database file) is " + String.valueOf(dbversion));
// Copy the Database if no database exists
if (!DatabaseAssetHandler.checkDataBase(context,DB_NAME)) {
DatabaseAssetHandler.copyDataBase(context,DB_NAME,true,DB_VERSION);
} else {
if (DB_VERSION > dbversion && DatabaseAssetHandler.checkDataBase(context, DB_NAME)) {
DatabaseAssetHandler.copyDataBase(context, DB_NAME, true,DB_VERSION);
DatabaseAssetHandler.clearForceBackups(context, DB_NAME); // Clear the backups
}
}
mDatabase = this.getWritableDatabase(); //<<<<<<<<<<<<<
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public void openDatabase() throws SQLException {
mDatabase = this.getWritableDatabase();
}
@Override
public synchronized void close() {
if (mDatabase != null)
mDatabase.close();
super.close();
}
}
- Đã sửa đổi ghi chú (2019-05-08) để chuyển số phiên bản sang phương thức copyDatabase.
MainActivity.java
public class MainActivity extends AppCompatActivity {
DatabaseHelper mDBHlpr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDBHlpr = new DatabaseHelper(this);
Cursor csr = mDBHlpr.getWritableDatabase().query(
DatabaseHelper.TABLE_DICTIONARY,
null,null,null,null,null,null
);
DatabaseUtils.dumpCursor(csr);
csr.close();
}
}
Kết quả
Đầu tiên, cơ sở dữ liệu được tạo bằng công cụ bên ngoài có 2 hàng trong bảng từ điển và được sao chép vào thư mục nội dung.
Chạy 1.
Lần chạy đầu tiên, sao chép cơ sở dữ liệu từ thư mục nội dung và kết quả là nhật ký chứa:-
04-17 19:24:54.249 3233-3233/m.example.so55711282dictionary D/DBVERSION: The Database Version (as hard coded) is 1
04-17 19:24:54.249 3233-3233/m.example.so55711282dictionary D/DBVERSION: The Database Version (as per the database file) is -666666666
04-17 19:24:54.249 3233-3233/m.example.so55711282dictionary D/DBPATH: DB Path is /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:24:54.250 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Initiated Copy of the database file dictionary.db from the assets folder.
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Asset file dictionary.db found so attmepting to copy to /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 3 which has 4096 bytes.
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: Finished copying Database dictionary.db from the assets folder, to /data/data/m.example.so55711282dictionary/databases/dictionary.db12288were copied, in 3 blocks of size 4096.
04-17 19:24:54.251 3233-3233/m.example.so55711282dictionary D/COPYDATABASE: All Streams have been flushed and closed.
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: >>>>> Dumping cursor [email protected]
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: 0 {
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: id=1
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: word=Apple
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: definition=Thing that drops from an Apple Tree.
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: }
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: 1 {
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: id=2
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: word=Bucket
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: definition=Hand held container with carrying hanlde.
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: }
04-17 19:24:54.273 3233-3233/m.example.so55711282dictionary I/System.out: <<<<<
tức là Cơ sở dữ liệu đã được sao chép từ thư mục nội dung và các hàng dự kiến đã được kết xuất.
Chạy 2
Nếu không thực hiện bất kỳ thay đổi nào, Ứng dụng đã được chạy lại (để kiểm tra xem nó không sao chép lại cơ sở dữ liệu):- Lần này logcat chứa:-
04-17 19:30:57.444 3343-3343/? D/DBVERSION: The Database Version (as hard coded) is 1
04-17 19:30:57.445 3343-3343/? D/DBVERSION: The Database Version (as per the database file) is 1
04-17 19:30:57.445 3343-3343/? D/DBPATH: DB Path is /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:30:57.449 3343-3343/? I/System.out: >>>>> Dumping cursor [email protected]
04-17 19:30:57.449 3343-3343/? I/System.out: 0 {
04-17 19:30:57.449 3343-3343/? I/System.out: id=1
04-17 19:30:57.449 3343-3343/? I/System.out: word=Apple
04-17 19:30:57.450 3343-3343/? I/System.out: definition=Thing that drops from an Apple Tree.
04-17 19:30:57.450 3343-3343/? I/System.out: }
04-17 19:30:57.450 3343-3343/? I/System.out: 1 {
04-17 19:30:57.450 3343-3343/? I/System.out: id=2
04-17 19:30:57.450 3343-3343/? I/System.out: word=Bucket
04-17 19:30:57.450 3343-3343/? I/System.out: definition=Hand held container with carrying hanlde.
04-17 19:30:57.450 3343-3343/? I/System.out: }
04-17 19:30:57.450 3343-3343/? I/System.out: <<<<<
tức là cơ sở dữ liệu, như nó tồn tại, chưa được sao chép.
Chạy 3.
Cơ sở dữ liệu đã được sửa đổi bằng cách thêm hai hàng khác bằng công cụ bên ngoài và sau đó được sao chép vào thư mục nội dung thay thế tệp cơ sở dữ liệu cũ hơn và DB_VERSION được đổi thành 2.
Nhật ký chứa:-
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/DBVERSION: The Database Version (as hard coded) is 2
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/DBVERSION: The Database Version (as per the database file) is 1
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/DBPATH: DB Path is /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/DBPATH: DB Path is /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Initiated Copy of the database file dictionary.db from the assets folder.
04-17 19:35:16.661 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Asset file dictionary.db found so attmepting to copy to /data/data/m.example.so55711282dictionary/databases/dictionary.db
04-17 19:35:16.662 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
04-17 19:35:16.662 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
04-17 19:35:16.662 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Attempting copy of block 3 which has 4096 bytes.
04-17 19:35:16.662 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: Finished copying Database dictionary.db from the assets folder, to /data/data/m.example.so55711282dictionary/databases/dictionary.db12288were copied, in 3 blocks of size 4096.
04-17 19:35:16.662 3459-3459/m.example.so55711282dictionary D/COPYDATABASE: All Streams have been flushed and closed.
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: >>>>> Dumping cursor [email protected]
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: 0 {
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: id=1
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: word=Apple
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: definition=Thing that drops from an Apple Tree.
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: }
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: 1 {
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: id=2
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: word=Bucket
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: definition=Hand held container with carrying hanlde.
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: }
04-17 19:35:16.689 3459-3459/m.example.so55711282dictionary I/System.out: 2 {
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: id=3
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: word=Yelllow
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: definition=A colour.
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: }
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: 3 {
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: id=4
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: word=Zebra
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: definition=A balck and white, horse-like animal.
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: }
04-17 19:35:16.690 3459-3459/m.example.so55711282dictionary I/System.out: <<<<<
Chạy 4.
Ứng dụng đang chạy lại (không sao chép lại cơ sở dữ liệu và kết xuất 4 hàng)
Chạy 5.
Ứng dụng được gỡ cài đặt và chạy lại (phản ánh cài đặt mới của Ứng dụng khi phiên bản Cơ sở dữ liệu ở mức 2 (ví dụ:tải xuống / cài đặt mới Ứng dụng từ playstore)):-
Cơ sở dữ liệu (phiên bản có 4 hàng) được sao chép và 4 hàng được kết xuất.
- Lưu ý rằng Phiên bản Cơ sở dữ liệu (theo tệp cơ sở dữ liệu) là -666666666 tin nhắn được bao gồm. Điều này được hiển thị khi không có cơ sở dữ liệu nào được thay thế (số lượng có thể dễ dàng thay đổi cho phù hợp).