How to Share a Sharedpreferences File Across Two Different Android Apps

How can I share a SharedPreferences file across two different android apps?

First, I should note that this is not officially supported, although there may be a supported way to do this (i.e. it would NOT be this method) added to Android in the future (source for both claims: see second paragraph of this link).

Again, this is unsupported and is very possibly unstable. I primarily did this as an experiment to see if it was possible; take extreme caution if you are planning to actually incorporate this method into an application.

However, it appears to be possible to share preferences between applications if a few requirements are met. First, if you want App B to be able to access App A's preferences the package name of App B must be a child of App A's package name (e.g. App A: com.example.pkg App B: com.example.pkg.stuff). Additionally, they can't be wanting to access the file at the same time (I assume the same rules apply as for accessing them between activities, if you want to ensure atomic access you'll have to use additional safeguards such as .wait() and .notify(), but I won't go into that here).

Note: all of this works on the emulator on 2.2 and 2.3.3- I haven't extensively tested across devices or android versions.


Things to do in the app which is going to own the preferences (App A from above):

1.) Declare the SharedPreferences file
This is fairly simple. Simply declare a couple variables for your sharedpreferences file and the editor in your class and instantiate them in your onCreate method. You can put a string in the preferences now that you will use to make sure the other app can read it properly.

public class stuff extends Activity {
SharedPreferences mPrefs = null;
SharedPreferences.Editor mEd= null;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = (getApplicationContext()).getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
mEd = mPrefs.edit();
mEd.putString("test", "original send from prefs owner");
mEd.commit();

2.) Set up the backup file
The getSharedPreferences method appears to check for a .bak file to load the preferences from. This is why it says in the documentation that it will not work across multiple processes; to minimize I/O, it loads the prefs ONCE when you grab them and only backs them up when you close your application/activity. However, if you call this from an outside application you will get a warning about not having the right file permissions for the folder (which is the first app's data folder). To fix this we are going to create the .bak file ourselves and make it publicly readable/writable. The way I chose to do this was to define three variables in my overall class.

final String[] copyToBackup = { "dd", "if=/data/data/com.example.pkg/shared_prefs/prefs.xml", "of=/data/data/com.example.pkg/shared_prefs/prefs.xml.bak", "bs=1024" };
final String[] mainFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml"};
final String[] bakFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml.bak"};

and make a function in my main class which would take these as arguments and execute them

public void execCommand(String[] arg0){
try {
final Process pr = Runtime.getRuntime().exec(arg0);
final int retval = pr.waitFor();
if ( retval != 0 ) {
System.err.println("Error:" + retval);
}
}
catch (Exception e) {}
}

It's not terribly pretty or good but it works.
Now, in your onCreate method (right after editor.commit())you will call this function with each of the three strings.

execCommand(copyToBackup);
execCommand(mainFixPerm);
execCommand(bakFixPerm);

This will copy the file and make both the main .xml and the .xml.bak files accessible to outside programs. You should also call these three methods in your onDestroy() to make sure the database is backed up properly when your app exits, and additionally call them right before you call getSharedPreferences elsewhere in your application (as otherwise it will load the .bak file which is likely out of date if another process has been editing the main .xml file). That's all you need to do in this application though, though. You can call getSharedPreferences elsewhere in this activity and it will grab all the data from the .xml file, allowing you to then call the getdatatype("key") methods and retrieve it.

Things to do in the accessing file(s) (App B from above)

1.) Write to the file
This is even simpler. I made a button on this activity and set up code in it's onClick method which would save something to the shared preferences file. Remember that App B's package must be a child of App A's package. We will be creating a context based on App A's context and then calling getSharedPreferences on that context.

prefsbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context myContext = null;
try {
// App A's context
myContext = createPackageContext("com.example.pkg", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
} catch (NameNotFoundException e) {e.printStackTrace();}

testPrefs = myContext.getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
testEd = testPrefs.edit();
String valueFromPrefs = testPrefs.getString("test", "read failure");
TextView test1 = (TextView)findViewById(R.id.tvprefs);
test1.setText(valueFromPrefs);
testEd.putString("test2", "testback");
boolean edit_success = testEd.commit();

This grabs the string I set in the other application and displays it (or an error message) in a textview in this application. Additionally it sets a new string in the preferences file and commits the changes. After this runs, if your other application calls getSharedPreferences it will retrieve the file including the changes from this app.

Using SharedPreferences to share data between two separate Android applications

even if shared preferences could be shared between process (see @CommonsWare answer..)
then it sounds like the most poor design solution for the problem you describes.
in fact, it smells like horrible idea, and I'm sure eventually it won't work anyway!

saying SharedPreferences is solution to communication between to different apps/ process in android, is like completely ignore all android API, and core components!

SharedPreferences not designed to be some kind of message queue between process.
not even close to that!

android provides much more elegant solutions for communicating between different apps, and sharing data between them

for example:

  • remote Service binding (app1 starts service which app2 can bind to)
  • sending broadcast from one app to another when some event accures and rceive it from the other app with BroadcastReceiver
  • app1 can implement and expose ContentProvider which can be accessed from app2

and there's more!

I suggest you better understand android's core components (Service , BroadcastReceiver, Activity, ContentProvider) before you get any decision how to implement your apps.
I can't imagine a way to create good functional application without using at least 3 of the above.
you can varify that with reading the first page written in Android developers getting started guide - http://developer.android.com/guide/components/fundamentals.html

links:

http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/reference/android/content/BroadcastReceiver.html
http://developer.android.com/reference/android/content/ContentProvider.html
http://developer.android.com/guide/components/bound-services.html

Android: Retrieving shared preferences of other application

Assuming the preference are WORLD_READABLE, this might work:

final ArrayList<HashMap<String,String>> LIST = new ArrayList<HashMap<String,String>>();
// where com.example is the owning app containing the preferences
Context myContext = createPackageContext("com.example", Context.MODE_WORLD_WRITEABLE);
SharedPreferences testPrefs = myContext.getSharedPreferences("test_prefs", Context.MODE_WORLD_READABLE);
Map<String, ?> items = testPrefs .getAll();
for(String s : items.keySet()) {
// do something like String value = items.get(s).toString());
}


Related Topics



Leave a reply



Submit