Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Примечание: все это работает на эмуляторе на 2.2 и 2.3.3. Я не тестировался на разных устройствах или версиях Android.




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

1.) Объявите файл SharedPreferences
Это довольно просто. Просто объявите пару переменных для файла 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 для загрузки предпочтений. Вот почему в документации говорится, что он не будет работать через несколько процессов; чтобы минимизировать ввод-вывод, он загружает prefs ONCE, когда вы их захватываете, и только поддерживает их при закрытии приложения/активности. Однако, если вы вызываете это из внешнего приложения, вы получите предупреждение о том, что у него нет прав доступа к папке (которая является первой папкой данных приложения). Чтобы исправить это, мы собираемся сами создать файл .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 ( "ключ" ) и получить их.


Что делать в файлах доступа (приложение B сверху)

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

prefsbutton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        Context myContext = null;
        try {
            // App A 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, оно будет извлекать файл, включая изменения из этого приложения.

Ответ 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. Теперь вы можете получить к нему доступ.