In my app, I inject some singleton manager objects using dagger.
Suppose one of these managers, say myManager, keeps data in a list, say myList.
Also I have a BroadcastReceiver class, say myBroadcastReceiver which calls one of myManager methods, say myMethod, when it receives some particular intents.
If I open my app and wait until myList is initialized, then press home, and after that myBroadcastReceiver receives the intent and calls myMethod, myList is null (though myManager itself is not null).
I can't figure if it's a matter of android's natural behavior or if I've actually made a mistake that it happens.
I think your singleton class is getting unloaded due to low memory you need to save that list in onSaveInstanceState(Bundle outState) and retrieve the same in onCreate method
You could either implement a "save state" solution using onSaveIntanceState functionality provided by android in your activity or service or whatever component you're using
Or you can save your data locally in a file or even better a sqlite database, which could be very simple and straight forward to implement.
And by keeping your list "in memory" synch'd with the local storage, you'll never lose any data.
Simply using lazy loading, each time your list is null, fetch the data back from local storage.
Related
There are 3 activities: A->B->C. Each contains a Button (to open next activity) and a EditText.
For example: if I type some text in C and go back to A(by pressing Back Button), how can I see the same text there?
I know 3 solution:
LocalBroadcastManager
SharedPreferences
Create Singleton class with static field and then get this field in onStart method of A - which cons of this solution?
IMHO, there are always cons in using Singleton design pattern in your applications. Some of them are (from the top of my head):
Coupling between otherwise unrelated objects and flows through Singleton's instance
Emergence of a "global state", which makes debug a lot harder
Inability to mock static fields and methods through "conventional" mocking
The fact that a reference to Singleton can be easily obtained in any part of the application leads to a total mess (people stop thinking about dependency graph)
Singletons tend to breed: you introduce one, then another one, then you find yourself with 10 singletons which hold app's state in a "global cloud state"
Note that what you're trying to do is against Android guidelines - if the user taps on "back" button, then he should find the previous Activity or Fragment in the exact same state it had the last time the user saw it, without any additions (unless you explicitly don't want to save it in the back-stack).
If you still want to do it, then I could suggest several options:
Use SharedPreferences and store the value there. Get the value in each Activity and diplay it in onResume()
Use startActivityForResult() call in order to start new Activities and pass the value back in the result. Note that by default press on "back" cancels the action, therefore you'll have to override onBackPressed() method.
Override onBackPressed() method in Activity in such a way that it starts another Activity (instead of just popping the back-stack) and pass the value in the Intent that you use. You might want to use FLAG_ACTIVITY_CLEAR_TOP in this case.
Use some event bus that supports "sticky" events. When user inputs the text you post a sticky event to event bus. In onResume() of Activity you check whether event of this type exists and if it is - you update UI.
Once again - the fact that you CAN do what you want doesn't mean it SHOULD be done.
Simply set into onResume() method of your class A, a call to the Singleton class instance you want to save (or istance of Application class which is the same)
LocalBroadcastManager is not a reliable option. It assumes the bottom activity to still be alive, which might not be the case. While you use B, A might be collected to free resources. Also, you should unregister LocalBroadcastManager receivers onResume(). So, no.
Singletons with static fields are generally to be avoided. It’s not worrying for a single string of text, but singleton fields are easily forgotten and can lead to memory leaks. Better to avoid this pattern if possible, and in your case it is.
Possible options.
If the field is something that must persist, e.g. user editing his username, use SharedPreferences or another storing solution (saving to a server, saving to cache, saving to device SQLite database).
If the field is temporary and is the result of activity B, you can start activity B with startActivityForResult() and then send the result back to activity A through an Intent.
For your problem the simpliest solution - store your value in Application class. Any activity can access it and read/write values.
Cons is that if you accidentally store static reference to activity, it will cause memory leak.
You may try using EventBus for horizontal communication: Activity-> Service, Service -> Fragment, etc.
It has static instance by default plus you can subscribe/unsubscribe to it in onPause and onResume methods.
Another advantage is a STICKY EVENTS - you can post event from Service and it will wait until something handle it - Activity will receive this event when it is ready - after onResume().
I need to share a list among all my activities, so I've extended my application class in order to store inside it my list :
public class MyApplication extends Application {
public ArrayList<Object> theList = new ArrayList<Object>();
}
It works fine, except sometimes when I resume the app, the list is cleared. The list is not null but is empty (while I never clear the list in my code of course).
I wonder why the list is cleared.
If it's because of the garbage collector, the list would be null, isn't it ?
Do you know a way to keep data even after resume ?
Your list lives in RAM, and not on the device storage, so if the app is removed from RAM by the system, even momentarily, the data will be lost.
What you need to do is save the data into a db or shared prefs but also have an in memory model of it as you do.
In MyApplication.onCreate method, start an ASyncTask to load the data from the db or shared prefs, so each time your app is restarted (by the system), it will load the data correctly. Use a boolean in your MyApplication class so you can check if the init is finished, and make it readable from your activities that need the data, so they know if loading data from db is in progress (show loading UI in this case).
Each time you add or remove an item from the list, also carry out the same operation on the db/shared prefs, so your saved data is always in sync with your in memory model.
Whether you choose a db or shared prefs depend on the type of data you have, but as it's a list, I'm guessing a db would make more sense.
I am developing an Android app and I would like to avoid reloading similar data when it comes from the same Activity using the same extra.
Specifically, when I launch my Activity 'A' with extra, I use this extra to load remote data from server.
From this Activity, I can relaunch 'A' with different extra and so on.
Example :
A:id1 --> A:id2 --> A:id3
But, it can also be an extra that I already loaded :
A:id1 --> A:id2 --> A:id3 --> A:id1
In this case, I wouldn't request the server again or lose the activities stack.
As I understand, "onSaveInstanceState" allows to save one instance of one Activity, but in my case, it's multiple instances of same Activity (with differents extras).
So, is it the solution to manage a list of saved instance states (bundle) for the same Activity ? Or something else ?
Thanks in advance
The onSaveInstanceState method isn't used in the way you describe. Check this out, it's the documentation for the Activity Class, specifically the Activity Lifecycle section. onSaveInstanceState is a method that gets called when the OS has to kill an Activity for some reason. It allows you to populate a Bundle which will help recreate that specific instance of the Activity where the user left off. Usually this happens because the user switched to a different app and the OS is killing the Activity to reclaim memory, but also happens on screen rotation, so it's a nuance of the platform that is important to at least be aware of.
As for your question, what I would do is use a database to store the information that is retrieved from the server. When you start an Activity, you can first check to see if the data that needs to populate that Activity exists in the database. If it does, load and display it from there, else make the server call.
This is nice, because the data will be persistent over multiple uses of the App. Going further, if the data from the server has the potential to be stale, you can easily extend this to display the data from the database initially, and fire off an asynchronous request for the data that will update both the UI and database when it returns. Your user will almost never be in a state where they're waiting for things to load, which is always a good thing!
Here's a good tutorial on the basics of implementing an sqlite database. This will also give you the added benefit of keeping the data stored over separate runs of your application.
As an alternative, if you don't really need the persistence or other features of the database and don't think that the overhead is worth it, you could create a Singleton class which keeps track of the data as its returned, perhaps implementing it using the Application class. It's important to note (and bringing us full circle) that any in-memory method of storing this data should be managed with onSaveInstanceState to ensure you don't lose any data if the Activity is killed at an unexpected time.
+1 for MattDavis' answer, but I'd also suggest you use what's known as the "singleton pattern".
Essentially, this is a way to cache things in memory. You create a class whose purpose is to hold all the database data you don't want to keep reloading. There's a single global instance of this object, which is initially null. You call a static method that returns that instance. If the static method finds that the instance is null, it creates the instance, populates it from the database, caches it, and returns it to the caller. From that point on, all requests for the instance just return the cached copy. If your app gets killed by the system for lack of resources, and started again later, it transparently re-creates the instance as needed.
This is a very common implementation, and works in a multi-threaded environment:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
// This is the place where you initialize the instance
// from the database.
}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
I use this pattern in a lot of places; it's very handy.
To really do it right, you should also implement onLowMemory() or onTrimMemory() in your activities to release the singleton instance when resources are tight.
Hello fellow developers,
I am trying to achieve something in Android and I would like some advise on the best practice.
I have created an Activity which can start and stop a Service which collects data.
Instead of simply starting and stopping the Service, the Activity should also display the data collected by the Service.
Here in lies the problem. The data could be quite large so I would like to avoid Serializing and sending it via an Intent. Is it possible to simply get a reference to the data stored in the Service from the Activity?
Simple Example
1) Activity starts
2) Activity starts Service to collect data
3) Activity exists
4) 24 hours pass
5) Activity starts
6) Activity wants to display data collected by Service, but data is quite large.
My question again is simply this. Can the Activity get a reference to the data stored in Service or does the data have to be Serialized and sent from the Service to the Activity using an Intent?
Kind regards,
Cathal
Research android.app.Application class. Essentially you can use this for global state.
http://developer.android.com/reference/android/app/Application.html
Also, any long term data should be stored to a local database. I would try to limit the SELECT to certain number of records to reduce memory footprint.
I've heard that the Application class should not be used in this way necessarily (via Google Team). Instead you could make a separate Singleton class (For example call it "MyData") that holds a public static reference to a let's say ArrayList (For example called "list_data").
So when he activity starts it will call
MyData.list_data = new ArrayList<MyDataObj>();
Then in your Service you can add all of your data objects to this static arraylist like so:
MyData.list_data.add(new MyDataObj());
This allows you to work off of the same ArrayList without passing it around using Intents and etc. Although I would suggest doing some null checking whenevr
I have a ListView with a custom ArrayAdapter, that shows orders from an ArrayList. These orders are supposed to be synced with my server. There I have an API, which I am requesting for new orders, and if there are any, I get an XML of the order back. What is the best approach to dynamically update the listView? I do not want to use SQLite to store the orders, because only last ten are supposed to be displayed. With a ContentProvider I am not able to store my custom Order object. And if I wrap the ArrayList into a singleton class and use it in the service as well as in the Activity class for the ArrayAdapter, the ListView is not dynamically updated (probably, because the arrayAdapter makes a copy of the arraylist?). Thank you very much.
Filip
use Intent or Bundle
i'm no sure what you mean regarding the ArrayAdapter not being updated, but i can give you a solution we used in my company.
I have a DataMaanger which is a bridge between the Activities and the Networking or SQLite.
The dataMaanger keeps it's data in memory so it's not in DB or on disk. the disadvantage of it is if your app gets killed for lack of memory and reconstructs itself, the dataManager will be empty, which leaves you with two options, either on every Activitie's death or you main task's activities death you serialize your DataManager's data, or if you are not dependant on any previous data, just make arequest again and update the data manager.
I use broadcasts to notify my activities.
To get an access to the DataManager i don't use a sigletone. i use the Application object, you can extend it and in the Manifest.xml give it's name in the tag, then it will be used instead of the regualr Application object.
You can access it later by using getApplication() method in Activity class.