I am trying to write a method that will save the maintenance logs for each bike in my app. The method cycles through each bike, which internally cycles through each maintenance log and saves it.
I can see the correct information is there because of the Log.i placed at each point.
The full code for my method -
public static void saveLogs() {
for (Bike thisBike : bikes) {
Log.i("Saving Logs", "" + thisBike);
try {
ArrayList<String> dates = new ArrayList<>();
ArrayList<String> logs = new ArrayList<>();
ArrayList<String> costs = new ArrayList<>();
for (maintenanceLogDetails thisLog : thisBike.maintenanceLogs) {
Log.i("Date :", thisLog.date);
dates.add(thisLog.date);
Log.i("Log :", thisLog.log);
logs.add(thisLog.log);
Log.i("Cost :", Double.toString(thisLog.price));
costs.add(Double.toString(thisLog.price));
}
sharedPreferences.edit().putString("dates", ObjectSerializer.serialize(dates)).apply();
sharedPreferences.edit().putString("logs", ObjectSerializer.serialize(logs)).apply();
sharedPreferences.edit().putString("costs", ObjectSerializer.serialize(costs)).apply();
} catch (Exception e) {
e.printStackTrace();
Log.i("Adding details", "Failed attempt");
}
}
}
At the top of the class I have -
static SharedPreferences sharedPreferences;
The full error I am getting is -
Attempt to invoke interface method 'android.content.SharedPreferences$Editor android.content.SharedPreferences.edit()' on a null object reference
At the line that is -
sharedPreferences.edit().putString("dates", ObjectSerializer.serialize(dates)).apply();
What confuses me, I've pretty much copied the code from somewhere else that works.
What am I doing wrong?
Many thanks!
static SharedPreferences sharedPreferences;
Did you even instantiate it..??
getContext().getSharedPreferences(String, int)
https://developer.android.com/reference/android/content/SharedPreferences.html
https://developer.android.com/reference/android/content/Context.html#getSharedPreferences(java.lang.String,%20int)
Related
I'm trying to create an app that only adds an entry to the database if there is no entry already at a specific time intervals and modifies the existing entry if there is already one in the database. I'm using Room.
It works, but only with a workaroud, because I have to call the add function twice before the value gets added (make the input two times before it works). And I also don't like my adding the Observer and immediately removing it afterwards. I also had to implement the workaround when instatiating the DB, with a value when it was first created.
How can I get the data from my LiveData List inside the Repository class and change it without ending up in an endless loop or how do I have to redesign my code to avoid that?
The complete code can be found on my Github account: Github repository
I would really appreciate any suggestion fix my problem and learn to design and plan my code better.
MainActivity
public void ok_clicked(View view) {
Intent intent = new Intent(this, DataActivity.class);
...
Diary addDiary = new Diary(new Date(), diaryCh.isChecked(), readingCh.isChecked(),writingCh.isChecked(),pianoCh.isChecked(),youtubeCh.isChecked());
mDiaryViewModel.insert(addDiary);
startActivity(intent);
}
DiaryViewModel
public void insert(Diary diary) {mRepositroy.add(diary);}
DiaryRepository
public class DiaryRepository {
private DiaryDao mDiaryDao;
private LiveData<List<Diary>> mEntriesToday;
DiaryRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mDiaryDao = db.diaryDao();
mEntriesToday = mDiaryDao.findEntriesByDate(Dates.getYesterdayMidnight(), Dates.getTomdayMidnight());
}
LiveData<List<Diary>> getmEntriesToday() { return mEntriesToday;}
void add(Diary diary) {
Observer<List<Diary>> observerEntriesToday = new Observer<List<Diary>>() {
#Override
public void onChanged(List<Diary> diaries) {
if (diaries != null) {
Log.e(TAG, "add: with matching entries"+ diaries.get(0) + " add: " + diary );
diaries.get(0).addAttributes(diary);
new updateDiaryAsyncTask(mDiaryDao).execute(diaries.get(0));
} else {
Log.e(TAG, "add: without matching entries"+" add: " + diary );
new insertDiaryAsyncTask(mDiaryDao).execute(diary);
}
}
};
getmEntriesToday().observeForever(observerEntriesToday);
getmEntriesToday().removeObserver(observerEntriesToday);
}
You shouldn't be using LiveData in this scenario at all. It is only a wrapper for data that will be observed from Activity/Fragment.
First you need to modify mEntriesToday to be MutableLiveData so you can update it.
In your case, you can omit using Observer for updating DB, and so something simple like:
void add(Diary diary){
if (mEntriesToday.getValue() != null) {
Log.e(TAG, "add: with matching entries"+ mEntriesToday.getValue().get(0) + " add: " + diary );
mEntriesToday.getValue().get(0).addAttributes(diary);
new updateDiaryAsyncTask(mDiaryDao).execute(mEntriesToday.getValue().get(0));
} else {
Log.e(TAG, "add: without matching entries"+" add: " + diary );
new insertDiaryAsyncTask(mDiaryDao).execute(diary);
}
}
If you need this data, outside this class, then you can use getmEntriesToday() and observe it.
You can get the value of the LiveData using the getValue() method
void add(Diary diary) {
List<Diary> diaries = mEntriesToday.getValue();
if(diaries!=null){
diaries.get(0).addAttributes(diary);
//update
}else{
//insert
}
I have an activity that shows the results of the previous activity to the user. I save these results to a database. In my onCreate method I create a reference to my ViewModel and populate a List.
mAtcViewModel = new AtcViewModel(getApplication());
atcUserStatsList = mAtcViewModel.getAllUsersList();
I then go on call the save to database method after this assignment:
private void saveToDB(String playerName) {
mUser = playerName;
getAtcUser getAtcUser = new getAtcUser(playerName, this);
getAtcUser.delegate = this;
getAtcUser.execute();
}
When I get the result from onPostExecute this method is called:
#Override
public void processFinish(AtcUserStats atc_user) {
callOnChanged(atc_user);
}
This then calls a method where I am able to see the users previous stored results and adjust them with their new results but in this block of code a problem only sometimes arises, saying that atcUserStatsList is null:
try {
for (int i = 0; i < atcUserStatsList.size(); i++) {
if (atcUserStatsList.get(i).getUserName().equals(mUser)) {
mAtcViewModel.updateAtcPlayerStats(mUser, finalDartsHit, finalGamesWon, finalGamesPlayed, finalSinglesPlayed, finalDoublesPlayed, finalTreblesPlayed, finalDartsThrown,
finalSinglesDartsThrown, finalDoublesDartsThrown, finalTreblesDartsThrown, finalSinglesDartsHit, finalDoublesDartsHit, finalTreblesDartsHit, finalSecondPlace, finalThirdPlace);
}
}
} catch (Execption e) {
e.printStackTrace();
Log.d("atcUserStatsList", "Unable to save to database");
getMessage("Unable to save to database");
}
When I debug the list is populated most of the time and then when I run the app without debugging sometimes it is empty/null and the snackbar message appears and nothing is saved whereas other times it wouldn't appear and results are saved? Why would this be?
Is it anything to do with the lifecycle?
EDIT:
For example here is the list populated while debugging now this code will work:
And tbh for some reason it can be hard to get this list not Null when debugging(?).
Background: I am creating a turnbased multiplayer game. The user is randomly matched with another user. Upon matching, the user is given a list of words, which he will need to use for the reminder of the game. This list of words is randomly chosen from a much larger list.
Problem: I get the index out of bounds exception when I run the app (exception points to line where mCopy is used outside the query.getFirstInBackground code block. When I debugged the app and set breakpoints on mCopy(list of words available to user) , it showed an empty list. It should be noted that the wordsList is appearing in the backend, and if I execute the all the code in the query.getFirstInBackground block, I do not have a problem. However, what I want is that once mCopy retrieves the list from Parse, I can use mCopy anywhere in the class.
Here is the relevant code:
public class PlayGameActivity extends Activity {
protected LinkedList<String> mCopy; //This is a global variable where words are stored
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_game);
mCopy = new LinkedList<String>();
Intent cameFromGIPActivity = getIntent();
mCameFromGIPActivity = cameFromGIPActivity.getStringExtra(ExTRA_GamesInProgress);
// if this is null, that means opp came from FindingOpp Activity
if (mCameFromGIPActivity == null) {
//use the opponent name as a key to get selected words(value)
ParseQuery<ParseObject> query = ParseQuery.getQuery(ParseConstants.CLASS_GAMES_IN_PROGRESS);
query.whereMatches(ParseConstants.KEY_OPPONENT_NAME, mOpponentName);
query.getFirstInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject gameInfo, ParseException e) {
if (e == null) {
Log.d(TAG, "success!");
mSavedWordList = gameInfo.getString(ParseConstants.KEY_SELECTED_WORDS);
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<String>>() {
}.getType();
ArrayList<String> finalWordList = gson.fromJson(mSavedWordList, type);
mCopy.addAll(finalWordList);
});
word1 = (TextView) findViewById(R.id.word1); //I get the index out of bound exception here,
word1.setText(mCopy.get(1));
word1.setOnClickListener(new View.OnClickListener());
Edit1: I previously stated that I get no exception in the logCat, I was wrong. I get the index out of bound exception. Apologies.
As stated here, getFirstInBackground retrieves a ParseObject based on your query in a separate thread without blocking/freezing the execution in your current thread. Once the fetch is complete, done() in callback object is invoked.
Even though it looks your statements are ordered one after the other in your code, getFirstInBackground is an Asynchronous operation. So fetch will be running in a different thread, while the remaining statements in your code are executed.
As your mCopy is updated within done method, any code that relies on the update should be executed in the same method like below.
query.getFirstInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject gameInfo, ParseException e) {
if (e == null) {
Log.d(TAG, "success!");
mSavedWordList = gameInfo.getString(ParseConstants.KEY_SELECTED_WORDS);
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<String>>() {
}.getType();
ArrayList<String> finalWordList = gson.fromJson(mSavedWordList, type);
mCopy.addAll(finalWordList);
/* mCopy is updated here - so use it now*/
word1 = (TextView) findViewById(R.id.word1);
word1.setText(mCopy.get(1));
word1.setOnClickListener(new View.OnClickListener());
});
I have the following problem:
I have implemented an API to my server returning objects, which is all fine and well. All the calls to the API starts an AsyncTask, and returns a result in a method. Problem is, at one point (I need this is bad coding, but disregard that for a minute) over a list of returned Events. These Events are returned from one AsyncTask call, and used for another AsyncTask call, and I need to pass the event I'm iterating over to the next API call as seen here:
api.retrieveEvents(new GetResponseCallback<Event>() {
#Override
public void onDataReceived(ArrayList<Event> list) {
for (Event e : list) {
api.retrieveReservationsForEvent(e, new GetResponseCallback<EventRegistration>() {
#Override
public void onDataReceived(ArrayList<EventRegistration> list) {
Log.d("We are in event", e.getEventName());
}
});
Now obviously, Java won't allow you to use a local variable outside its classes scope, without it being declared final. That's obviously not gonna work here, and I can't assign the event to a field variable either, since that will just result in the final event I'm iterating over to be the one the retrieveReservationsForEvent call all the use. Any ideas of what to do?
retrieveReservationsForEvent looks like this:
public void retrieveReservationsForEvent(Event event, final GetResponseCallback<EventRegistration> callback) {
final ArrayList<EventRegistration> eventRegistrations = new ArrayList<>();
String restUrl = SERVER_NAME + REST_EVENTREGISTRATION + event.getId();
new GetTask(restUrl, new RestTaskCallback() {
#Override
public void onComplete(String response) {
try {
// TODO probably throws an exception if there is only one attendant...
JSONArray jsonArray = new JSONArray(response);
for(int i = 0; i < jsonArray.length(); i++) {
JSONObject tempEventRegistrationJSON = (JSONObject) jsonArray.get(i);
long dateInMillis = tempEventRegistrationJSON.getLong("timeOfArrival");
Date date = new Date(dateInMillis);
EventRegistration tempEventRegistration = new EventRegistration(tempEventRegistrationJSON.getInt("id"),
tempEventRegistrationJSON.getInt("eventNightId"), tempEventRegistrationJSON.getInt("guestId"),
date, tempEventRegistrationJSON.getInt("numberOfGuests"));
tempEventRegistration.setGuestName(tempEventRegistrationJSON.getString("guestName"));
tempEventRegistration.setGuestPhoneNumber(tempEventRegistrationJSON.getInt("phoneNumber"));
tempEventRegistration.setGuestMail("mail");
eventRegistrations.add(tempEventRegistration);
callback.onDataReceived(eventRegistrations);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}).execute();
}
GetTask is merely doing a GET request, returning the response.
Are you sure that changing it to:
for (final Event e : list) {
doesn't work? Does it give you an error during compilation?
Here are following my classes:
StatsObjectId.java
public class StatsObjectId extends Activity {
DBClass db;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
db = new DBClass(this);
}
public void addObjId(String objid){
Log.e("objectid","This is the object id going to store: "+objid);
db.addObjectId(objid); //This is the line# 105
if(getObjId()){
Log.e("objectid","Successfully stored!");
}else{
Log.e("objectid","Error in storing object id!");
}
}
public boolean getObjId(){
boolean result;
try{
c = db.getObjectId();
c.moveToFirst();
String str = c.getString(c.getColumnIndex("objectid"));
Log.e("objectid","Object id returned form DB: "+str);
result = true;
}catch(CursorIndexOutOfBoundsException e){
Log.e("objectid","Cursor index out of bound");
result = false;
e.printStackTrace();
}catch(Exception e){
Log.e("objectid","Some Another Exception");
result = false;
e.printStackTrace();
}
return result;
}
ParseComServerAccessor.java
public class ParseComServerAccessor {
//I am skipping some irrelevant code
public void putStats(String authtoken, String userId, Tas statsToAdd) throws Exception {
//Again skip some code
//Here I got some HttpResponse and I need to extract an object id and save it to database
HttpResponse response = httpClient.execute(httpPost);
String responseString = EntityUtils.toString(response.getEntity());
JSONObject json = new JSONObject(responseString);
Log.e("objectid","Now Object Id is: "+json.getString("objectId") );
StatsObjectId ob = new StatsObjectId();
ob.addObjId(json.getString("objectId")); // This is the line#156
//skip some code
}
}
TasSyncAdapter.java
public class TasSyncAdapter extends AbstractThreadedSyncAdapter {
//skipped Constructor code
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
//skipped some code
ParseComServerAccessor parseComService = new ParseComServerAccessor();
//skipped some code again
parseComService.putStats(authToken, userObjectId, remoteTas); //This is the line# 134
//skip some code
}
}
Now finally when I run my app... this is the following Log Cat
Tag Text
objectid This is the object id going to store: 9AFysqffz7
System.err java.lang.NullPointerException
System.err at com.myapp.ds_app.StatsObjectId.addObjId(StatsObjectId.java:105)
System.err at com.myapp.ds_app.syncadapter.ParseComServerAccessor.putStats(ParseComServerAccessor.java:156)
System.err at com.myapp.ds_app.syncadapter.TasSyncAdapter.onPerformSync(TasSyncAdapter.java:134)
System.err at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:254)
DBClass.java
public class DBClass extends SQLiteOpenHelper {
private static final String DATABASE_NAME="myapp.db";
public DBClass(Context cxt){
super(cxt, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase mydatabase) {
mydatabase.execSQL("CREATE TABLE IF NOT EXISTS temp (objectid STRING)");
}
public Cursor getObjectId(){
Cursor cursor = getReadableDatabase().rawQuery("SELECT objectid FROM temp", null);
return cursor;
}
public void addObjectId(String objid){
try{
ContentValues cv = new ContentValues(1);
Log.e("objectid","In DBClass and object id: "+objid);
cv.put("objectid", objid);
Log.e("objectid","Content value contains: "+cv.toString());
getWritableDatabase().insert("temp", "objectid", cv);
}catch(NullPointerException e){
e.printStackTrace();
}
}
}
Now, I am stucked at this point!
So far, I need to save just a single value. I tried to create a file instead of saving a value in database. But again there is some exception of ContextWrapper.
I am currently interested to deal with database.
Please let me know if you guys need any other information.
I would really appreciate if any one please explain this thing. I'm android newbie and would love to learn about this problem. Thanks in advance!
StatsObjectId ob = new StatsObjectId();
You are instanciating an Activity class. You are not allowed to do that. (There should really be something in Android to tell you when you do that) Basically, the context is not initialized, because android needs to do that in order to have a functional Activity.
Plus, Android (when it creates the Activity) calls the onCreate method with a proper context. You don't (and you can't, either), therefore your db is null.
In AbstractThreadedSyncAdapter, you have a getContext method to get a proper context. Use this to initialize your database and to insert data in it, rather than passing it to the Activity object.