public class MYApplication extends Application {
String property;
setter getter
}
does above code make sure property will not be collectied by android OS if not used for a long period of time.
No. Android reserves the right to kill any application at any time if it feels the need. Even foreground processes can be killed if the device gets low on memory. This means the Application object will be destroyed and all its attributes lost.
The only way to ensure your application's transient state never gets lost is to respond appropriately to the lifecycle events Android offers, or just store values persistently.
If you want to store a String for your application why not use Preferences? This means the value would be never be lost, even if the device is switched off.
private static final String PREFERENCE_MYSTRING = "mystring";
static String getProperty(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getString(PREFERENCE_MYSTRING, "");
}
static void setProperty(Context context,String value) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putString(PREFERENCE_MYSTRING, value)
.commit();
}
(I'm not quite sure what getter and setter are in your code sample, but I don't think that's relevant.)
As I understand it, it's central to override onDestroy() if you want to prevent Android OS from killing your process.
Related
I'm having trouble understanding the problem in this scenario.
I have a class used for Analytics Tracking, lets call it FlurryTracker,
it has 2 methods StartTrackingScreen(ScreenName) and StopTrackingScreen().
Now if I have a static var called screenName and each time start tracking screen is called screenName is reassigned.
startTrackingScreen(activity: Activity, screen: DhTracker.Screen<T>) {
screenName = screen.getName()
val lastScreen = Singleton.getLastScreen()
//If last screen is not same as current screen
FlurryAgent.logEvent(screenName, true)
}
}
override fun stopTrackingScreen() {
//New screen will start tracking before lastScreen tracking is stopped.
if (enabled) {
FlurryAgent.endTimedEvent(Singleton.getLastScreen()?.getName())
}
}
companion object{
lateinit var screenName : String
}
These methods are called in onStart() and onStop() in the app itself.
So with that being said, we are only tracking 1 screen at a time because when the user transitions to a new screen, onStop() and onStart() will be called.
So even though screenName is static, every time the lifecycle methods are called, that static var is being reassigned. Since a phone can't have 2 activites running at the same time, there will only be 1 instance of my tracker active at a time.
I don't really see the problem with using a static here, although I know best practices say that screenName should be created with every new instance. Am I looking at this the wrong way?
You can do that.
The two main patterns for something like this are static variables & methods or a singleton (That is often static so you can use it from different paths without passing it around). Both of these approaches are functionally identical.
The negatives for the static class are:
Difficult to test due to the fact that you have to replace the static method
Difficult to create a second instance
Difficult to pass around if you decide you want to (Some people like to know what classes are used by a given path for testing purposes)
These aren't that bad, you can live with them--however none of these issues exist if you use a singleton. You can easily pass it around, change it's behavior, convert it to use injection instead of the singleton pattern, …
So I can't really come up with a reason to ever use the static approach, there aren't any advantages.
I'm part of an Android project that uses a global class that contains public static variables, like the example below:
public class Globals {
public static MyObject variable = "this is a test";
}
In the middle of using the application, this global variable's value will be changed let's say:
#Override
public void onCreate(Bundle savedInstanceState) {
Globals.variable = new MyObject(somethingHere);
}
And I came across using Headless Retained Fragments from these blog posts:
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
http://www.vogella.com/tutorials/AndroidFragments/article.html#headlessfragments
Problem:
I need to have a reference of an initialized object all over the application.
Question:
which is better to implement, a good practice, and practical? Or is there another way I could store a reference of an object all over the application?
The global class has been used over the project, and works fine, but are there any downsides of this implementation?
are there any downsides of this implementation?
Memory leaks, depending on what you are storing there. Leaks are why static data members are considered poor form in classic Java, even if we tend to use them more in Android app development.
Also, this data needs to be considered a cache, one that has to be able to be lazy-created on first access. Your process can be terminated at any point (wiping out the static data member) and the user can return to any activity in your app (courtesy of the recent-tasks list). If you want this data to survive process termination, the most likely solution will be to persist it in a file, database, or SharedPreferences.
All that being said, a retained fragment is a per-activity solution, not a per-application solution, and so it is not a valid alternative for your scenario AFAICT.
The work flow of my program is:
Launch app
Splash screen, check the server api, from the api get a list of file name
Download some of the file in file list , remove the downloaded file name from the list
App opened
when the download is finished , jump to main page that will start download another file in the list
The problem is , the list I was keep in the download manager , when I select don't leave activities in android setting , it will be killed. If I need a class that is some Data Class , that means I put a share data (A several hash map , array list) in it, and it keep updating (delete after async download finish) , and it never get killed. How can it be done? Thanks
The more general problem you are encountering is how to save state across several Activities and all parts of your application. A static variable (for instance, a singleton) is a common Java way of achieving this. I have found however, that a more elegant way in Android is to associate your state with the Application context. As you know, each Activity is also a Context, which is information about its execution environment in the broadest sense. Your application also has a context, and Android guarantees that it will exist as a single instance across your application. The way to do this is to create your own subclass of android.app.Application, and then specify that class in the application tag in your manifest. Now Android will automatically create an instance of that class and make it available for your entire application. You can access it from any context using the Context.getApplicationContext() method (Activity also provides a method getApplication() which has the exact same effect):
class MyApp extends Application {
private String myState;
public String getState() {
return myState;
}
public void setState(String s) {
myState = s;
}
}
class Blah extends Activity {
#Override public void onCreate(Bundle b) {
...
MyApp appState = ((MyApp) getApplicationContext());
String state = appState.getState();
...
}
}
This has essentially the same effect as using a static variable or singleton, but integrates quite well into the existing Android framework. Note that this will not work across processes (should your app be one of the rare ones that has multiple processes).
Can anyone enlighten me about the safety of a class holding global values in Android?
Here's a short example of what I mean:
public class Globals {
public static int someVariable = 0;
public static User currentUser = null;
public static Handler onLogin = null;
}
Then somewhere in an Activity I do the following:
Globals.someVariable = 42;
Globals.currentUser = new User("John", "Doe");
I have to rely on Globals.currentUser at multiple places in my app as soon as the user is logged in, but I'm unsure if I should do it, and also if I could use a Handler like this.
I read everywhere that an Android app could be killed anytime, does this mean it is killed completely or maybe just a part of it, thus killing my Globals class only?
Or is there any other way to store globally available data in a safe way, without writing every member change to the database (in fact, my User class is a little more complex than in this example. ;-)
Thanks for your effort!
Edit: Ok, here's what I finally did:
public class MyApp extends Application {
private static MyApp _instance;
public MyApp() {
super();
_instance = this;
}
public static MyApp getContext() {
return _instance;
}
....
private User _user = null;
public User getUser() {
if (_user == null) _user = new User();
return _user;
}
}
Then modify the AndroidManifest.xml and add android:name=".MyApp" to your application node to tell the app to use your subclass.
So far everything works fine and I can easily access the current Context (f.ex. in SQLiteOpenHelper) by calling MyApp.getContext().
It would be better to use the Android Application class. It's meant to store global application state
http://developer.android.com/reference/android/app/Application.html
Just create a subclass and make sure to update your manifest file to use your version. Then you can store whatever you need to in it. Activities have a method getApplication() which you can cast to your class to access your implementation
The pattern is discouraged--you will run into problems when unit testing.
Can you explain how you unit-test a class that must supply different custom "Users" here? You are either forcing a mock/fake class into "User" which will probably have a cross-effect on other tests or you are putting an if(test) into your code which gets ugly quick.
Over time populating this class artificially for testing gets more complex and starts to have relationships and dependencies.
More simply it makes it difficult to unit test a class in isolation.
It's one of those patterns that a given programmer either doesn't see a problem with or never uses because he's been burnt--you'll see little middle ground.
Looking at the SharedPreferences docs it says:
"Note: currently this class does not
support use across multiple processes.
This will be added later."
So in and of itself it doesn't appear to be Thread Safe. However, what kind of guarantees are made in regards to commit() and apply()?
For example:
synchronized(uniqueIdLock){
uniqueId = sharedPreferences.getInt("UNIQUE_INCREMENTING_ID", 0);
uniqueId++;
sharedPreferences.edit().putInt("UNIQUE_INCREMENTING_ID", uniqueId).commit();
}
Would it be guaranteed that the uniqueId was always unique in this case?
If not, is there a better way to keep track of a unique id for an application that persists?
Processes and Threads are different. The SharedPreferences implementation in Android is thread-safe but not process-safe. Normally your app will run all in the same process, but it's possible for you to configure it in the AndroidManifest.xml so, say, the service runs in a separate process than, say, the activity.
To verify the thready safety, see the ContextImpl.java's SharedPreferenceImpl from AOSP. Note there's a synchronized wherever you'd expect there to be one.
private static final class SharedPreferencesImpl implements SharedPreferences {
...
public String getString(String key, String defValue) {
synchronized (this) {
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
...
public final class EditorImpl implements Editor {
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
...
}
}
However for your case of the unique id it seems you'd still want a synchronized as you don't want it to change between the get and the put.
I was wondering the same thing - and came across this thread that says they are not thread safe:
The implementations of Context.getSharedPreferences() and Editor.commit
() do not synchronize on the same monitor.
I have since looked at the Android 14 code to check, and it is quite involved. Specifically SharedPreferencesImpl seems to use different locks when reading & writing to disk:
enqueueDiskWrite() locks on mWritingToDiskLock
startLoadFromDisk() locks on this, and launches a thread locking on SharedPreferencesImpl.this
I'm unconvinced that this code really is safe.
I think that will do it.
You can test it using sleep inside the synchronized section and call it from different threads
You should be aware that SharedPreferences are not working on Samsung handsets, have a look at android issue.
I have implemented simple database preferences storage which you can find on github.
Cheers,