I have an application Activity that in onCreate loads an XML file from a service using an AsyncTask. The XML is parsed into an ArrayList. When I switch to a different activity and then back to the main activity, I want to be able to recognize that that XML file was already loaded and use the populated ArrayList.
What is the best way to persist that ArrayList?
onSaveInstanceState only seems to support primitives and I've been unable to set up a case where onRetainNonConfigurationInstance actually gets called. So in onCreate, the XML data is loaded from the server ever time I switch to that Activity. I have made the models that are in the ArrayList implement Parcelable, so could use that in some way?
What is the best way to persist that ArrayList?
I don't see where your problem has anything to do with multiple activities. What happens if the user presses HOME (gasp!), for example? Your app will eventually be closed. Do you want to reload the data from the server? If the answer is "yes", then you don't need to "persist" anything, and onSaveInstanceState() may suffice (see below). If the answer is "no", then you need to rethink your approach to your data model, so you arrange to keep the data in a database, synchronizing with your Web service periodically, and probably dumping the ArrayList and replacing it with a Cursor.
onSaveInstanceState only seems to support primitives
If the answer to my HOME question is "yes", then you can just hold onto the data in a data member of your activity, and, if it is modestly sized, also stash it in the Bundle in onSaveInstanceState(). A Bundle can hold an ArrayList of Parcelable. However, if the data set is large (say, 100KB or more), you probably don't want to go this route and should consider the "no" path I described above.
I've been unable to set up a case where onRetainNonConfigurationInstance actually gets called.
Rotate the screen. There are other scenarios, but orientation changes are the easiest ones to trigger it.
However, it has nothing to do with your problem.
"onSaveInstanceState only seems to support primitives"
onSaveInstanceState supports objects, as long as they are declared serializable.
// ON_SAVE_INSTANCE_STATE
// save instance data (5) on soft kill such as user changing phone orientation
protected void onSaveInstanceState(Bundle outState){
password= editTextPassword.getText().toString();
try {
ConfuseTextStateBuilder b= ConfuseTextState.getBuilder();
b.setIsShowCharCount(isShowCharCount);
b.setTimeExpire(timeExpire);
b.setTimeoutType(timeoutType);
b.setIsValidKey(isValidKey);
b.setPassword(password);
state= b.build(); // may throw
}
catch(InvalidParameterException e){
Log.d(TAG,"FailedToSaveState",e); // will be stripped out of runtime
}
outState.putSerializable("jalcomputing.confusetext.ConfuseTextState", state); // save non view state
super.onSaveInstanceState(outState); // save view state
//Log.d(TAG,"onSaveInstance");
}
Related
I'm a just diving head first into Android Studio and am working on my first app which is a sort of surfing log application. Basically it is to keep track of various weather conditions each time the user goes out surfing.
I have created a Java class "Session" with the following fields:
Date (Date)
Location (String)
Tide height (float)
Surf size (float)
more to come, keeping it simple for now.
My application will flow as follows:
There will be a Main home screen activity, with buttons, and also a small tableview that displays your 3 most recent sessions.
Buttons including:
New Session: this takes you to a new activity with various text inputs for each of the above fields, a date selector... and a save button.
My Sessions: this will take you to a tableview where you can view all of your past sessions. You can organize them by location, date, surf size...
Thus my question:
What is the best practice to pass all this data between the various activities?
To me the most obvious way to go about this is to have a central ArrayList that gets loaded in the main activity and then this gets somehow passed around to all of the subsequent activities. It contains all the sessions that you have ever created.
So lets say I tap the "new session" button on the main screen. This takes me to the new Session activity. I enter all the fields and hit save. I would like to take this data, create a new session object and then add this to the array back in Main Activity. So far my research tells me to pass all this data back using a bundle and the intent.putextra() technique. However it seems cumbersome. Is there not a better approach where I could just create a new object of my Session class and then append it to the central array?
Again let's say I tap the 'my sessions' button from the main activity. I would like this to load up a new activity which is a tableview that allows the user to scroll through all of their previously created sessions and tap on one to view the details.
I've done a bit of research and it seems there are various ways of going about this, I've read up a bit on Singletons, I've looked into creating some sort of static class that I could then reference from multiple activities, I've read about parcelable and serializable...
Perhaps though someone with some android experience could shed some light on the most efficient and standard way of accomplishing what I would like to do.
Again I want to reiterate that this Array or collection of "Sessions" is going to be the center of the app. Pretty much every activity I implement down the road is going to be using this data in someway or another. Whether it's displaying it a tableview which can be sorted in different ways, to running statistical analysis on it, to displaying pins on a map of each location...
I think you want to keep your data in a database and use a pub/sub workflow to keep track of it.
For example, you can use Firebase. Everytime a part of your application does an update (even a different user on a different device) any other part of your code can listen to that change and capture it.
Firebase is just an example, RethinkDB, MongoDB all have this option.
It is not a good option to use a data structure that is on your app: it will become really messy if it needs to be shared between different parts of your app, and impossible if you need other users to be aware of the change.
I think you should go for an event-based library like RxJava2. you can easily subscribe to a bus (subject) which emits all data created up till now.to keep other app's components up-to-date.
for you specefic use case. there is sth great in RXjava2 is called Replay Subject
Replay Subject:
It emits all the items of the source Observable, regardless of when the subscriber subscribes
a simple implementation would look like
public class RxReplayBus {
private static ReplaySubject<Object> subject = ReplaySubject.create();
private RxReplayBus() {
// hidden constructor
}
public static Disposable subscribe(#NonNull Consumer<Object> action) {
return subject.subscribe(action);
}
public static void publish(#NonNull Object message) {
subject.onNext(message);
}
}
from your component subscribe :
disposable = RxReplayBus.subscribe(new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
}
});
and unsubscribe using :
disposable.dispose();
I am trying to send the string from one Activity to Another WITHOUT changing the CURRENT ACTIVITY. This my code I used:
Intent intent = new Intent(this, AnotherActivity.class);
intent.putExtra("getFollowerNumberData", txt);
startActivity(intent);
Using this code bring me to the other activity or I do not want that. I just want to send this string without changing activity. I tried this one
Intent intent = new Intent();
But it is crashing my app. First of all it is possible to do so? If yes, how can I achieve that?
Edit: To be more clear, it works like follower and Following features in other apps or games. When you follow someone the number of the person you just followed goes up as well your Following number. Since my SignInPlayerProfile.Class(where the Following should go up to one too) is in another activity I was trying to get this information intent.putExtra("getFollowerNumberData, txt); from Main Activity and display that in the SignInPlayerPlayer by using text.setText(). But the Problem is by using intent = new Intent(this, AnotherActivity.class); it brings me to SignInPlayerProfile Activity which I do not like since
I am trying to send the string from one Activity to Another WITHOUT changing the CURRENT ACTIVITY(or simpler words WIHTOUT Going to the Other Activity).
Thanks for Help.
When an activity getting closed, according to it's life-cycle, it will be destroyed and no longer exists, when you start an activity it will be created and then it can get your data, so you can't send data to an destroyed activity which no longer exists
You can use Static Variables to communicate between activities (Classes), you can change value of a Static-Variable of an activity from another activity, but it's not a good option for data you need to be alive because Static Variables lives on Heap Memory and Heap will be freed if Android OS needs more Memory
Another way is to create a Message Handler in your first activity as a Static Variable and then send a Message to the Handler from second activity, see this example :
http://stacktips.com/tutorials/android/android-handler-example
I suggest you using SharedPreferences for saving your data in first activity and load from it on second activity
EDIT :
According to your edit, the "Number" you want to use in another Activity as "Following" or "Followers" is just needed when the second activity is visible, you should use sharedpreferences to save the "Number" and load from it when you need it. For example before text.settext() method you can load the number from sharedpreferences and then pass it to text.settext()
You should not save your data on the variables or classes and should save them on a file like a Database or SharedPreferences then you can load them every time you want
Furthermore you can search about Activity life-cycle and see how to use life-cycle events like OnStart to load your data
Firstly please consider startActivityForResult() ,we can send information from one activity to another and vice-versa. As mentioned consider using shared_preferences for local in memory storage. To truly accomplish this feat in a elegant way though, do consider using obervables(rxandroid). You publish observations in one activity or fragment ,
then subscribe in another activity or fragment. I did not mentioned event bus nor otto since rxjava/rxandroid surpasses it. They act a promise context management system. Also because observable in process dependent consider using broadcast receivers, to broadcast events through out your application and external if so desired.
If I have a class that contains a static variable, say x:
class MyClass {
static boolean x = false;
// Other methods
}
Now let us say that, hypothetically, I set x = true; from my first activity. Is there any point through the rest of my app's life cycle (including various activities and threads) where this value will simple be 'reset' back to false due to how the 'Google JVM' or the android environment works? I have heard that static variables have a 'lifetime', that dies when the program dies. Do Activities count as separate 'programs'? What about services? Or even Widgets?
I am asking this because it is often difficult to share complex data structures that rely on other complex processing (like syncing data from an online database) in android due to how 'separated' activities are, and static variables are often a very quick and dirty solution to the problem. Other things I have tried include serialisation, but that doesn't really seem like a practical solution either (constantly serialising and decoding objects when the user navigates from one activity to the next seems like it would be very resource intensive).
If I am an evil person for doing this, please tell me what I am doing wrong, or even better, give me some links or examples of better ways to solve this problem.
Yes. There are times where that will reset. Primarily if the user leaves the app and starts fiddling around with other apps or if the user lets the phone go to sleep for a long period of time. The Android process could kill the actual app. Then the "state" of the app will be restored when the user comes back, however static variables will be at their defaults because the actual process was rebuilt.
Generally passing small objects between Activities and Services is done by overriding the Parcelable interface. This will allow you to save and restore objects using setOnInstanceState methods of both Activities, Views, and some adapters. They will likewise, have a restore method in which you can rebuild the object. Parcelable is preferable over Serializable.
Larger data may require a shared file or database depending on the data that you want to have synced. There is a 1 MB size limit for parcelables being passed between Activities. One common tactic is to save the information to a file and send a URI to the location of where the information can be retrieved.
Answering your question - yes, there is a situation when you set x = true and value will be 'reset' back to false. Well, not exactly reset but consider this scenario: you have an activity and a service. Service is using separate process (you can define that in AndroidManifest when you declare your service). Then those two processes (main app and service) won't share memory and setting x to true in your activity won't affect the value of MyClass.x in your service. In all other cases changing value in one place will be visible everywhere else. Hope it helps!
No, a static variable will not be changed unless you change it or the app ends, it is safe (but generally unclean) to use it. Closing the activity the variable lives in won't hurt it.
You suggest you just need to keep track of a value as you move around activities. In that case you can add the value in your Intents as what is called an 'extra'. If you need to also pass back the value after, android also has the startActivityForResult feature
Intent extras example:
x below could be any type of value including any object which implements Parcelable
Intent intent = new Intent(...);
intent.putExtra("myKey", x);
startActivity(intent);
in receiving class:
x = getIntent().getBooleanExtra("myKey");
Edit:
Given your additional comment - "lists of objects that contain yet more lists of objects" you may get a Parcel too large exception when trying to use extras, but this is an indication you have a bigger architectural problem and that there may be a better approach
Use Gson.
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source-code of.
You have two options. Convert to string , then put data in an intent then pass to activity. Or Convert to string with gson, save to a preference, then in the other activity, check if the preference is alive and read from it.
If you wish to be bold, you can persist to database preferably using Realm for Android or ObjectBox is a new mobile object database optimized for performance. With ObjectBox, we are bringing technology from NoSQL server databases to mobile.
I'm not very sure of how to go about doing this, so I'm asking for some suggestions.
For starters, I have a activity that contains ClassA.java and ClassB.java.
ClassA has a button that, when tapped, goes into ClassB. ClassB then asks for input (1 Integer and 1 String), and then reverts back to ClassA, which displays what the user did on screen. I also want the user to be able to hit 'Save' when everything is done, back in ClassA.
Problem is, how do I go about storing that data temporarily and permanently? ClassB does not return anything, so I'm thinking the temporary storage of data should be done in ClassB, brought over to ClassA, then saved in a database if the user hits 'Save'.
Am I doing this the right way?
Also, am I right to call Android Bundles 'temporary storage'? They save the user's input temporarily, and if the application closes the Bundle is deleted (nothing is saved), right?
So would storing strings and integers in a Bundle and then transferring them to a database afterwards be theoretically correct and also be a possible way to tackle this problem?
Or does anyone have a better solution?
If you want to withhold values permenantly, i.e.., Even after restarting the application, you want the data back to your application, then go for SharedPreferences
Shared Preferences
If you want to with hold temporarily and you want to pass the value from one Activity to another activity, You should pass intent and get the values from the bundle in the target activity.
Intent
I have this listview that gets populated with the data that is downloaded from the internet.
Hence in the onCreate() method, I will run the async task to download the information and put it into the listview.
I placed a boolean value to whether if the list is downloaded or not during the saveInstanceState() method, this will work if the user gets out of the app from the home button and returns. However, when the user exits the program through the back button, the saveInstanceState() method is not run.
I do not want to download the list again, how can I check whether if it's downloaded before?
Check this out: Implementing the lifecycle callbacks
http://developer.android.com/guide/topics/fundamentals/activities.html
Use the callbacks to get noticed what happens to your activity and react if nesseceray.
You can use the Shared Preferences to store key-value data:
http://developer.android.com/guide/topics/data/data-storage.html
If you want to persist data beyond the LifeCycle of the application, you'll need to store it outside the Bundle.
Your options are:
Shared Preferences
Internal Storage (on Device)
External Storage (memory card)
Local Database
Network (which would probably defeat your question)
I'd suggest a private Internal Storage file for saving your data.
You can override Activity.onBackPressed() and save your state there as well.
Also, you can do
void onPause() {
if(isFinishing()) {
// save your state. maybe you'd be better off with using preferences (or other things suggested by people here), so you don't need to worry about the instance state.
}
}