SharedPreferences and Thread Safety - android

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,

Related

Use SharedPreferences on multi-process mode

I've defined an instance of SharedPreferences that used on multi-process mode.
public class Prefs {
private static SharedPreferences prefs;
private static SharedPreferences.Editor editor;
private static void init(Context context) {
prefs = context.getSharedPreferences("alaki",
Context.MODE_MULTI_PROCESS);
editor = prefs.edit();
}
// static methods to set and get preferences
}
Now I'm using this class on a service with separate process and also in my main application process in static way.
Everything is going well, but sometimes all stored data on SharedPreferences instance removed!
How can I solve this problem?
Edit:
Finally I've solved my problem using by IPC.
There is currently no way of safely accessing SharedPreferences on multiple processes, as described in its documentation.
Note: This class does not support use across multiple processes.
After testing a lot with MODE_MULTI_PROCESS, I've three trials to share:
1- Initialize the SharedPreferences once in each process and use it multiple times.
The problem: The values are not reflected in each process as expected. So each process has its own value of the SharedPreferences.
2- Initialize the SharedPreferences in each put or get.
This actually works and the value now is interchangeable between processes.
The problem: sometimes after aggressively accessing the sharedpref, the shared preferences file got deleted with all its content, as described in this issue, and I get this warning in the log:
W/FileUtils﹕ Failed to chmod(/data/data/com.hegazy.multiprocesssharedpref/shared_prefs/myprefs.xml): android.system.ErrnoException: chmod failed: ENOENT (No such file or directory)
You can find why this happens in the issue.
3- Use synchronization to lock the methods that put and get values in the SharedPreferences.
This is completely wrong; synchronization doesn't work across processes. The SharedPreferences is actually using synchronization in its implementation, but that only ensures thread safety, not process safety. This is described very well here.
SharedPreferences itself is not process-safe. That's probably why SharedPreferences documentation says
Note: currently this class does not support use across multiple processes. This will be added later.
I've worked around this by combining:
Providing each process mutually-exclusive access to the SharedPreferences file (such as by using a socket-based locking mechanism)
Re-initialising the SharedPreferences with the MODE_MULTI_PROCESS flag every time you want to use it to bypass in-memory caching
This seems to work OK, but it hasn't been thoroughly tested in the real world, so I don't know if it's perfectly reliable.
You can see a working example I wrote here.
Warning: Looks like MODE_MULTI_PROCESS has been deprecated in Android M. It might stop working in the future.
Using the commit() method store the changes in persistent storage, hence it is slow and would make conflict across multiple call from other processes.
However there is an alternative to this method, you should call the apply() method, this method stores the changes in memory and then in disk storage asynchronously, so it is more reliable.
recalls that the use of context objects as static field, you have the risk of leakage of context because not declare the object in the application class
public class CustomApplication extends Application{
private Prefs prefs;
public void onCreate(){
prefs = new Prefs(this);
}
public Prefs getPrefs(){
return prefs;
}
}
From any context you can get the prefs
((MyApplication)context.getApplicationContext()).getPrefs();
Use a Content Provider which uses SharedPreferences. Example see here: https://github.com/hamsterksu/MultiprocessPreferences
public static int getValore(Context ctx, String contenitore, String chiave, int valore){
try {
SharedPreferences sh = ctx.getApplicationContext()
.getSharedPreferences(contenitore, Context.MODE_MULTI_PROCESS);
//SharedPreferences.Editor editor = sh.edit();
return sh.getInt(chiave, valore);
}catch (Exception ex){
return valore;
}
}
If two processes write data to SharedPreferences, then it might possible all SharedPreferences are reset to default values.
Also you can try to call clear() on the editor before storing val
SharedPreferences.Editor sp = settings.edit();
sp.clear();
sp.putString("Name", "YourName");
sp.commit();

How should I keep the sharing variables in android?

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).

How do I share common functions and data across many activities in a single android application

