Как я могу поделиться файлом SharedPreferences в двух разных приложениях для Android?

Я боролся с этим некоторое время. По сути, я хочу иметь два приложения (которые всегда будут устанавливаться вместе) с общими настройками, причем одно из них будет просто службой, которая работает в фоновом режиме и должна использовать настройки (должны владеть настройками, но только действительно< /em> должен их прочитать), а другое приложение является интерфейсным приложением пользовательского интерфейса, которое должно иметь возможность записывать в файл настроек, принадлежащий другому приложению. Служба будет работать в фоновом режиме (что может определяться настройками), а пользовательский интерфейс позволит пользователю редактировать настройки и просматривать некоторую информацию из службы. Однако это будут разные пакеты/приложения.

Я попытался следовать этому руководству, которое дало мне довольно хороший идея о том, как иметь настройки в одном приложении, которые могут быть прочитаны другим. По сути, я создаю новый контекст через myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);, а затем вызываю myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); Однако я не могу успешно записать настройки из внешнего приложения - (SharedPreferences.Editor).commit() возвращает false, и я получаю предупреждение в logcat о невозможности отредактировать pref_name.xml.bak.

Как я могу успешно настроить свои приложения, чтобы они оба могли читать и записывать в один и тот же файл настроек (который хранится в папке данных одного из них)?


person matt5784    schedule 14.06.2012    source источник
comment
Почему, когда вы говорите, что они всегда устанавливаются вместе, это должны быть два разных приложения? Похоже, это большая головная боль, поэтому, по-видимому, вы предвидите выгоду, но мне интересно, есть ли способ добиться этого, фактически не имея отдельных приложений.   -  person Chris Stratton    schedule 14.06.2012
comment
Я хотел иметь возможность установить оба приложения, задать настройки, а затем удалить пользовательский интерфейс внешнего интерфейса, но оставить службу в фоновом режиме. Это также позволяет мне обновить одно приложение без переустановки обоих. Это определенно не необходимо (и я могу даже не использовать это), но это была одна из моих идей для реализации этого так, как я это разработал.   -  person matt5784    schedule 14.06.2012


Ответы (2)


Для файла лучше установить приватный режим. Приложение должно быть подписано с тем же набором сертификатов, чтобы поделиться этим файлом.

Установите одинаковый sharedUserId в обоих приложениях.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hello"
android:versionCode="1"
android:versionName="1.0" 
android:sharedUserId="com.example">
....

Получить контекст из другого пакета:

mContext = context.createPackageContext(
                        "com.example.otherapp",
                        Context.MODE_PRIVATE);
 mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE);

Получите элементы, как обычно, из SharedPreference. Вы можете получить к нему доступ сейчас.

person Omer Cansizoglu    schedule 15.07.2014
comment
Не уверен, почему это не принятый ответ. Это правильный способ сделать это, и он работает безупречно. - person Miro Markaravanes; 06.12.2014
comment
Также вам, возможно, придется заменить Context.MODE_PRIVATE на Context.CONTEXT_RESTRICTED. - person Miro Markaravanes; 06.12.2014
comment
Это легко реализовать, однако, к сожалению, если вы добавите это на каком-то более позднем этапе разработки, обновление вашего приложения не сработает (придется переустанавливать). Кроме того, если вы используете биллинг в приложении, он, скорее всего, не будет работать. - person lenooh; 11.05.2015
comment
@Omer и Miro Как я могу успешно настроить свои приложения, чтобы они оба могли читать и записывать в один и тот же файл настроек (который хранится в папке данных одного из них)? Вы смогли добиться этого? - person Napolean; 16.07.2015
comment
Да, это возможно, если оба приложения подписаны одним и тем же сертификатом и объявлены в манифесте одинаковым идентификатором SharedUserID. - person Omer Cansizoglu; 20.07.2015
comment
@lenooh, я планирую принять этот способ, не могли бы вы пояснить, почему это будет препятствием для обновления приложения. Заранее спасибо. - person Bhupesh Pant; 31.10.2017
comment
@Наполеан, это тоже мое требование, и, пожалуйста, поделитесь своими мыслями по этому поводу, если у вас это получилось. - person Bhupesh Pant; 31.10.2017
comment
@BhupeshPant У меня это работает частично. Запись настроек прошла успешно в разных приложениях, но изменения не отразились сразу. Так что в моем случае тоже не сработало. - person Napolean; 01.11.2017
comment
Бхупеш Пант: Я где-то читал об этом. Давно это было, точно не помню. В любом случае, вы можете попробовать и посмотреть, как это работает (используйте альфа-версию). - person lenooh; 01.11.2017
comment
Исходя из следующего, определенно НЕ НОРМАЛЬНО изменять или добавлять sharedUserId после публикации приложения: stackoverflow.com/questions/3254157/ - person dazed; 31.12.2018
comment
Любая причина, по которой это больше не будет работать? @OmerCansizoglu Я не могу заставить его работать. Все значения возвращаются в зашифрованном виде. - person metamonkey; 03.11.2020

