Reputation: 397
I am using SharedPreferences in Android to exchange simple data between 2 apps inside the same project. My purpose is having a 'configuration app' (app A) to save some values and make them accesible to app B through a shared file that is used by SharedPreferences class. App A must allow user to update values launching the app again and app B should see that changes.
All my apps are working as same one by declaring the following in both apps:
android:sharedUserLabel="@string/user_id"
android:sharedUserId="sos.app"
This allow my apps to use same data. So, Firstly I launch app A, then I introduce values and commit data to the file. Then I launch app B, I access to SharedPreferences via packageContent and I get my values properly.
My problem appears when I launch app A and app B again to update values. All changes are made correctly in app A (I saw changes in sharedPreferences) but when I launch app B again, it access to previous values instead of new ones.
It is quite strange. It looks like Android holds my sharedPreferences instance reference and when app B starts again, it does not go again to shared file through getSharedPreferences method.
This is related code in app A:
//Instance of SharedPreferences made in onCreate method
sharedPreferences = getSharedPreferences("my.configuration.file", Context.MODE_PRIVATE)
//Function to insert values in sharedPreferences
fun addConfigItemToSharedPreferences(itemToBeSaved: String){
with (sharedPreferences.edit()) {
putString(keyItemsList[configItemToBeAddedCounter], itemToBeSaved)
commit()
}
}
And this is related code in app B (inside onCreate method):
val packageContext = createPackageContext("com.televes.angrod.configurationapp", 0)
val sharedPreferences = packageContext.getSharedPreferences("my.configuration.file", Context.MODE_PRIVATE)
val mapOfPreferences: Map<String, *> = sharedPreferences.all
Is my approach correct or shall I use another mechanism to share data between apps?
Upvotes: 2
Views: 1626
Reputation: 397
I found my own solution!
Finally I decided to join all my apps to the same one. I just declared each activity in same manifest and then all of them are sharing same file directory and permissions. I also used a direct file access instead of shared preferences because of my problem with cached values. I also implemented a File Observer in order to get notified in each app about file changes:
class FileObserver(val context: MyWatchFaceSOSApp.Engine, absolutePath: String) : FileObserver(absolutePath, FileObserver.MODIFY) {
val TAG = "FileObserver"
override fun onEvent(event: Int, path: String?) {
//data was written to a file
if (FileObserver.MODIFY and event != 0) {
Log.i(TAG, "Wohoo. El archivo se ha actualizado!")
Log.i(TAG, "Iniciando actualización de parámetros del WATCHFACE")
context.cancelAndPurgePresenceReportService()
context.updateConfigurationParams()
context.setUpTimeBetweenPresenceReportMsgs()
context.startPresenceReportService()
}
}
}
So now I´m using a File Manager with to read/write functions. I found it in stack overflow and did my own changes. Just for sharing:
public class FileManagerUtility {
private static String TAG = "FileManagerUtility";
public static void writeSettings(Context context, Map<String, String> watchSettings) {
File appDirectory = new File(context.getFilesDir(), "watchsettings");
String file = "settings.txt";
if (!appDirectory.exists()) {
appDirectory.mkdirs();
}
File fileName = new File(appDirectory, file);
FileOutputStream fos = null;
ObjectOutputStream out = null;
try {
Log.i(TAG, "Escribiendo en fichero: " + fileName.getAbsolutePath());
fos = new FileOutputStream(fileName);
out = new ObjectOutputStream(fos);
out.writeObject(watchSettings);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
if (fos != null)
fos.flush();
fos.close();
if (out != null)
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
Log.i(TAG, "Se ha escrito correctamente en el fichero: " + fileName.getAbsolutePath());
Log.i(TAG, "Contenido del fichero: " + watchSettings.toString());
}
}
@SuppressWarnings("unchecked")
public static Map<String, String> readSettings(Context context) {
Map<String, String> watchSettings;
File appDirectory = new File(context.getFilesDir(), "watchsettings");
String file = "settings.txt";
if (!appDirectory.exists()) return null; // File does not exist
File fileName = new File(appDirectory, file);
FileInputStream fis = null;
ObjectInputStream in = null;
try {
Log.i(TAG, "Leyendo de fichero: " + fileName.getAbsolutePath());
fis = new FileInputStream(fileName);
in = new ObjectInputStream(fis);
Map<String, String> myHashMap = (Map<String, String> ) in.readObject();
watchSettings = myHashMap;
return watchSettings;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (StreamCorruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(fis != null) {
fis.close();
}
if(in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
And that´s it! I´m saving every configuration value in a Map to do quick access to data, which is also easy to read/write from files.
Hope it helps!
Upvotes: 0
Reputation: 203
Please use Content Providers to share data across apps. That's the recommended Android pattern: https://developer.android.com/guide/topics/providers/content-providers
Upvotes: 1
Reputation: 9378
Earlier you could solve this by changing MODE_PRIVATE to MODE_WORLD_READABLE or MODE_WORLD_WRITABLE but it has been deprecated now.
From official documentation (https://developer.android.com/training/data-storage/shared-preferences#GetSharedPreferences):
Caution: The MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE modes have been deprecated since API level 17. Starting with Android 7.0 (API level 24), Android throws a SecurityException if you use them. If your app needs to share private files with other apps, it may use a FileProvider with the FLAG_GRANT_READ_URI_PERMISSION. For more information, also see Sharing Files.
So, you have to use FileProvider now.
You can learn more about FileProvider here:
https://developer.android.com/reference/android/support/v4/content/FileProvider
Upvotes: 2