All I have to do is get the return value from the insert as a long. I got that but it isn't working, I am getting back 0 from the returned value. I am using a DAO, Repository and ViewModel as stated in the Google CodeLabs. I have followed this post Rowid after Insert in Room.
Player Class
#Entity(tableName = "player_table")
public class Player {
#PrimaryKey(autoGenerate = true)
private long id;
#NonNull
#ColumnInfo(name = "username")
private String username;
}
DAO
#Insert
long insert(Player player);
Repository
public long insert(Player player) {
new insertAsyncTask(mPlayerDao).execute(player);
rowId = player.getId();
return rowId;
}
ViewModel
public long insert(Player player){
rowId = mRepository.insert(player);
return rowId;
}
Activity
String playerString = editTextUsername.getText().toString();
Player player = new Player(playerString);
long rowId = mDreamViewModel.insert(player);
The problem is that you return player.getId() before the AsyncTask finishes its background work of insertion; you must wait until it delivers you the correct result of insertion, and here is a suggested solution using the thread-safe CountDownLatch which pauses the the execution of the subsequent code using .await() method until count of the CountDownLatch reaches 0; where it decrements by 1 each time .countDown() is invoked.
public class Repository {
private long rowId = -1;
private CountDownLatch mLatch;
public long insert(Player player) {
mLatch = new CountDownLatch(1);
new insertAsyncTask(mPlayerDao).execute(player);
try {
mLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("LOG_TAG", String.valueOf(rowId));
return rowId;
}
class insertAsyncTask extends AsyncTask<Player, Void, Void> {
#Override
protected Void doInBackground(Player... players) {
rowId = mDatabase.getContactDAO().addContact(players[0]);
mLatch.countDown();
return null;
}
}
}
The insert method in your Repository class can be changed to this:
public long insert(Player player) {
return new insertAsyncTask(mPlayerDao).execute(player).get();
}
I found this by searching GitHub for AsyncTasks with "Void, Long" as the last two types, along with some other Room-specific terms.
When debugging my own application I saw that execution of the return statement in the insert method was occurring before the doInBackground method was executed, which seemed obvious after I saw it happen.
Related
I created a room database following this guide from code labs It makes use of a repository to:
A Repository manages query threads and allows you to use multiple backends. In the most common example, the Repository implements the logic for deciding whether to fetch data from a network or use results cached in a local database.
I followed the guide and i'm now able to create the entity's & retrieve the data. I even went further and created another whole entity outside the scope of the guide.
However I can't find many resources that use this MVVM(?) style so am struggling as to really under stand the repository. For now I want to update a field. Just one, as if I am able to manage that the rest should be similar.
I want to update a field called dartshit and I have the dao method created for this:
#Query("UPDATE AtcUserStats SET dartsHit = :amount WHERE userName = :userName")
void UpdateHitAmount(int amount, String userName);
I have one repository which I assumed I use for all entities:
public class UsersRepository {
private UsersDao mUsersDao;
private AtcDao mAtcDao;
private LiveData<List<Users>> mAllUsers;
private LiveData<List<AtcUserStats>> mAllAtc;
private AtcUserStats mAtcUser;
UsersRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mUsersDao = db.usersDao();
mAtcDao = db.atcDao();
mAllUsers = mUsersDao.fetchAllUsers();
mAllAtc = mAtcDao.getAllAtcStats();
}
LiveData<List<Users>> getAllUsers() {
return mAllUsers;
}
LiveData<List<AtcUserStats>> getAllAtcStats() {
return mAllAtc;
}
LiveData<AtcUserStats> getAtcUser(String username) {
return mAtcDao.findByName(username);
}
public void insert (Users user) {
new insertAsyncTask(mUsersDao).execute(user);
}
public void insertAtc (AtcUserStats atc) {
new insertAsyncAtcTask(mAtcDao).execute(atc);
}
private static class insertAsyncTask extends AsyncTask<Users, Void, Void> {
private UsersDao mAsyncTaskDao;
insertAsyncTask(UsersDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Users... params) {
mAsyncTaskDao.insertNewUser(params[0]);
return null;
}
}
private static class insertAsyncAtcTask extends AsyncTask<AtcUserStats, Void, Void> {
private AtcDao mAsyncTaskDao;
insertAsyncAtcTask(AtcDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final AtcUserStats... params) {
mAsyncTaskDao.insertNewAtcUser(params[0]);
return null;
}
}
}
My question is how do I create a AsyncTask for the update query I am trying to run in this repository?
Here is what I have so far by broadly copying the insert repository methods:
private class updateHitAsyncTask {
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
protected Void doInBackground(int amount, String name) {
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
Which is incorrect is that I'm getting a llegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. error. But i thought this AsyncTask is suppose to take care of this?
Here is my update method in my view model, which is reporting 0 errors:
void updateHitAmount (int amount, String name) {
mRepository.updateAtcHits(amount, name);
}
and here is the UI code where im actually trying to tie all these together, I suspect there must be a better way that using onChanged for simply updating a field but again I am struggling to come across any advice on google with the repository approach:
private void callOnChanged() {
mAtcViewModel = ViewModelProviders.of(this).get(AtcViewModel.class);
mAtcViewModel.getAllUsers().observe(this, new Observer<List<AtcUserStats>>() {
#Override
public void onChanged(#Nullable final List<AtcUserStats> atc) {
// Update the cached copy of the users in the adapter.
for (int i = 0; i < atc.size(); i++) {
if (atc.get(i).getUserName().equals(mUser)) {
mAtcViewModel.updateHitAmount(55, mUser);
//atc.get(i).setDartsHit(55);
Log.d("id", String.valueOf(userSelected.getId()));
}
}
}
});
How can I update fields using this approach on the background thread?
Figured it out due to this answer here. It was mostly because of my lack of understanding of AsyncTask. Essentially I needed to create an object and pass the data that way and then execute in the background:
private static class MyTaskParams {
int amount;
String name;
MyTaskParams(int amount, String name) {
this.amount = amount;
this.name = name;
}
}
public void updateAtcHits (int amount, String name) {
MyTaskParams params = new MyTaskParams(amount,name);
new updateHitAsyncTask(mAtcDao).execute(params);
}
private class updateHitAsyncTask extends AsyncTask<MyTaskParams,Void,Void>{
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
#Override
protected Void doInBackground(MyTaskParams... myTaskParams) {
int amount =myTaskParams[0].amount;
String name = myTaskParams[0].name;
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
I've been using the codelabs for my Room Database Persistence. Now I am trying to get the latest rowID after inserting data into my room database. However, I am stuck in my repository trying to return the rowID from my AsyncTask.
LogEntity.java
#Entity
public class LogEntity {
#PrimaryKey(autoGenerate = true)
private int id;
LogDao.java
public interface LogDao {
#Insert
long insert(LogEntity logEntity);
LogDatabase.java
#Database(entities = LogEntity.class, version = 1)
public abstract class LogDatabase extends RoomDatabase {
private static LogDatabase instance;
public abstract LogDao logDao();
public static synchronized LogDatabase getInstance(Context context){
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(),
LogDatabase.class, "log_database").
fallbackToDestructiveMigration().build();
}
return instance;
}
}
LogRepository.java
public long insertLogs(LogEntity logEntity) {
new InsertLogAsyncTask(logDao).execute(logEntity);
return **
}
private static class InsertLogAsyncTask extends AsyncTask<LogEntity, Void, Long>{
private LogDao logDao;
private InsertLogAsyncTask(LogDao logDao){
this.logDao = logDao;
}
#Override
protected Long doInBackground(LogEntity... logEntities) {
logDao.insert(logEntities[0]);
return logDao.insert(logEntities[0]);
}
}
I put two asterisks because I'm not sure what to do here in order to get the insert RowID and whether my AsyncTask is completely correct.
LogViewModel.java
public long insertLog(LogEntity logEntity){
return repository.insertLogs(logEntity);
}
MainActivity.java
long id = logViewModel.insertLog(logEntity);
I want to be able to use this final id variable for future use.
You are on the right track but not quite there.
You should declare the AsyncTask class as an inner class of the ViewModel and not the DB.
In the ViewModel add an ID variable and in the AsyncTask add the onPostExecute override to handle the execution result.
LogViewModel.java
long mLastInsertedID;
private static class InsertLogAsyncTask extends AsyncTask<LogEntity, Void, Long>{
private LogDao logDao;
private InsertLogAsyncTask(LogDao logDao){
this.logDao = logDao;
}
#Override
protected Long doInBackground(LogEntity... logEntities) {
//you are now off the UI thread
logDao.insert(logEntities[0]);
return logDao.insert(logEntities[0]);
}
#Override
protected void onPostExecute(Long result) {
//Do whatever you like with the result as you are back on the UI thread
mLastInsertedID = result;
}
}
I use LiveData + Transformations.map() :
private final LiveData<List<Task>> observableTasks;
(...)
observableTasks = Transformations.map(tasksRepository.getTasks(), tasks-> filterTasks(tasks));
How to force LiveData to refresh? I need Transformations.map() to prepare new list. When the user changes filtering options I need to call filterTasks(tasks) again and show new, sorted list. Data coming from the repository (tasksRepository.getTasks()) stays the same.
I think I found the solution. I created additional LiveData field filterChangeEvent. Every time the user changes filtering order, new value is set to filterChangeEvent. Then I use switchMap with filterChangeEvent as a trigger:
observableTasks = Transformations.switchMap(filterChangeEvent, input -> {
final MediatorLiveData<List<Tasks>> result = new MediatorLiveData<>();
result.addSource(tasksRepository.getTasks(), tasks -> result.setValue(filterTasks(tasks)));
return result;
});
Based on the information you have provided, I did following steps, and my observable got triggered as expected. I think you're doing something wrong either in repository or in class where you handle the transformation. As there's not enough code to check, I created my own dummy classes:
I created dummy Task POJO:
public class Task {
private String id;
public Task(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#NonNull
#Override
public String toString() {
return "Task id is " +id;
}
}
I created dummy repository which has LiveData:
public class TasksRepository {
private List<Task> taskList = new ArrayList<>();
private MutableLiveData<List<Task>> _tasks = new MutableLiveData<>();
private LiveData<List<Task>> tasks = _tasks;
public TasksRepository() {
for (int i = 0; i < 5; i++) {
taskList.add(new Task(createTaskId()));
}
_tasks.setValue(taskList);
}
private String createTaskId() {
return UUID.randomUUID().toString();
}
public void addTask() {
taskList.add(new Task(createTaskId()));
_tasks.setValue(taskList);
}
public LiveData<List<Task>> getTasks() {
return tasks;
}
}
Created class called MyViewModel which can handle transformation. During transformation we just add "TEST" prefix to task id. You can compare it with your code and it should be fine:
public class MyViewModel {
private final LiveData<List<Task>> observableTasks;
public MyViewModel(TasksRepository tasksRepository) {
this.observableTasks = Transformations.map(tasksRepository.getTasks(), this::changeId);
}
private List<Task> changeId(List<Task> tasks) {
List<Task> resultTaks = new ArrayList<>();
for (Task task : tasks) {
String newId = "TASK" + task.getId();
task.setId(newId);
resultTaks.add(task);
}
return resultTaks;
}
public LiveData<List<Task>> getObservableTasks() {
return observableTasks;
}
}
Add data on when button clicked and observe data changes:
TasksRepository tasksRepository = new TasksRepository();
MyViewModel viewModel = new MyViewModel(tasksRepository);
button.setOnClickListener(v -> tasksRepository.addTask());
viewModel.getObservableTasks().observe(this,
tasks -> Log.d(TAG, Arrays.toString(new List[]{tasks})));
Checkout Transformation.switchMap. It explains following case when to use it also.)
Scenario:
In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the userIdLiveData value is set to "1", the switchMap will call getUser(1), that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"), the userLiveData gets automatically notified and will emit User(1, "Sarah").
When the setUserId method is called with userId = "2", the value of the userIdLiveData changes and automatically triggers a request for getting the user with id "2" from the repository. So, the userLiveData emits User(2, "John"). The LiveData returned by repository.getUserById(1) is removed as a source.
Hi I am using Executors for loading some data in parallel. My application is fetching some data from DB which have parent-child relation like:
parent 1 -> [child11, child12,..., child1N]
parent 2 -> [child21, childy22,..., child2N]
.....
parent N -> [childN1, childyN2,..., childNN]
Now here I want parallel processing. What I am doing now is loading all data
of a parent child set at a time from DB and calling executor service to map those in relationship and store in my data structure.
Now the code I have for this:
The Parent Child relation is like:
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
private Integer postId;
private String postText;
private String postType;
private Integer menuItemId;
private boolean parentPost;
private Integer parentPostId;
// Contains all the Child of this Post
private List<Post> answers = new ArrayList<Post>();
....
//getters and setters
}
Now I have a wrapper for this Post class for synchronization
public class PostList {
private List<Post> postList;
public PostList() {
super();
this.postList = new ArrayList<Post>();
}
public List<Post> getPostList() {
return postList;
}
public synchronized boolean add(Post post) {
return postList.add(post);
}
public synchronized boolean addAnswer(Post answer) {
for(Post post : postList)
{
if(post.getPostId() == answer.getParentPostId())
{
post.getAnswers().add(answer);
break;
}
}
return true;
}
}
Now My Loading code from DB is:
/* This is called to load each parent-child set at a time, when the
first set is fetched from DB then call to executor to store those in
internal data structure. */
List<Post> posts = null;
PostList postList = null;
Integer args[] ={menuItemId};
// Fetch all Posts which are in parent child relation
posts = getDataFromDB(...)
if(posts != null && posts.size() >0)
{
postList = new PostList();
ExecutorService executor = Executors.newFixedThreadPool(10);
for(Post post : posts)
{
executor.execute(new PostProcessor(post, postList));
}
logger.debug("Starting executor shutdown...");
executor.shutdown();
while (!executor.isTerminated()) {
try {
executor.awaitTermination(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
logger.error("Interrupted executor >>", ex.getMessage(), ex);
}
}
logger.debug("All post loading done ...");
logger.debug("PostList >> " + postList);
if(postList.getPostList() != null)
return postList.getPostList();
}
And in PostProcessor I have
public class PostProcessor implements Runnable {
private Post post;
private PostList postList;
public PostProcessor(Post post, PostList postList) {
super();
this.post = post;
this.postList = postList;
}
#Override
public void run() {
// TODO Auto-generated method stub
Post answer = null;
try
{
// if Post is parent / is a question
if ("Q".equalsIgnoreCase(post.getPostType()))
{
// do some operation
postList.add(post);
}
// Post is an Answer, so add the answer to proper Question
else {
answer = post;
postList.addAnswer(answer);
}
Thread.sleep(1000);
}
catch(Throwable throwable)
{
logger.error(throwable.getMessage(),throwable);
}
}
}
But it behaving abnormally, some time its loading all question post but not all answers and some times its not loading a parent post at all. Please help where I am doing wrong.
If addAnswer fails then it should return false or throw an exception; this indicates that the appropriate question has not yet been loaded or doesn't exist. Two options:
Process all questions first, and throw an exception if an answer doesn't match a question.
When you query the database, get a count of questions and decrement this every time a question is processed (do the decrement after you add the question to the post list, otherwise you might wind up with a question_count == 0 but without a question having been added to the list yet); if an answer fails to match up to a question and question_count > 0 then put the answer back on the queue, else throw an exception.
More as a matter of efficiency than correctness, I suggest that you eliminate the synchronized methods and use thread-safe data structures from java.util.concurrent instead - this will reduce lock contention. This would look something like
public class PostList {
private AtomicInteger questionCount;
private ConcurrentLinkedQueue<Post> questions;
private ConcurrentHashMap<String, ConcurrentLinkedQueue<Post>> answers;
public boolean addQuestion(Post post) {
questions.offer(post);
if(answers.putIfAbsent(post.getPostId(), new ConcurrentLinkedQueue<>())
!= null) {
questionCount.decrementAndGet();
return true;
} else throw new IllegalArgumentException("duplicate question id");
}
public boolean addAnswer(Post answer) {
ConcurrentLinkedQueue<Post> queue = answers.get(answer.getParentPostId());
if(queue != null) {
queue.offer(answer);
return true;
} else if(questionCount.get() > 0) {
return false;
} else {
throw new IllegalArgumentException("answer has no question");
}
}
}
I have conceptual problem understanding how to compose between streams\Observables which have different return type.
Here is a draft method I'm trying to code:
public void findSeat() {
rx.Observable<GameObject> userObs = context.getUser();
rx.Observable<ActiveGame> gameObs = context.findGame();
rx.Observable.zip(userObs, gameObs, (userObj, game) -> {
User user = ...;
final List<Object> results = new ArrayList<Object>(3);
if(userObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-1);
return results;
}
...
...
//***********************************
// THE PROBLEM IS HERE:
// "context.getActiveGameManager().updateGame(game)" returns Observable<GameOBject> and not List<Object> like .zip() expects.
// because of that I cannot do:
// "return context.getActiveGameManager().updateGame(game);"
// How can I do this convertion from Observable<GameObject> to List<Object>
//************************************
context.getActiveGameManager().updateGame(game)
.map((gameObj) -> {
if(gameObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-2);
return (Observable<? extends Object>) results;
}
results.add(ErrorCodes.STATUS_OK);
results.add(user);
results.add(gameObj);
return gameObs;
});
return Observable.empty();
}).subscribe((results) -> {
int status = (int) results.get(0);
User user = (User) results.get(1);
ActiveGame game = (ActiveGame) results.get(2);
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
}
The flow is as follow:
1. emit 2 Observable using .zip method.
2. do some logic on the return value of streams and if it results in error-code --> put it in list and return it so "subscribe" can return the error to user.
3. if no error, emit another "update" method using flatMap() - and this is where I have my problem.
4. eventually, all the results should be processed in "subscribe" because this is the point I acknowledge the user about his request.
Hope it's clear enough...
by the way, I'm trying to learn rxJava, but it's very hard I find there are enough\good sources - can someone recommend to me the best way to learn it?? I trying looking at tutorials at Youtube, Wikipedia, Github...most of them teaches using Scala and other scripting languages - couldn't find anything in Java.
Thank you for everyone that put the effort trying understand it!!
I think you were almost there, but try breaking down the code inside your .zip lambda into smaller Rx operations. For example:
rx.Observable
.zip(userObs, gameObs, (userObj, game) -> {
// Combine the user & game objects and pass them to the
// next Rx operation.
return new UserAndActiveGame(userObj, game);
})
.filter(userAndActiveGame -> {
// Remove this filter if you want errors to make it to the subscriber.
return userAndActiveGame.getUserObj().getStatus() == ErrorCodes.STATUS_OK;
})
.flatMap(userAndActiveGame -> {
// Remove this check if you filter errors above.
if (userAndActiveGame.getUserObj().getStatus() != ErrorCodes.STATUS_OK) {
return Observable.just(new FindSeatResult(-1));
}
return context.getActiveGameManager().updateGame(userAndActiveGame.getGame())
.map(gameObj -> {
if (gameObj.getStatus() != ErrorCodes.STATUS_OK) {
return new FindSeatResult(-2);
}
User user =...; // Whatever you are doing to get this in your example code.
return new FindSeatResult(ErrorCodes.STATUS_OK, user, gameObj);
});
})
The following classes are used for passing intermediate and final results:
private class UserAndActiveGame {
private final GameObject userObj;
private final ActiveGame game;
public UserAndActiveGame(GameObject userObj, ActiveGame game) {
this.userObj = userObj;
this.game = game;
}
public GameObject getUserObj() {
return userObj;
}
public ActiveGame getGame() {
return game;
}
}
private class FindSeatResult {
private final int status;
private final User user;
private final ActiveGame game;
public FindSeatResult(int status) {
this(status, null, null);
}
public FindSeatResult(int status, User user, ActiveGame game) {
this.status = status;
this.user = user;
this.game = game;
}
public User getUser() {
return user;
}
public int getStatus() {
return status;
}
public ActiveGame getGame() {
return game;
}
}
Your subscriber then uses the packaged result similar to what you are already doing.
.subscribe((results) -> {
// You don't need this if you filter errors above.
if (findSeatResult.getStatus() == -1) {
return;
}
int status = findSeatResult.getStatus();
User user = findSeatResult.getUser();
ActiveGame game = findSeatResult.getGame();
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
By using the intermediate and final results classes instead of passing around your results in a List<Object> your code is much more forgiving to changes and the compiler will type check everything for you.