I am looking for how to share functions and data across multiple activities within a single application. I researched the daylights out of it and find some ideology war between overriding the extend for the application and doing a singleton, neither of which I can find examples sufficient to make me understand. Basically I want to share data and share functions. All activities need the same functions and data so this is not one activity sharing data with another activity. It is all activities needing to have access to the same functions and data.
What I want to know is what is the way to go and how do I do it. I need to see what I need to do in my 34 activities, what the class that is going to be common looks like, and what the Manifest entry needs to be. I also need to be sure the common data area will not be closed by the OS.
This is my first Android - Java program and now find my 15,000 line, 34 activity application needs some structure. I know, should have done things differently but the app works really well with two exceptions. One is that it is structurally a mess. Two is that the fact it is a mess is making it hard to fix one behavior I would like to fix.
This is a GPS based application for racing sailboats. It is timing critical and every activity basically runs a once a second loop inside the location manager onLocationChanged function. That part is fine and I do not want to put the GPS code in one place. The problem is that most activities need to filter the data so a lot of code is copied and pasted to the activities. The filter needs history so it needs to remember a state. There are other functions that are used by several activities so these have been copied as well. Think of a function that averages the last three GPS speed readings. It needs to save some history, do its thing, and give a result. All activities need to do the exact same thing. All this works but the problem is that the averaging starts over every time I switch activities because every activity has its own filter. That gives a glitch in the data that I need to get rid of. I need common place to save the data and hopefully a common place to run the filtering and other functions that are common. If every activity can call the filter function that is using common state data, there will be no glitch across activity changes.
I would appreciate some guidance.
Why you don't just make a Class with only static functions, passing needed Parameters? An example if you want to show an ErrorDialog
public class SharedHelper{
public static Dialog showErrorDialog(Context ctx, String message, String title, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener){
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setMessage(message).setTitle(tilte);
if (okListener != null){
builder.setPositiveButton(R.string.button_positive, okListener);
}
if (cancelListener != null){
builder.setNegativeButton(R.string.button_negative, cancelListener);
}
return builder.show();
}
}
Singletons are (from my point of view) one of the uglyest design pattern and will bite you sooner or later. Putting anything in Application requires you to cast it everytime to the Special Application class you designed. A class with only statics however is very flexible in its usage and doesn't need an instance to work.
For the storage-issue:
lookup "SharedPreferences" & "SQLite" and decide afterwards which storage-type suits your needs more.
For the methods-issue:
This question is a bit more complex and there are different ways to do it. For example you could write a parent-class that implements all your globally needed questions and you let all your activity-classes inherit from it.
public class MyParentActivity extends Activity {
public void myMethod() {
}
}
and:
public class Activity1of34 extends MyParentActivity {
myMethod();
}
I think what this comes down to is not an Android problem but an Object-Oriented Programming problem. If I understand the situation correctly, I'm betting the best solution would be to take your shared filter and create a new Filter class that is instantiated within each Activity (this is likely more manageable than a singleton, but not having seen your use case, it's hard to say for sure). If you need to centrally track the averaging, you can simply create a static variable within the Filter class that maintains the same value during the life of the application. If you really want to maintain that average (even past the application's current lifecycle), you can persist it in a database or other local data options. However, I don't see any reason to put everything in a singleton just to maintain that average. Singletons (and all static data structures) can be potentially troublesome if used incorrectly.
I, for one, do not mind the singleton pattern. Of course as everything else it should not be abused.
This is the construction I use for my shared objects. My app is divided into modules this way but can just as well be used in your case.
public class SharedDataObject {
private Context context;
private static SharedDataObject instance;
public static SharedDataObject getInstance() {
if (instance == null) throw new RuntimeException("Reference to SharedDataObject was null");
return instance;
}
public static SharedDataObject createInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new SharedDataObject(context.getApplicationContext());
}
// notice the constructor is private
private SharedDataObject(Context context) {
this.context = context;
}
...
public void myMethod() {
// do stuff
}
}
Notice that it uses the application context, that means among other things, means that the context owned by SharedDataObject cannot be used for GUI operations. But, the context will live for the entire lifetime of the application, which is nice.
Furthermore I hate having to pass a context everytime I wish to call methods on my SharedDataObject, thus I have a splashscreen calling SharedDataObject.createInstance() on all my modules.
Once an instance is create, I can call:
SharedDataObject.getInstance().myMethod();
Anywhere in my code, regardless of a context being present or not (from the place calling this code that is).

Is a "Globals" class holding static variables in Android safe?

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.

Android Application data should not be released by android OS

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.

Categories

Resources