Как обмениваться данными в группе приложений в Android

Рассмотрим следующий сценарий. Компания выпускает множество приложений. И они хотят, чтобы некоторые данные были общими для всех этих приложений. Любое из этих приложений может создавать или читать эти данные, как обычная база данных. Поэтому компания решила создать для этой цели библиотеку Android. Я искал несколько дней, и мой анализ приведен ниже.

  1. SharedPreferences — не рекомендуется и устарел. Это тоже не служит цели. Все остальные приложения должны знать имя пакета приложения, которое создало данные для создания PackageContext. Здесь это нецелесообразно, так как любое приложение может создавать/обновлять/считывать данные и невозможно сказать, кто есть кто.

  2. ContentProviders - это не работает для меня. Причина в том, что ContentProviders должен присутствовать в каждом приложении. В устройстве не может быть 2 контент-провайдеров с одинаковым именем. В дополнение к этому ContentProviders в основном предназначены для того, чтобы одно приложение создавало данные, а другие приложения подписывались на него с помощью Content_Uri.

  3. Сетевое подключение. Мы не хотим хранить данные на каком-либо сервере.

  4. Внешнее хранилище — это единственный оставшийся вариант. Должен ли я пойти на это?

И что интересно, данные также должны быть защищены, что нигде не поддерживается ни одним из вариантов хранения.

Примечание. Для iOS мы используем связку ключей для реализации той же функциональности.


person Winster    schedule 30.10.2013    source источник
comment
Вы не используете удаленную базу данных?   -  person KOTIOS    schedule 30.10.2013
comment
если вы говорите о базе данных вне устройства, я не хочу. Я действительно хочу, чтобы данные хранились на устройстве максимально защищенным способом. Но я не против сохранности данных, если Android ничего не предоставляет.   -  person Winster    schedule 30.10.2013
comment
Отличный вопрос со слабыми ответами. Я борюсь с этим в течение нескольких недель и не нашел достойных ответов, даже после наблюдения за ~ 50 связанными вопросами. У меня также есть еще одно бремя, которое я хочу достичь с помощью реактивного натива. Я использую этот модуль, и он работает так, как я хочу для iOS с Keychain, но я не могу управлять им на Android.   -  person milkersarac    schedule 08.02.2017
comment
@milkersarac Ты смог решить свою проблему? Я тоже сейчас в той же лодке, что и вы в прошлом году.   -  person dariru    schedule 20.06.2018
comment
@dariru Я не был. Поэтому я записал данные в файл с шифрованием и прочитал их несколькими сторонами с общим ключом. Дела идут быстро, возможно, они могли бы решить проблему и для Android. Я надеюсь, что вы можете добиться этого лучше, чем я.   -  person milkersarac    schedule 20.06.2018


Ответы (3)


Понимание проблемы на Android

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

Тем не менее, существует множество способов обмена данными между приложениями без использования облака.

Общие настройки

В исходном вопросе говорится, что SharedPreferences устарели. Насколько я могу судить, это неправда, однако контексты MODE_WORLD_READABLE и MODE_WORLD_WRITABLE устарели, что делает этот подход неработоспособным в будущем. Однако этот режим уже давно устарел — начиная с Android 4.2 (2012 г.). В текущих документах по Android нет никаких угроз, чтобы предположить, что они фактически отказываются от него (иногда устаревание просто означает «это не очень хорошая идея», а не «это будет удалено»). Я подозреваю, что отсутствие более безопасной прямой альтернативы на уровне ОС для обмена данными приложения на уровне настроек, вероятно, является причиной того, что он оставался в состоянии устаревания в течение последних 5 лет.

Доступ к файлам

Самый простой и наиболее распространенный известный мне способ реализации обмена данными между приложениями на Android — это просто запросить доступ к файлу на устройстве и создать общее расположение на внешнем хранилище для этих данных. (Не путайте с обозначением «внешнее хранилище» — именно так Android относится к общим данным. Это не обязательно относится к SD-карте.) Вы даете файлу уникальное имя и сохраняете его где-нибудь, чтобы ваши приложения знают, где его искать. Лучший способ получить этот путь: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)

Очевидная проблема с этим - безопасность. Несмотря на то, что ОС не объявляет его устаревшим, он создает ту же проблему, которая указана в документах Android как причину отказа от MODE_WORLD_* — он по своей сути небезопасен и открывает потенциальные уязвимости в вашем приложении.

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

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

Создание собственной службы

Вы всегда можете создать Service или IntentService. (Между ними есть тонкие различия, но IntentService — это подкласс Service, который работает в рабочем потоке, в то время как Service прерывает основной поток. IntentService также реализует поддержку Intent, которая обеспечивает наиболее прямое взаимодействие между приложениями на Android).

У этой службы есть собственное частное хранилище, для которого она имеет полный доступ на чтение/запись, но больше ничего. Затем эта служба предоставляет интерфейс для получения намерений от других приложений и для возврата результатов (в виде намерений) этим приложениям. Это чрезвычайно удобный способ реализации данных между приложениями, при этом максимально повышая конфиденциальность и безопасность этих данных. Если удаленным приложениям в основном нужно запрашивать очень простую информацию из центрального приложения, это вариант начального уровня.

Реализация BroadcastReceiver

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

Создание ContentProvider

Исходное сообщение, похоже, неправильно понимает, что такое ContentProvider и как он работает. Вы должны думать об этом типе элемента, как об облачном решении, даже если оно является локальным для вашего устройства. Каждому приложению не нужен ContentProvider — все они должны взаимодействовать с ContentProvider, и этот ContentProvider поддерживает, обновляет и возвращает данные.

