问题
I have been analyzing the way of reading and writing a ROOM database SYNCHRONICALLY without the need of a Content Provider or BroadcastReceiver. Obviously the database access must be asynchronous. I have found a solution that makes the asynchronous task to be transparent to the programmer, using
Callable
and
Future
interfaces that allows me to make direct calls to the database within the main thread, as well as from any other part of the application.
The advantages is having almost direct and synchronous access to the query’s defined in the room DAO interface, which implies a huge advantage over traditional methods; I call it "CALLABLE CLASS FOR ROOM".
My question is what disadvantages do you see for simple queries?
Simple example: Suppose we define a room database with table named "users" and that it contains the 3 columns: "id", "LastName" and "Name", where you what to insert, update and read your database SYNCHRONICALLY:
Room ENTITY class (DB_Entity):
@Entity(tableName = "users")
public class DB_Entity {
@PrimaryKey (autoGenerate = true)
@ColumnInfo(name = "id")
private int id;
@ColumnInfo(name = "LastName")
private String lastName;
@ColumnInfo(name = "Name")
private String name;
// getters and setters
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getLastName() {return lastName;}
public void setLastName(String lastName) {this.lastName = lastName;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
DAO interface called (DB_Dao) with to simple queries: update and read user:
@Dao
public interface DB_Dao {
@Insert
void insert(DB_Entity data);
@Update
void update(DB_Entity data);
@Query("SELECT * FROM users ORDER BY id ASC")
List<DB_Entity> getAll();
@Query("SELECT * FROM users WHERE id IS :id")
DB_Entity getUserById(int id);
@Query("SELECT COUNT(id) FROM users")
int getCount();
}
The Client class (DB_Client):
public class DB_Client {
private static DB_Client instance;
private final DB myDB;
private DB_Client(Context context) {
myDB = Room.databaseBuilder(context, DB.class, "users").build();
}
public static synchronized DB_Client getInstance(Context context) {
if (instance == null) {
instance = new DB_Client(context);
} else {
if (!instance.myDB.isOpen()) {
instance = new DB_Client(context);
}
}
return instance;
}
and finally.. the Room database:
@Database(entities = {DB_Entity.class}, version = 1, exportSchema = false)
public abstract class DB extends RoomDatabase {
public abstract DB_Dao myDao();
}
Now create what I call the "CALLABLE CLASS FOR ROOM" that have all the static methods you like, that will be called from any part of your application. Let’s call the class as DB_Utilitys.class:
public class DB_Utility {
/**
* Getter example
* Read user
*/
public static DB_Entity getDBUser(final Context context, final int id){
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(1);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, workQueue);
// Initialize the class returned variable
DB_Entity userData = null;
// Define what type of variable the interface will return
Future<DB_Entity> future;
// Execute the Callable async task using the same variable to be returned
future = threadPoolExecutor.submit(new Callable<DB_Entity>() {
@Override
public DB_Entity call() {
// TODO: here you add whatever you want to do with the database
// In this example, the getUserById query
DB_Dao db = DB_Client.getInstance(context).getDB().myDao();
return db.getUserById(id);
}
});
/*
The future interface returns the result to the previously initialized variable "userData",
has soon the room database is fetched in the CALL(). If the query takes more than the
defined here timeout value (500ms), TimeoutException will be called.
(You can change the value of the time out (500ms) depending of your requirements)
*/
try {
userData = future.get(500,TimeUnit.MILLISECONDS);
}
catch (ExecutionException e) {e.printStackTrace();}
catch (InterruptedException e) {e.printStackTrace();}
catch (TimeoutException e) {e.printStackTrace();}
// Clear to avoid out of memory
future.cancel(true);
return userData;
}
/**
* Setter example
* Change row data
*/
public static void updateDBRow(final Context context, final DB_Entity userData){
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(1);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, workQueue);
// In this case we don't need a return value, so use Void for the Future and Callable as
// a returning object
Future<Void> future;
future = threadPoolExecutor.submit(new Callable<Void>() {
@Override
public Void call() {
// TODO: here you add whatever you want to do with the database
// In this example, the update query
DB_Dao db = DB_Client.getInstance(context).getDB().myDao();
db.update(userData);
// Since no return value is requiered, return null.
return null;
}
});
try {
future.get(500,TimeUnit.MILLISECONDS);
}
catch (ExecutionException e) {e.printStackTrace();}
catch (InterruptedException e) {e.printStackTrace();}
catch (TimeoutException e) {e.printStackTrace();}
// Clear to avoid out of memory
future.cancel(true);
}
}
How to use it:
private void example(Context context) {
// If you what to read user id 1 information call:
DB_Entity userInfo = DB_Utility.getDBUser(context, 1);
Log.i("mytag", " User last name: " + userInfo.getLastName());
Log.i("mytag", " User name: " + userInfo.getName());
// If you want to change the name of the same user id 1 use:
userInfo.setName("my_new_name");
DB_Utility.updateDBRow(context, userInfo);
}
来源:https://stackoverflow.com/questions/58532832/read-and-write-room-database-synchronically-everywhere