Вопрос по sharedpreferences, android-context, android-intent, android – Как я могу поделиться файлом SharedPreferences между двумя разными приложениями для Android?

18

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

Я пытался следоватьэтот урок что дало мне довольно хорошее представление о том, как иметь предпочтения в одном приложении, которые могут быть прочитаны другим. По сути, я создаю новый контекст через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.

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

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

Ваш Ответ

2   ответа
6

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

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.

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

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
Это довольно просто. Просто объявите пару переменных для вашего файла 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.) Set up the backup file Появляется метод 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 (& quot; ключ & quot;) и извлечь их.


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

1.) Write to the file
Это еще проще. Я сделал кнопку для этого действия и настроил код в его методе onClick, который сохранит что-то в файле общих настроек. Помните, что пакет App B должен быть дочерним по отношению к пакету App 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, оно извлечет файл, включая изменения из этого приложения.

Возможно ли для двух приложений записывать данные в одни и те же общие настройки?
Я не мог предсказать, что будет делать команда Android (кроме случаев, когда они скажут мне). Тем не менее, яquite конечно, если бы они реализовали это, это было бы намного чище и не потребовало бы запускаdd или жеchmod, На самом деле, это может быть даже возможно с помощью другой техники - и если это так, я надеюсь, что кто-то опубликует ответ. Это просто способ, которым я заставил это работать. matt5784
"хотя это может произойти в будущем" - использование двоичных файлов командной строки никогда не будет поддерживаться в Android SDK. Лично я не стал бы касаться этой техники 10-метровым шестом. Если вы хотите обмениваться данными между приложениями, используйте реальный API, такой как реализацияContentProvider что поддерживается вашимSharedPreferences.
42

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

Установите 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. Вы можете получить к нему доступ сейчас.

Не уверен, почему это не принятый ответ. Это правильный способ сделать это, и работает без нареканий.
Да, это возможно, если оба приложения подписаны с одним и тем же сертификатом и объявили один и тот же sharedUserID в манифесте.
@Omer и Miro Как я могу успешно настроить свои приложения, чтобы они оба могли читать и записывать в один и тот же файл настроек (который хранится в папке данных одного из них)? Удалось ли вам достичь этого?
Также вам, возможно, придется заменитьContext.MODE_PRIVATE сContext.CONTEXT_RESTRICTED.
Это легко реализовать, однако, к сожалению, если вы добавите это на более позднем этапе разработки, обновление вашего приложения не будет работать (вам придется переустанавливать). Кроме того, если вы используете биллинг внутри приложения, он, скорее всего, не будет работать.

Похожие вопросы