对使用房间数据库填充 AutoCompleteTextView 建议感到困惑
confused about Populating AutoCompleteTextView Suggestions with a Room Database
我是新手,正在尝试在 JAVA 中的 android 应用程序中实现 autoComplete 函数(不是 Kotlin,因为我还没有了解它)与本教程:
用户可以在自动完成的文本视图中搜索城市名称,当他们键入时,建议会根据他们键入的内容出现在下拉列表中,例如,如果他们键入“N”,那么建议应显示“纽约”、“新城堡”等
这些建议应该预先填充了一个包含数千个城市信息的现有数据库(数据已经在里面),我将这个 city_db.db 文件保存在我的资产文件夹中。
我有几个问题:
- 在写My DAO.java class的时候应该包括我的查询命令,我只包括查询部分,因为我只需要从数据库中读取,不需要写入,所以我写了:
package com.example.roomtest;
import androidx.room.Dao;
import androidx.room.Query;
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * city_name FROM city")
}
但它只显示红线并告诉我“此处不允许注释”。
而且我不确定这是否是正确的查询命令,它不应该像
"SELECT * city_name FROM city where like "what the user typed%" "?
- 我不确定我是否需要更多 classes 来实现这个自动完成功能,
现在我有这些 classes:
1.MainActivity.java
2.Entity.java
3.Dao.java
4.Database.java
我需要更多 classes 来实现这个自动完成功能吗?我想我错过了什么。
这是我在所有 class 中的代码:
package com.example.roomtest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private AutoCompleteTextView at;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
at = findViewById(R.id.att);
}
}
package com.example.roomtest;
import android.content.Context;
import androidx.room.Room;
import androidx.room.RoomDatabase;
@androidx.room.Database(entities = Entity.class,version = 1)
//must include the entity associated with database
public abstract class Database extends RoomDatabase {
//must be abstract
public abstract DAO dao();
//must declare this
//check if a database instance is existing
//if not, create one and return it
private static volatile Database db;
static Database getDatabase(final Context context){
if(db==null){
synchronized (Database.class){
if(db == null){
db =Room.databaseBuilder(context.getApplicationContext(),
Database.class, "Database")
.createFromAsset("city_db.db").build();
}
}
}
return db;
}
}
package com.example.roomtest;
import androidx.room.ColumnInfo;
import androidx.room.PrimaryKey;
@androidx.room.Entity(tableName = "city")
public class Entity {
//define data types in this class
@PrimaryKey
public int id;
@ColumnInfo(name = "city_name")
public String name;
public String state;
public String country;
public String lon;
public String lat;
}
package com.example.roomtest;
import androidx.room.Dao;
import androidx.room.Query;
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * city_name FROM city")
}
我的 gradle 依赖项:
dependencies {
//room components
def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
这里还有我的数据库的属性:
很抱歉这么长post,但我对这个简单功能所需的所有工作太困惑了,如果有人知道这方面的好教程,请与我分享,谢谢。
您需要在列名称之间使用逗号(* 表示所有列,因此不需要 *,city_name)。
即 SELECT * FROM city....
相当于,在你的情况下,说 SELECT id,city_name,state,country,lon,lat FROM city ....
虽然 SELECT *,city_name ....
在说 SELECT id,city_name,state,country,lon,lat,city_name FROM city ....
虽然有效SQL为什么两次得到相同的值(修辞)?此外,Room 应该使用哪个(即使它们相同)city_name(rehtorical)? 这是模棱两可的,有可能导致错误(我认为 room 会处理这个并选择最后一个)
你想要:-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * FROM city WHERE city_name LIKE :cityName")
List<Entity> getCity(String cityName); //<<<< The method that is called from the code
}
这将 return 一个列表,即实体对象的列表
或者:-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT city_name FROM city WHERE city_name LIKE :cityName")
List<String> getCityName(cityName);
}
- 这是一个 return 列表,其中每个字符串都是 city_name 的
- 你可以两者兼得
调用时,如果传递的字符串 (cityName) 是“New%”,那么您将得到 New York、Newcastle 等(即 % 是 1 个或多个字符的通配符 _ 是单个字符)
例子
使用你的代码,稍作改动,上面的代码编译成功 运行s OK。
上面建议的更改已经包含(两者)所以使用的 DAO class 是 :-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * FROM city WHERE city_name LIKE :cityName")
List<Entity> getCitiesByName(String cityName);
@Query("SELECT city_name FROM city WHERE city_name LIKE :cityName")
List<String> getCityNamesByName(String cityName);
}
我对 数据库 class 进行了一些更改,以 a) 允许在主线程上 运行ning(添加 .allowMainThreadQueries()
) b) 不根据 :-
从资产文件创建(注释掉 .createFromAsset("city_db.db")
)
@androidx.room.Database(entities = Entity.class,version = 1)
//must include the entity associated with database
public abstract class Database extends RoomDatabase {
//must be abstract
public abstract DAO dao();
//must declare this
//check if a database instance is existing
//if not, create one and return it
private static volatile Database db;
static Database getDatabase(final Context context){
if(db==null){
synchronized (Database.class){
if(db == null){
db = Room.databaseBuilder(context.getApplicationContext(),
Database.class, "Database")
//.createFromAsset("city_db.db")
.allowMainThreadQueries()
.build();
}
}
}
return db;
}
}
使用的 MainActivity 是 :-
public class MainActivity extends AppCompatActivity {
Database db;
DAO dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Database.getDatabase(this);
dao = db.dao();
dao.getCitiesByName("Somewhere%");
dao.getCityNamesByName("Nowhere%");
}
}
因此,即使没有对结果进行任何处理(数据库中没有数据),上面的操作也会打开数据库(不使用 .createFromAsset 创建它)。
您可以在 Android Studio 中根据 App Inspection(又名数据库检查器)查看此内容:-
像你这样编码,除非资产文件的副本在按照建议修改时有效。
正在进行的问题
根据您的屏幕截图,您可能会在 .build()
.
方面遇到持续的问题
房间期望复制的数据库与从实体派生的模式完全匹配(对于在@Database 注释的实体参数中定义的实体)。
作为一个示例,屏幕截图显示城市名称位于名为 name 的列(字段)中,而在您的实体中则是等效的列名称,按照实体实体是 city_name。房间构建将失败。同样,coord.lon 和 coord.lat 是经度和纬度。
此外,列定义(列类型和约束,例如 NOT NULL)也必须匹配,否则 Room 构建将失败。
因此,您必须确保模式匹配。这可能需要使用一种可用的工具来相应地转换数据库(由于 Room 施加的限制,转换正在复制的数据库可能比尝试匹配现有数据库容易得多)。
起点是当您使用包含实体的 valid/usable @Database 编译时 Room 生成的 SQL。
例如在您的情况下,在 class 中生成的 java(从 Android Studio 中的 Android 视图可见)与具有 @ 的 class 命名相同数据库注释,但后缀为 _IMPL。
因此,由于您的@Database class 被命名为数据库,因此将会有一个 class Database_IMPL.
在 class 中会有一个方法 createAllTables 它有 SQL 用于所有 table(任何 SQL 可以忽略与 room_ 的交易)。
例如你的会是这样的:-
您很可能需要使用 SQLite 工具转换数据库,例如 DB Browser for SQLite、SQlite Studio、DBeaver、Navicat For SQlite 以使用模式。
粗略地说,使用从生成的java中获得的SQL创建table,然后复制数据。
转换本身会非常简单。首先创建 city_db.db 文件的副本。
打开其中一个(根据您的喜好,在您喜欢的 SQLite 工具中进行演示
我使用 Navicat 并使用查询生成原始示例(只有几个国家))按照 :-
CREATE TABLE IF NOT EXISTS city (id INTEGER PRIMARY KEY, name TEXT,state TEXT, country TEXT, `coord.lon` REAL, `coord.lat`);
INSERT INTO city VALUES
(3904809,'Santa Rita','','BO',-63.3499,-17.966),
(3904890,'Santa Elenta','','BO',-64.7833,-20.5496)
;
- don't use the above
然后使用下面的 SQL 注意这将删除原来的 table (不想要超大的资产文件)。 如果 运行 第二次,这将失败,因为原始列名已被有效覆盖,因此原始列名不存在。
DROP TABLE IF EXISTS room_city;
/*As copied from the App's generated java */
/* BUT table name changed to room_city from city */
CREATE TABLE IF NOT EXISTS `room_city` (`id` INTEGER NOT NULL, `city_name` TEXT, `state` TEXT, `country` TEXT, `lon` TEXT, `lat` TEXT, PRIMARY KEY(`id`));
INSERT INTO room_city SELECT id,name AS city_name, state, country, `coord.lon` AS lon, `coord.lat` AS lat FROM city;
/* To clean up so as to not include the original in the asset
you don't want the asset hold twice the data that it needs to
!!!!!NOTE!!!!! you should use a copy of the original city database
*/
DROP TABLE IF EXISTS city;
ALTER TABLE `room_city` RENAME TO `city`;
VACUUM;
/* Below not needed but good to check all is as expected */
SELECT * FROM city;
SELECT * FROm sqlite_master;
请注意,这假设原始数据库与屏幕截图一致。
VACUUM 之后的第一个 SELECT 显示:-
第二个显示架构:-
- 它与 Room 期望的架构相匹配
应关闭数据库和连接(打开检查并再次关闭以仔细检查要复制的文件是否已保存)。
然后将文件复制到 assets 文件夹(您可能需要创建它)确保它被命名(可以重命名)city_db.db . :-
在使用上述演示的情况下,数据库 class 已更改为现在包含行 .createFromAsset("city_db.db") // <<<<< reinstated
和 MainActivity 已更改为:-
public class MainActivity extends AppCompatActivity {
Database db;
DAO dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Database.getDatabase(this);
dao = db.dao();
for(Entity city: dao.getCitiesByName("%")) { //* <<<<<< changed to show all as only 2 cities in demo
Log.d("CITYINFO-1","CityName is " + city.name + " state/county is " + city.state + " etc.....");
}
for(String cityname: dao.getCityNamesByName("%")) { //* <<<<<< changed to show all as only 2 cities in demo
Log.d("CITYINFO-2","CityName is " + cityname);
}
dao.getCityNamesByName("New%");
}
}
当 运行 日志显示时:-
2021-10-23 15:44:37.480 D/CITYINFO-1: CityName is Santa Rita state/county is etc.....
2021-10-23 15:44:37.480 D/CITYINFO-1: CityName is Santa Elenta state/county is etc.....
2021-10-23 15:44:37.482 D/CITYINFO-2: CityName is Santa Rita
2021-10-23 15:44:37.482 D/CITYINFO-2: CityName is Santa Elenta
所以数据库可以正常工作。
如果不转换会怎样?
再次根据屏幕截图,应用程序失败并显示:-
2021-10-23 15:50:53.826 6345-6345/a.a.so69683821cities E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so69683821cities, PID: 6345
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so69683821cities/a.a.so69683821cities.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: city(a.a.so69683821cities.Entity).
Expected:
TableInfo{name='city', columns={country=Column{name='country', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, city_name=Column{name='city_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lon=Column{name='lon', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, state=Column{name='state', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lat=Column{name='lat', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='city', columns={country=Column{name='country', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, state=Column{name='state', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, coord.lat=Column{name='coord.lat', type='', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}, coord.lon=Column{name='coord.lon', type='REAL', affinity='4', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
其中 EXPECTED 是 Room 根据实体期望的架构 class 但是 Room FOUND 不匹配来自资产的架构。
我是新手,正在尝试在 JAVA 中的 android 应用程序中实现 autoComplete 函数(不是 Kotlin,因为我还没有了解它)与本教程:
用户可以在自动完成的文本视图中搜索城市名称,当他们键入时,建议会根据他们键入的内容出现在下拉列表中,例如,如果他们键入“N”,那么建议应显示“纽约”、“新城堡”等
这些建议应该预先填充了一个包含数千个城市信息的现有数据库(数据已经在里面),我将这个 city_db.db 文件保存在我的资产文件夹中。
我有几个问题:
- 在写My DAO.java class的时候应该包括我的查询命令,我只包括查询部分,因为我只需要从数据库中读取,不需要写入,所以我写了:
package com.example.roomtest;
import androidx.room.Dao;
import androidx.room.Query;
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * city_name FROM city")
}
但它只显示红线并告诉我“此处不允许注释”。
而且我不确定这是否是正确的查询命令,它不应该像
"SELECT * city_name FROM city where like "what the user typed%" "?
- 我不确定我是否需要更多 classes 来实现这个自动完成功能, 现在我有这些 classes:
1.MainActivity.java
2.Entity.java
3.Dao.java
4.Database.java
我需要更多 classes 来实现这个自动完成功能吗?我想我错过了什么。
这是我在所有 class 中的代码:
package com.example.roomtest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private AutoCompleteTextView at;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
at = findViewById(R.id.att);
}
}
package com.example.roomtest;
import android.content.Context;
import androidx.room.Room;
import androidx.room.RoomDatabase;
@androidx.room.Database(entities = Entity.class,version = 1)
//must include the entity associated with database
public abstract class Database extends RoomDatabase {
//must be abstract
public abstract DAO dao();
//must declare this
//check if a database instance is existing
//if not, create one and return it
private static volatile Database db;
static Database getDatabase(final Context context){
if(db==null){
synchronized (Database.class){
if(db == null){
db =Room.databaseBuilder(context.getApplicationContext(),
Database.class, "Database")
.createFromAsset("city_db.db").build();
}
}
}
return db;
}
}
package com.example.roomtest;
import androidx.room.ColumnInfo;
import androidx.room.PrimaryKey;
@androidx.room.Entity(tableName = "city")
public class Entity {
//define data types in this class
@PrimaryKey
public int id;
@ColumnInfo(name = "city_name")
public String name;
public String state;
public String country;
public String lon;
public String lat;
}
package com.example.roomtest;
import androidx.room.Dao;
import androidx.room.Query;
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * city_name FROM city")
}
我的 gradle 依赖项:
dependencies {
//room components
def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
这里还有我的数据库的属性:
您需要在列名称之间使用逗号(* 表示所有列,因此不需要 *,city_name)。
即 SELECT * FROM city....
相当于,在你的情况下,说 SELECT id,city_name,state,country,lon,lat FROM city ....
虽然 SELECT *,city_name ....
在说 SELECT id,city_name,state,country,lon,lat,city_name FROM city ....
虽然有效SQL为什么两次得到相同的值(修辞)?此外,Room 应该使用哪个(即使它们相同)city_name(rehtorical)? 这是模棱两可的,有可能导致错误(我认为 room 会处理这个并选择最后一个)
你想要:-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * FROM city WHERE city_name LIKE :cityName")
List<Entity> getCity(String cityName); //<<<< The method that is called from the code
}
这将 return 一个列表,即实体对象的列表
或者:-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT city_name FROM city WHERE city_name LIKE :cityName")
List<String> getCityName(cityName);
}
- 这是一个 return 列表,其中每个字符串都是 city_name 的
- 你可以两者兼得
调用时,如果传递的字符串 (cityName) 是“New%”,那么您将得到 New York、Newcastle 等(即 % 是 1 个或多个字符的通配符 _ 是单个字符)
例子
使用你的代码,稍作改动,上面的代码编译成功 运行s OK。
上面建议的更改已经包含(两者)所以使用的 DAO class 是 :-
@Dao
public interface DAO {
//this class contains methods that accesses the database
@Query("SELECT * FROM city WHERE city_name LIKE :cityName")
List<Entity> getCitiesByName(String cityName);
@Query("SELECT city_name FROM city WHERE city_name LIKE :cityName")
List<String> getCityNamesByName(String cityName);
}
我对 数据库 class 进行了一些更改,以 a) 允许在主线程上 运行ning(添加 .allowMainThreadQueries()
) b) 不根据 :-
.createFromAsset("city_db.db")
)
@androidx.room.Database(entities = Entity.class,version = 1)
//must include the entity associated with database
public abstract class Database extends RoomDatabase {
//must be abstract
public abstract DAO dao();
//must declare this
//check if a database instance is existing
//if not, create one and return it
private static volatile Database db;
static Database getDatabase(final Context context){
if(db==null){
synchronized (Database.class){
if(db == null){
db = Room.databaseBuilder(context.getApplicationContext(),
Database.class, "Database")
//.createFromAsset("city_db.db")
.allowMainThreadQueries()
.build();
}
}
}
return db;
}
}
使用的 MainActivity 是 :-
public class MainActivity extends AppCompatActivity {
Database db;
DAO dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Database.getDatabase(this);
dao = db.dao();
dao.getCitiesByName("Somewhere%");
dao.getCityNamesByName("Nowhere%");
}
}
因此,即使没有对结果进行任何处理(数据库中没有数据),上面的操作也会打开数据库(不使用 .createFromAsset 创建它)。
您可以在 Android Studio 中根据 App Inspection(又名数据库检查器)查看此内容:-
像你这样编码,除非资产文件的副本在按照建议修改时有效。
正在进行的问题
根据您的屏幕截图,您可能会在 .build()
.
房间期望复制的数据库与从实体派生的模式完全匹配(对于在@Database 注释的实体参数中定义的实体)。
作为一个示例,屏幕截图显示城市名称位于名为 name 的列(字段)中,而在您的实体中则是等效的列名称,按照实体实体是 city_name。房间构建将失败。同样,coord.lon 和 coord.lat 是经度和纬度。
此外,列定义(列类型和约束,例如 NOT NULL)也必须匹配,否则 Room 构建将失败。
因此,您必须确保模式匹配。这可能需要使用一种可用的工具来相应地转换数据库(由于 Room 施加的限制,转换正在复制的数据库可能比尝试匹配现有数据库容易得多)。
起点是当您使用包含实体的 valid/usable @Database 编译时 Room 生成的 SQL。
例如在您的情况下,在 class 中生成的 java(从 Android Studio 中的 Android 视图可见)与具有 @ 的 class 命名相同数据库注释,但后缀为 _IMPL。
因此,由于您的@Database class 被命名为数据库,因此将会有一个 class Database_IMPL.
在 class 中会有一个方法 createAllTables 它有 SQL 用于所有 table(任何 SQL 可以忽略与 room_ 的交易)。
例如你的会是这样的:-
您很可能需要使用 SQLite 工具转换数据库,例如 DB Browser for SQLite、SQlite Studio、DBeaver、Navicat For SQlite 以使用模式。
粗略地说,使用从生成的java中获得的SQL创建table,然后复制数据。
转换本身会非常简单。首先创建 city_db.db 文件的副本。
打开其中一个(根据您的喜好,在您喜欢的 SQLite 工具中进行演示
我使用 Navicat 并使用查询生成原始示例(只有几个国家))按照 :-
CREATE TABLE IF NOT EXISTS city (id INTEGER PRIMARY KEY, name TEXT,state TEXT, country TEXT, `coord.lon` REAL, `coord.lat`); INSERT INTO city VALUES (3904809,'Santa Rita','','BO',-63.3499,-17.966), (3904890,'Santa Elenta','','BO',-64.7833,-20.5496) ; - don't use the above
然后使用下面的 SQL 注意这将删除原来的 table (不想要超大的资产文件)。 如果 运行 第二次,这将失败,因为原始列名已被有效覆盖,因此原始列名不存在。
DROP TABLE IF EXISTS room_city;
/*As copied from the App's generated java */
/* BUT table name changed to room_city from city */
CREATE TABLE IF NOT EXISTS `room_city` (`id` INTEGER NOT NULL, `city_name` TEXT, `state` TEXT, `country` TEXT, `lon` TEXT, `lat` TEXT, PRIMARY KEY(`id`));
INSERT INTO room_city SELECT id,name AS city_name, state, country, `coord.lon` AS lon, `coord.lat` AS lat FROM city;
/* To clean up so as to not include the original in the asset
you don't want the asset hold twice the data that it needs to
!!!!!NOTE!!!!! you should use a copy of the original city database
*/
DROP TABLE IF EXISTS city;
ALTER TABLE `room_city` RENAME TO `city`;
VACUUM;
/* Below not needed but good to check all is as expected */
SELECT * FROM city;
SELECT * FROm sqlite_master;
请注意,这假设原始数据库与屏幕截图一致。
VACUUM 之后的第一个 SELECT 显示:-
第二个显示架构:-
- 它与 Room 期望的架构相匹配
应关闭数据库和连接(打开检查并再次关闭以仔细检查要复制的文件是否已保存)。
然后将文件复制到 assets 文件夹(您可能需要创建它)确保它被命名(可以重命名)city_db.db . :-
在使用上述演示的情况下,数据库 class 已更改为现在包含行 .createFromAsset("city_db.db") // <<<<< reinstated
和 MainActivity 已更改为:-
public class MainActivity extends AppCompatActivity {
Database db;
DAO dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Database.getDatabase(this);
dao = db.dao();
for(Entity city: dao.getCitiesByName("%")) { //* <<<<<< changed to show all as only 2 cities in demo
Log.d("CITYINFO-1","CityName is " + city.name + " state/county is " + city.state + " etc.....");
}
for(String cityname: dao.getCityNamesByName("%")) { //* <<<<<< changed to show all as only 2 cities in demo
Log.d("CITYINFO-2","CityName is " + cityname);
}
dao.getCityNamesByName("New%");
}
}
当 运行 日志显示时:-
2021-10-23 15:44:37.480 D/CITYINFO-1: CityName is Santa Rita state/county is etc.....
2021-10-23 15:44:37.480 D/CITYINFO-1: CityName is Santa Elenta state/county is etc.....
2021-10-23 15:44:37.482 D/CITYINFO-2: CityName is Santa Rita
2021-10-23 15:44:37.482 D/CITYINFO-2: CityName is Santa Elenta
所以数据库可以正常工作。
如果不转换会怎样?
再次根据屏幕截图,应用程序失败并显示:-
2021-10-23 15:50:53.826 6345-6345/a.a.so69683821cities E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so69683821cities, PID: 6345
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so69683821cities/a.a.so69683821cities.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: city(a.a.so69683821cities.Entity).
Expected:
TableInfo{name='city', columns={country=Column{name='country', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, city_name=Column{name='city_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lon=Column{name='lon', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, state=Column{name='state', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lat=Column{name='lat', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='city', columns={country=Column{name='country', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, state=Column{name='state', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, coord.lat=Column{name='coord.lat', type='', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}, coord.lon=Column{name='coord.lon', type='REAL', affinity='4', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
其中 EXPECTED 是 Room 根据实体期望的架构 class 但是 Room FOUND 不匹配来自资产的架构。