Это, вероятно, наиболее подходящее для Android решение для данного конкретного случая использования, которое предлагает наибольшую расширяемость. Вы реализуете независимый процесс, который обрабатывает хранение данных и отвечает другим приложениям. Это, однако, более развитое решение и, как таковое, может быть более сложной задачей. Если вам нужна настоящая служба базы данных, а не довольно простая служба типа запрос/ответ, ContentProvider кажется лучшим выбором.

person EddieOffermann    schedule 26.02.2017

Похоже, вам нужен общий идентификатор пользователя.

Это позволяет использовать изолированную программную среду приложения для нескольких приложений Android, если все они подписаны одной и той же подписью.

Но следите за подводными камнями!

person Eugene Loy    schedule 30.10.2013
comment
как общий идентификатор пользователя поможет здесь? Я все еще не могу использовать SharedPreferences с MODE_PRIVATE. Есть ли другой способ справиться с этим сценарием? - person Winster; 31.10.2013
comment
SharedUserId устарел на уровне API 29. - person JasonJotted; 19.10.2020

да. Вероятно, лучше всего использовать один и тот же путь во внешнем хранилище для всех приложений. Общая часть кода может использоваться, чтобы узнать, существует ли база данных или нет, и, следовательно, открыть или создать новую. В целях безопасности я рекомендую всегда использовать «пользователя» и «пароль» при подключении к БД, но если вы считаете, что этого недостаточно, я советую вам увидеть это: http://www.hwaci.com/sw/sqlite/see.html

person Emisilve86    schedule 30.10.2013
comment
Похоже, что добавление шифрования/защиты паролем бесполезно в сценарии OP. Нет ничего, что могло бы помешать мне, злонамеренному разработчику стороннего приложения, от обратного проектирования приложения OP, чтобы получить эти пароли / ключи, чтобы я мог позже использовать их для расшифровки (и возиться с ней так, как я хочу) база данных, которая хранится на внешнее хранилище, как вы предлагаете. С другой стороны, при использовании общего идентификатора пользователя я не смогу получить доступ к песочнице, используемой приложениями OP (и, следовательно, к данным), если мое приложение не подписано подписью OP (что не так, потому что эта подпись должна храниться в секрете). - person Eugene Loy; 31.10.2013
comment
Если только этот пароль не будет предоставлен позже первоначальным разработчиком всем его аутентифицированным клиентам по безопасному соединению, такому как SSL. Да, пароль в источниках ставить не надо, это очевидно. Шифровать данные с помощью AES-128 быстро и безопасно, принудительно использовать AES с открытым текстом и зашифрованным текстом так же сложно, как пытаться подобрать 2 ^ 128-битный пароль методом грубой силы. - person Emisilve86; 31.10.2013
comment
Я могу лишь частично согласиться. Если этот пароль позже не будет предоставлен первоначальным разработчиком всем его аутентифицированным клиентам по безопасному соединению [...], подразумевается, что существует серверная часть, на которой хранятся ключи/пароли и некоторые средства аутентификации клиента. Судя по тому, что сказал OP, это (хранение данных на сервере) нежелательно в его сценарии. Однако я могу вспомнить один случай, когда шифрование может быть полезным. Если ключ/пароль для базы данных получен от самого пользователя (т.е. если в общем месте хранится зашифрованная база данных, и каждое из приложений OP запрашивает у пользователя ключи для этой базы данных) - person Eugene Loy; 31.10.2013
comment
... в этом случае не будет необходимости в серверной части. - person Eugene Loy; 31.10.2013
comment
Я предполагаю, что приложения загружаются только от определенных лиц (т.е. тех, кто за это платит), что означает, что если у вас есть мое приложение, вы и только вы можете получить MAC, аутентификация разрешена. Требуется только одно соединение при первом запуске приложения таким образом, чтобы получить пароль, и это отличается от хранения данных на сервере. Ключ будет сохранен локально в приватном режиме, и никакое другое приложение не сможет его прочитать. Но это только мое представление о том, как это приложение должно работать... Хосе может использовать любой подход, который он предпочитает. - person Emisilve86; 31.10.2013
comment
Как насчет того, чтобы использовать отдельное приложение для хранения/извлечения этих данных? Все остальные приложения могут вызывать это приложение для создания данных, а с помощью поставщика контента мы также можем получать данные. Но я хотел бы избежать любого пользовательского интерфейса из этого (библиотека) приложения. Как насчет того, чтобы сделать фоновый сервис? - person Winster; 31.10.2013
comment
Отдельное приложение?! Если требуется много операций чтения или записи, этот подход должен снизить производительность. Сервер SQLite - просто хороший сервер для этого. Вы можете использовать сервисы для всех приложений, которые должны выполнять чтение/запись в фоновом режиме, когда их действия находятся в состоянии onPause(). Только одно действие в данный момент находится на переднем плане и прослушивает события, и если ваш сценарий предусматривает, что это взаимодействие строго связано с действиями чтения/записи, вам не требуется дальнейшее обслуживание между приложением и сервером БД. - person Emisilve86; 31.10.2013
comment
Скорее, я бы использовал библиотеку для импорта, которая запускает новый поток, когда приложение переходит в onResume(), и завершает работу, когда приложение переходит в onPause(), параллельный поток, который получает сообщения в обработчике FIFO через собственный Looper и выполняет любые действия, которые вы нужно перенаправить сервер БД. Никакой траты ресурсов, пока активность спит. - person Emisilve86; 31.10.2013