Issue
I am trying to query some data saved in room local database using the id. I have received the id from a serialized object and passed it to my view model class which calls the repository to perform the query using the dao.
An asynctask is used to make things async and return the requested value from db. I have made an interface and initialized it in the async task so I can return the result from db but it fails with NPE.
In activity, I have called repository and passed the serialized data like:
public class ViewSessionActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = ViewSessionActivity.class.getSimpleName();
private Toolbar toolbar;
private Sessions sessions; // serialized object from adapter
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_session);
init();
setSupportActionBar(toolbar);
}
private void init() {
// skipped fvid calls
sessions = (Sessions) getIntent().getSerializableExtra(Constants.SESSION_DETAIL_KEY);
Log.d(TAG, "Prog id:\t" + sessions.prog_sessionId);
//error occurs here which leads to async task call in repository
SessionsRepository.getRepository(getApplication()).getSessionByID(sessions.prog_sessionId);
}
and in repository:
public class SessionsRepository {
private static final String TAG = SessionsRepository.class.getSimpleName();
private SessionsDAO dao;
private static SessionsRepository repository = null;
private LiveData<List<Sessions>> allSessions;
private MutableLiveData<Boolean> loadingState;
private Context context;
private final SportsDatabase database;
public SessionsRepository(Application application) {
database = SportsDatabase.getInstance(application);
dao = database.sessionsDAO();
allSessions = dao.getAllSessions();
loadingState = new MutableLiveData<>();
context = application.getApplicationContext();
}
public static SessionsRepository getRepository(Application context){
if (repository == null){
repository = new SessionsRepository(context);
}
return repository;
}
public void fetchSessions() {
String coachId = new PrefsUtils(context).getCoachId();
Call<SessionDetails> call = RestClient.getRestInstance().getSessionsService().fetchSessions(coachId);
call.enqueue(new Callback<SessionDetails>() {
@Override
public void onResponse(Call<SessionDetails> call, Response<SessionDetails> response) {
if (response.isSuccessful()) {
loadingState.postValue(false); // remove progress
SessionDetails details = response.body();
List<Sessions> sessions = details.getSessions();
// Log.d(TAG, "N/w sesh size:\t" + sessions.size());
saveSessions(sessions);
}
}
@Override
public void onFailure(Call<SessionDetails> call, Throwable t) {
loadingState.postValue(false);
Toast.makeText(context, "Error Fetching Sessions", Toast.LENGTH_SHORT).show();
}
});
}
private void saveSessions(List<Sessions> sessions) {
new SaveSessionsTask(dao).execute(sessions);
}
// this method which calls the async task
public void getSessionByID(String id){
new FindSessionTask(dao, context).execute(id);
}
public LiveData<List<Sessions>> getSavedSessions() {
return allSessions;
}
public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void> {
private SessionsDAO dao;
public SaveSessionsTask(SessionsDAO dao) {
this.dao = dao;
}
@Override
protected Void doInBackground(List<Sessions>... lists) {
dao.addSessions(lists[0]);
return null;
}
}
public class FindSessionTask extends AsyncTask<String, Void, Sessions>{
private SessionsDAO dao;
private OnSessionResultCallback callback;
public FindSessionTask(SessionsDAO dao, Context context) {
this.dao = dao;
this.callback = (OnSessionResultCallback) context;
}
@Override
protected Sessions doInBackground(String... strings) {
Sessions sessions = dao.getSessionById(strings[0]);
return sessions;
}
@Override
protected void onPostExecute(Sessions sessions) {
super.onPostExecute(sessions);
Log.d(SessionsRepository.TAG, "Session name:\t" + sessions.session_name);
}
}
// need help in returning the session found from db in onpostexecute method
}
dao class:
@Dao
public interface SessionsDAO {
@Insert
void addSessions(List<Sessions> sessions);
@Query("select * from sessions")
LiveData<List<Sessions>> getAllSessions();
@Query("select * from sessions where prog_sessionId = :id")
Sessions getSessionById(String id); // here
}
and interface callback to receive data in async task on post execute method:
public interface OnSessionResultCallback {
void onSessionFound(Sessions sessions);
}
Error in logcat:
Caused by: java.lang.ClassCastException: sashi.in.ecosports.extras.App cannot be cast to sashi.in.ecosports.interfaces.OnSessionResultCallback
at sashi.in.ecosports.rest.db.repositories.SessionsRepository$FindSessionTask.<init>(SessionsRepository.java:110)
at sashi.in.ecosports.rest.db.repositories.SessionsRepository.getSessionByID(SessionsRepository.java:82)
at sashi.in.ecosports.ui.activities.ViewSessionActivity.init(ViewSessionActivity.java:63)
at sashi.in.ecosports.ui.activities.ViewSessionActivity.onCreate(ViewSessionActivity.java:38)
at android.app.Activity.performCreate(Activity.java:6671)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
....
I was expecting to log out the found session name but instead got NPE. Can someone help me out in executing a room query async, using an entity field as search string to return the entire entity from a list. How does the view model receive the data and is my interface necessary? Thanks.
Solution
Since you're getting NPE when you're trying to print session.session_name
this means your returned session object was null which can happen:
with a bad query (since your select query is fine I'm guessing you're passing a wrong id)
when you haven't inserted any record with that id yet (in that case you have to debug your insertion process and check the database to see if insertion occurred right)
To check your database content on the runtime you can easily use Stetho.
Also about the update query with room you can use the built-in @Update
annotation which finds the passed object by its primary key and updates all columns or use @Query
annotation to update a specific column(s) like @Query("UPDATE sessions SET session_name = :newName WHERE prog_sessionId = :id")
which updates session_name
column with session id of id
. Below is a sample code:
@Dao
public interface SessionsDAO {
@Query("SELECT * FROM sessions") // Retrieve all columns saved in sessions table
LiveData<List<Sessions>> getAllSavedSessions();
@Query("SELECT * FROM sessions WHERE prog_sessionId = :id") // Retrieve a single session with all columns saved in sessions table
Sessions findSessionWithId(String id);
@Query("SELECT session_name FROM sessions WHERE prog_sessionId = :id") // Retrieve a single session name saved in sessions table
String getSessionNameWithId(String id);
@Update // Update all non-primary columns of a session
void updateSession(Sessions session);
@Query("UPDATE sessions SET session_name = :newName") // Update session_name column of all saved sessions
void updateAllSessionNamesTo(String newName);
@Query("UPDATE sessions SET session_name = :newName WHERE prog_sessionId = :id") // Update session_name column of a session located with its id
void updateSessionNameTo(String newName, String id);
@Query("UPDATE sessions SET session_name = :newName AND session_date = :newDate WHERE prog_sessionId = :id") // Update session_name and session_date column of a session located with its id
void updateSessionNameAndDateTo(String newName, long newDate, String id);
}
In case you need to run queries on a worker thread you can use java executers, here is a simple example for retrieving session name:
@Dao
public abstract class SessionsDAO {
void getSessionNameWithId(final String id, final NameLoadedListener onFinishListener) {
Executors.newSingleThreadExecuter().submit(new Runnable() {
@Override
public void run() {
String name = getSessionNameWithId(id);
onFinishListener.onNameLoaded(name);
}
});
}
@Query("SELECT session_name FROM sessions WHERE prog_sessionId = :id") // Retrieve a single session name saved in sessions table
abstract String getSessionNameWithId(String id);
public interface NameLoadedListener {
void onNameLoaded(String name);
}
}
And here is an example of updating session name by retrieving the entity and returning the updated one at the end.
@Dao
public abstract class SessionsDAO {
void updateSessionNameWithId(final String id, final String newName, final SessionUpdateListener onFinishListener) {
Executors.newSingleThreadExecuter().submit(new Runnable() {
@Override
public void run() {
updateSessionName(id, newName, onFinishListener);
}
});
}
@Transaction
void updateSessionName(String id, String newName, SessionUpdateListener onFinishListener) {
Sessions session = findSessionWithId(id);
session.setName(newName);
updateSession(session);
onFinishListener.onSessionUpdated(session);
}
@Query("SELECT * FROM sessions WHERE prog_sessionId = :id") // Retrieve a single session with all columns saved in sessions table
Sessions findSessionWithId(String id);
@Update // Update all non-primary columns of a session
void updateSession(Sessions session);
public interface SessionUpdateListener {
void onSessionUpdated(Sessions session);
}
}
Hope you get the memo.
Answered By - Sdghasemi
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.