Currently I am working on the second version of my vocabulary app. The first version does not provide as much documentation to expand the application. so I decided to start from the ground up.
I have an activity, every time I send it to background and then get it back to foreground, some objects became null and activity restarts. In onResume, I logged the value of the null object.
The old activity (version 1) does not have that problem.
I tried avoid that null problem whilst saving activities states and objects to SaveInstanceState and restore them. but RestoreInstance was never called.
here is my code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_query);
Log.e(TAG, "onCreate");
/* Initialize DATA */
data = new DataHandler(this);
/* Initialize VIEWS */
btnPhase = findViewById(R.id.query_voc_phase);
cardPath = findViewById(R.id.query_voc_path);
queryView = findViewById(R.id.query_view);
btnShow = findViewById(R.id.query_btn_show_answer);
btnRight = findViewById(R.id.query_btn_mark_right);
btnWrong = findViewById(R.id.query_btn_mark_wrong);
btnNext = findViewById(R.id.query_btn_show_next);
}
#Override
protected void onStart() {
super.onStart();
Log.e(TAG, "onStart");
/* set OnClickListener */
btnShow.setOnClickListener(this);
btnRight.setOnClickListener(this);
btnWrong.setOnClickListener(this);
btnNext.setOnClickListener(this);
//init Variables
queryList = new ArrayList<>();
idList = new ArrayList<>();
listGroupings = new ArrayList<>();
answeredList = new ArrayList<>();
//init Preferences
preferences = PreferenceManager.getDefaultSharedPreferences(this);
//set Visibility of view items based on Preferences
if (!preferences.getBoolean(Preferences.SHOW_CARD_PATH_ENABLED, true))
cardPath.setVisibility(View.GONE);
//load preferences
userID = preferences.getInt(Preferences.CURRENT_USER, -1);
//check if flashing card is enabled and get time
flash_time = preferences.getBoolean(Preferences.FLASH_CARD_ENABLED, false) ? preferences.getInt(Preferences.FLASH_TIME, flash_time) : -1;
//get data from intent
Intent intent = getIntent();
if (intent.hasExtra(QUERY_CONTAINER_LIST)) {
//get data into the list
HashMap<String, ArrayList<Integer>> queryData = data.getCardsToQuery(intent.getStringArrayListExtra(QUERY_CONTAINER_LIST));
idList = queryData.get("iList");
listGroupings = queryData.get("listGroupings");
}
// change active Fragment to initial fragment
changeFragment(QueryViews.Simple);
}
#Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
Log.e(TAG, "current card is null " + (currentCard == null));
//loads a new card if there is none
if(currentCard == null) loadNext();
}
/*
InstanceState
*/
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e(TAG, "RESTORE");
queryList = savedInstanceState.getParcelableArrayList("queryList");
idList = savedInstanceState.getIntegerArrayList("iList");
listGroupings = savedInstanceState.getIntegerArrayList("listGroupings");
answeredList = savedInstanceState.getParcelableArrayList("answeredList");
currentCard = (VocabularyCard)savedInstanceState.getSerializable("currentCard");
lastCard = (VocabularyCard)savedInstanceState.getSerializable("lastCard");
}
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "SAVE");
outState.putParcelableArrayList("queryList", queryList);
outState.putIntegerArrayList("idList", idList);
outState.putIntegerArrayList("listGroupings", listGroupings);
outState.putParcelableArrayList("answeredList", answeredList);
outState.putParcelable("currentCard", currentCard);
outState.putParcelable("lastCard", lastCard);
super.onSaveInstanceState(outState);
}
Spotting below error, maybe it is a typo error, but may be the culprit of your null object issue. You restore iList inside onRestoreInstanceState,
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e(TAG, "RESTORE");
idList = savedInstanceState.getIntegerArrayList("iList");
}
But inside onSaveInstanceState, you put idList,
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "SAVE");
...
outState.putIntegerArrayList("idList", idList);
...
super.onSaveInstanceState(outState);
}
I found a solution. I found out that onRestoreInstanceState will called if device rotates. First of all if something became null, you should first check if the object was assigned a value. in this case i overlooked, that the object was never assigned a value.
Related
I am currently building an Android app where I display quotes from famous people. I have a home screen and 2 other screens, where I am displaying all quotes and the other where I display favourite quotes.
So, when I hit the like button in the screen of AllQuotesActivity the quote and author will be saved in a LinkedHashSet, which will be saved in SharedPreferences, so my FavouriteQuotes Activity can obtain the data. I can obtain the data, but the data is mixed, even though other links say that LinkedHashSet maintains the insertion order. Maybe I did something wrong. Here are the important code snippets:
AllQuotesActivity.java:
SharedPreferences sharedPref;
Set<String> set = new LinkedHashSet();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.all_quotes);
Resources res = getResources();
Context context = getApplicationContext();
this.sharedPref = context.getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
final String[] quotesAndAuthors = res.getStringArray(R.array.quotes);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
set.add(quotesAndAuthors[counter]);
set.add(quotesAndAuthors[counter + 1]);
}
});
}
#Override
public void onPause() {
super.onPause();
Log.d("RichQuotes", "Its paused mkay");
Editor editor = sharedPref.edit();
editor.putStringSet("quotesAndAuthors", this.set);
editor.commit();
}
}
FavouriteQuotesActivity.java:
SharedPreferences sharedPref;
Set<String> set = new LinkedHashSet();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.favourite_quotes);
Resources res = getResources();
Context context = getApplicationContext();
this.sharedPref = context.getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
set = sharedPref.getStringSet("quotesAndAuthors", null);
for (Iterator<String> it = set.iterator(); it.hasNext(); ) {
String s = it.next();
Log.v("test", s);
}
I removed unnecessary code.
In the FavouriteQuotesActivity.java I am logging the set to check its values. The log-outputs and the outputs on the screen are the same, both unsorted the same way.
Set set = new LinkedHashSet();
In this line, you instantiate a new empty LinkedHashSet object. This object was then assigned to the variable named set.
set = sharedPref.getStringSet("quotesAndAuthors", null);
In this line, you reassigned the set variable to point to some other object, some Set object returned by your call to getStringSet. We do not know the concrete class of this second object that implements the Set interface. You can ask by calling getClass.
Your first set, the empty LinkedHashSet, went unused. With no other references pointing to it, that set became a candidate for eventual garbage-collection.
Update: including actual code
I am using ArrayList to fill a custom adapter and it works fine on orientation change etc.:
"onCreate"
myArrayList = savedInstanceState.getParcelableArrayList(myListkey);
"onSaveInstanceState"
savedInstanceState.putParcelableArrayList(myListkey, myArrayList);
and I can get it out fine
But.. I need to pass multiple variables and then it breaks, it looks like it´s only the one inserted last that I can get.
ex.1
"onCreate"
myNameString = savedInstanceState.getString(namekey);
myArrayList = savedInstanceState.getParcelableArrayList(myListkey);
"onSaveInstanceState"
savedInstanceState.putString(listkey, myNameString);
savedInstanceState.putParcelableArrayList(myListkey, myArrayList);
Here "myArrayList" is filled, but "myNameString" is null
ex. 2
"onCreate"
myArrayList = savedInstanceState.getParcelableArrayList(listkey);
myNameString = savedInstanceState.getString(namekey);
"onSaveInstanceState"
savedInstanceState.putParcelableArrayList(listkey, myArrayList);
savedInstanceState.putString(namekey, myNameString);
Here "myNameString" is filled, but "myArrayList" is null
original code:
#Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
_listName = savedInstanceState.getString(getApplicationContext().getResources().getString(R.string.saved_instance_list_name));
_listItems = savedInstanceState.getParcelableArrayList(getApplicationContext().getResources().getString(R.string.saved_instance_list_items_edit));
}
... more code
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(getApplicationContext().getResources().getString(R.string.saved_instance_list_name), _listName);
savedInstanceState.putParcelableArrayList(getApplicationContext().getResources().getString(R.string.saved_instance_list_items_edit), _listItems);
}
I have created Activity with a listView which uses setOnItemClickListener to determine which index has been pressed. Then it should pass the data into the next activity based on the selected index.
Passing of the values works, but it only works for the first listView index pressed after the app is reloaded.
To make it more clear. When the app is loaded I can press on any index of the ListView which will pass the data to the next Activity and display it correctly, but then when I go back to the ListView and select different index it still displays the same data from the previous index without updating it. Therefore, I have to reload the app to get new index results displayed.
Here is my ViewListActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_artist);
artistList = (ListView) findViewById(R.id.artistList);
databasehandler = new SqliteHandler(ViewArtistActivity.this);
allArtists = databasehandler.getAllArtists();
artistName = new ArrayList<>();
singleArtistDetails = new ArrayList<>();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (allArtists.size() > 0) {
for (int i=0; i < allArtists.size(); i++) {
ArtistModel artistModel = allArtists.get(i);
artistName.add(artistModel.getArtistName());
}
}
adapter = new ArrayAdapter(ViewArtistActivity.this, android.R.layout.simple_list_item_1, artistName);
artistList.setAdapter(adapter);
artistList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ArtistModel artistModel = allArtists.get(position);
singleArtistDetails.add(artistModel.getArtistName());
singleArtistDetails.add(artistModel.getArtistDOB());
singleArtistDetails.add(artistModel.getArtistBiography());
Intent intent = new Intent(ViewArtistActivity.this, SavedArtistDetailsActivity.class);
intent.putExtra("NAME", singleArtistDetails.get(0));
intent.putExtra("DOB", singleArtistDetails.get(1));
intent.putExtra("BIOGRAPHY", singleArtistDetails.get(2));
startActivity(intent);
}
});
}
Here is my code for displaying selected index results Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_saved_artist_details);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
artistSavedNameLbl2 = (TextView) findViewById(R.id.artistSavedNameLbl2);
artistSavedDOBLbl2 = (TextView) findViewById(R.id.artistSavedDOBLbl2);
artistSavedBiographyLbl2 = (TextView) findViewById(R.id.artistSavedBiographyLbl2);
btnDeleteDetails = (Button) findViewById(R.id.btnDeleteDetails);
updateUI();
}
private void updateUI() {
artistSavedNameLbl2.setText(getIntent().getStringExtra("NAME"));
artistSavedDOBLbl2.setText(getIntent().getStringExtra("DOB"));
artistSavedBiographyLbl2.setText(getIntent().getStringExtra("BIOGRAPHY"));
}
Do one thing , initialize [not saying declare] your array list singleArtistDetails inside the onClick method, before adding any values to it.
like
singleArtistDetails = new ArrayList();
Hope this will help.
I have a fragment activity where I navigate to a search page, then back to the list with the result. Sometimes the activity needs to be re-created, so I am saving my instance data like so:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
System.out.println("Saving instance state!");
// Save the instance data
// Filter data
Intent mIntent = new Intent(this, BasicMapDemoActivity.class);
filter.addFilterToIntent(mIntent);
outState = mIntent.getExtras();
// Save date data
int year = currentDate.getYear();
int month = currentDate.getMonth();
int day = currentDate.getDay();
outState.putInt("YEAR",year);
outState.putInt("MONTH",month);
outState.putInt("DAY",day);
// Location data
double latitude = mapLocation.latitude;
double longitude = mapLocation.longitude;
outState.putDouble("LATITUDE",latitude);
outState.putDouble("LONGITUDE",longitude);
// Other
outState.putString("VIEW_MODE",viewMode.toString());
outState.putString("SORT_TYPE",sortType);
outState.putInt("ZOOM", mapZoom);
}
Then I am attempting to restore the data in onCreate, but when I get there, the bundle is not null, but it does not contain any of the data I saved to it. All of the following get functions return zero (I confirmed that the data was non-zero at the end of the above code block):
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If we are 're-creating', then get the old instance data, otherwise set defaults
if(savedInstanceState == null)
{
// Set up default instance data
currentDate = new Date(115,4,1);
filter = new ObajoFilter();
filter.start = currentDate;
filter.end = new Date(115,4,1,23,59,0);
viewMode = ViewMode.MAP;
sortType = "Distance";
mapLocation = userLocation;
mapZoom = 16;
} else {
// Get date data
int year = savedInstanceState.getInt("YEAR");
int month = savedInstanceState.getInt("MONTH");
int day = savedInstanceState.getInt("DAY");
currentDate = new Date(year,month,day);
Any ideas on what could be emptying out the bundle? From what I understand, the savedInstanceData is only empty once the app is completely destroyed.
Because of this line outState = mIntent.getExtras();
Your outState points to different address mIntent.getExtras(), So it's empty
You should use outState.putXX directly without creating new object.
Are you storing the information that you want to retain in the onSaveInstanceState something like the following
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable("someobject", someobject);
outState.putBoolean("someboolean", someBoolean);
outState.putInt("someint", someInt);
super.onSaveInstanceState(outState);
}
Right now I am have a activity screen with edittext lines that I am obtaining user input from. When I hit the create event button on the button, the event text should populate into a news feed that I have on another activity.
Here is the portion CreateEvent activity/screen code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_event);
CreateEvent_Button = (Button)findViewById(R.id.create_event_button);
WhatEvent_Text = (EditText)findViewById(R.id.what_event);
WhenEventTime_Text = (EditText)findViewById(R.id.time_event);
WhenEventDate_Text = (EditText)findViewById(R.id.date_event);
WhereEvent_Text = (EditText)findViewById(R.id.where_event);
CreateEvent_Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WhatEvent_String = WhatEvent_Text.getText().toString();
WhenEventTime_String = WhenEventTime_Text.getText().toString();
WhenEventDate_String = WhenEventDate_Text.getText().toString();
WhereEvent_String = WhereEvent_Text.getText().toString();
Log.e("What: ", WhatEvent_String);
Log.e("When_Time: ", WhenEventTime_String);
Log.e("When_Date: ", WhenEventDate_String);
Log.e("Where_Event: ", WhereEvent_String);
Intent intent = new Intent(CreateEvent.this,MainActivity.class);
intent.putExtra("WhatEvent_String", WhatEvent_String);
intent.putExtra("WhenEventTime_String", WhenEventTime_String);
intent.putExtra("WhenEventDate_String", WhenEventDate_String);
intent.putExtra("WhereEvent_String", WhereEvent_String);
startActivity(intent);
MainActivity main= new MainActivity();
//make sure you call method from other class correctly
main.addEvent();
}
});
}
When the create event button is pressed, it creates an event into the MainActivity screen/activity; however, the user input is null. I am not sure why this is happening because I believe I am using the intent methods correctly.
Here is my MainActivity screen.
Here is the getIntent method in my onCreate method in my MainActivity
Intent intent = getIntent();
if (null != intent) {
test = intent.getStringExtra("WhatEvent_String");
}
However, when I call my addEvent method:
protected void addEvent() {
// Instantiate a new "row" view.
// final ViewGroup newView = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.list_row, mContainerView, false);
// Set the text in the new row to a random country.
// Because mContainerView has android:animateLayoutChanges set to true,
// adding this view is automatically animated.
//mContainerView.addView(newView, 0);
HitupEvent hitupEvent = new HitupEvent();
hitupEvent.setTitle("Rohit "+ "wants to "+test );
hitupEvent.setThumbnailUrl(roro_photo);
//hitupEvent.setRating(30);
//hitupEvent.setYear(1995);
//hitupEvent.setThumbnailUrl(null);
ArrayList<String> singleAddress = new ArrayList<String>();
singleAddress.add("17 Fake Street");
singleAddress.add("Phoney town");
singleAddress.add("Makebelieveland");
hitupEvent.setGenre(singleAddress);
hitupEventList.add(hitupEvent);
adapter.notifyDataSetChanged();
}
The event input text is null. I am not sure why this is happening.
I tried to resolve it but no luck,
How to send string from one activity to another?
Pass a String from one Activity to another Activity in Android
Any idea as for how to resolve this?!
Thanks!
Don't use Intent to get the String you put through putExtra()
Intent intent = getIntent(); // don't use this
if (null != intent) {
test = intent.getStringExtra("WhatEvent_String");
}
Instead Use Bundle, an example snippet
Bundle extra = getIntent().getExtras();
if(extra!=null){
test= extra.getString("WhatEvent_String");
}
EDIT
I noticed just now that you're calling the method main.addEvent() from the wrong place ! Call it in the MainActivity in the onCreate() method.
Remove these lines
MainActivity main= new MainActivity();
//make sure you call method from other class correctly
main.addEvent();
and in your MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.yourLayout);
Bundle extra = getIntent().getExtras();
if(extra!=null){
test= extra.getString("WhatEvent_String");
}
addEvent(); // call your addEvent() method here
}