Во-первых, я должен отметить, что это официально не поддерживается, хотя может быть поддерживаемый способ сделать это (то есть это НЕ будет этот метод), добавленный в Android в будущем (источник обоих утверждений: см. второй абзац эту ссылку).

Опять же, это не поддерживается и, возможно, нестабильно. В первую очередь я сделал это в качестве эксперимента, чтобы увидеть, возможно ли это; будьте предельно осторожны, если планируете использовать этот метод в приложении.

Тем не менее представляется возможным совместно использовать предпочтения между приложениями, если выполняется несколько требований. Во-первых, если вы хотите, чтобы приложение B могло получить доступ к настройкам приложения A, имя пакета приложения B должно быть дочерним по отношению к имени пакета приложения A (например, приложение A: com.example.pkg приложение B: com.example.pkg.stuff). Кроме того, они не могут захотеть получить доступ к файлу одновременно (я предполагаю, что применяются те же правила, что и для доступа к ним между действиями, если вы хотите обеспечить атомарный доступ, вам придется использовать дополнительные меры безопасности, такие как .wait( ) и .notify(), но я не буду вдаваться в подробности здесь).

Примечание. Все это работает на эмуляторе версий 2.2 и 2.3.3. Я не проводил тщательного тестирования на разных устройствах или версиях Android.




Что нужно сделать в приложении, которому будут принадлежать настройки (приложение A сверху):

1.) Объявите файл SharedPreferences
Это довольно просто. Просто объявите пару переменных для вашего файла общих настроек и редактора в вашем классе и создайте их экземпляры в вашем методе onCreate. Теперь вы можете поместить в настройки строку, которую вы будете использовать, чтобы убедиться, что другое приложение может правильно ее прочитать.

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.) Настройте файл резервной копии. Появляется метод getSharedPreferences для проверки наличия файла .bak для загрузки настроек. Вот почему в документации сказано, что он не будет работать в нескольких процессах; чтобы свести к минимуму ввод-вывод, он загружает префы ОДИН РАЗ, когда вы их захватываете, и создает их резервные копии только тогда, когда вы закрываете свое приложение/активность. Однако, если вы вызовете это из внешнего приложения, вы получите предупреждение об отсутствии правильных прав доступа к файлу для папки (которая является папкой данных первого приложения). Чтобы исправить это, мы собираемся сами создать файл .bak и сделать его общедоступным для чтения/записи. Я решил сделать это так, чтобы определить три переменные в моем общем классе.

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"};

и создайте функцию в моем основном классе, которая будет принимать их в качестве аргументов и выполнять их

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) {}
}

Это не очень красиво или хорошо, но это работает. Теперь в вашем методе onCreate (сразу после editor.commit()) вы будете вызывать эту функцию с каждой из трех строк.

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

Это скопирует файл и сделает оба основных файла .xml и .xml.bak доступными для внешних программ. Вы также должны вызывать эти три метода в своем onDestroy(), чтобы убедиться, что база данных резервируется правильно, когда ваше приложение выходит, и дополнительно вызывать их непосредственно перед вызовом getSharedPreferences в другом месте вашего приложения (иначе он загрузит файл .bak, который вероятно, устарел, если другой процесс редактировал основной файл .xml). Это все, что вам нужно сделать в этом приложении. Вы можете вызвать getSharedPreferences в другом месте этого действия, и он соберет все данные из файла .xml, что позволит вам затем вызвать методы getdatatype("key") и получить их.


Действия при доступе к файлу(ам) (приложение B сверху)

1.) Запись в файл
Это еще проще. Я сделал кнопку для этого действия и настроил код в методе onClick, который сохранил бы что-то в файле общих настроек. Помните, что пакет приложения B должен быть дочерним по отношению к пакету приложения A. Мы будем создавать контекст на основе контекста приложения A, а затем вызывать getSharedPreferences в этом контексте.

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();

Это захватывает строку, которую я установил в другом приложении, и отображает ее (или сообщение об ошибке) в текстовом представлении в этом приложении. Кроме того, он устанавливает новую строку в файле настроек и фиксирует изменения. После этого, если ваше другое приложение вызовет getSharedPreferences, оно получит файл, включая изменения из этого приложения.

person matt5784    schedule 14.06.2012
comment
хотя это может произойти в будущем — использование двоичных файлов командной строки никогда не будет поддерживаться в Android SDK. Лично я бы не стал трогать эту технику 10-метровым шестом. Если вы хотите обмениваться данными между приложениями, используйте настоящий API, например, реализуйте ContentProvider, поддерживаемый вашим SharedPreferences. - person CommonsWare; 14.06.2012
comment
Я не мог предсказать, что сделает команда Android (кроме случаев, когда они говорят мне). Однако я совершенно уверен, что если бы они реализовали это, это было бы намного чище и не требовало бы запуска dd или chmod. На самом деле, это может быть даже возможно с помощью какой-то другой техники, и если это так, я надеюсь, что кто-нибудь опубликует ответ. Это именно то, как я заставил это работать. - person matt5784; 14.06.2012
comment
Возможно ли, чтобы два приложения записывали данные в одни и те же общие настройки? - person H S Progr; 12.02.2018