1

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

Я следовал документации, поэтому провайдер работает большую часть времени, но у меня есть проблемы с некоторыми приложениями, а именно:

1) Gmail прикрепляя файл из моего провайдера: Первоначально мой общедоступный ParcelFileDescriptor OpenDocument было что-то вот так:

ParcelFileDescriptor[] pipe=null; 
pipe=ParcelFileDescriptor.createPipe(); 
OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]); 
new TransferThread(file_id,out).start(); 
return pipe[0]; 

но это никогда не работало с Gmail. У меня была ошибка сломанной трубы. Он работает, если я сначала загружаю файл в тот же метод (блокирование).

Оригинал работал с большинством других приложений

2) Blackberry Hub - Я не могу заставить его работать в любом случае, как это, кажется, вызов методов поставщика на основном потоке

Я бы отказаться, но Я вижу, что провайдер Dropbox работал со всеми приложениями, включая BB-хаб.

Еще хуже :) кажется, что dropbox может показывать собственный пользовательский интерфейс при загрузке, например.

Любая идея, как это можно сделать?

Обратите внимание, что я неоднократно проверял документацию и несколько примеров, но по-прежнему не могу заставить провайдера работать со всеми приложениями, я знаю, что некоторые из приложений могут неправильно использовать, но dropbox доказывает, что можно встретить все apps

+0

попробовал 'ContentProvider # openPipeHelper'? это упростит – pskink

+0

или отправьте код 'TransferThread', если вы не хотите использовать' openPipeHelper' – pskink

ответ

0

Ваша проблема с Gmail, скорее всего, связана с жизненным циклом ContentProvider.

ContentProvider на самом деле просто IPC Binder, очень похожи на те, которые обычно возвращаются из Service#onBind. Но вместо привязки к вашему приложению клиенты получают его косвенно через ContentResolver каждый раз, когда они делают запрос. Обычно нет явной отмены привязки, система Android кэширует поставщика в течение короткого времени после завершения каждого запроса IPC.

К сожалению, скрытый характер неявной привязки ContentProvider означает, что нет возможности немедленно освободить поставщика. Хуже того, нет способа справиться с ошибками в багги-провайдерах - , если ваш ContentProvider сработает перед возвратом Cursor или ParcelFileDescriptor, вызывающее приложение немедленно спустится с вами! Google, очевидно, знает об этом, поэтому они создали другой API для взаимодействия с неверно доверенными сторонними ContentProviders - ContentProviderClient. Обратите внимание, что ContentProviderClient содержит оба способа обработки удаленных исключений и процесса смерти и способ явно закрыть ContentProvider.

Теперь представьте себе гипотетическую рабочий процесс Gmail ContentProvider:

ParcelFileDescriptor fd = null; 

try (ContentProviderClient c = resolver.acquireUnstableContentProviderClient(...)) { 
    fd = c.openFile(...) 
} catch (Exception ohThoseBuggyProviders) { 
    ... 
} 

// here ContentProvider is already closed 

if (fd != null) { 
    // use the received descriptor to create email attachment 
    ... 
} 

Но что, если ваш ContentProvider хочет жить немного дольше, чтобы прочитать остальную часть файла с сервера в фоновом потоке? Ну, система, скорее всего, уничтожит ваш процесс, потому что он не знает, что вы этого хотите. Ваш процесс умирает, Gmail получает ошибку «сломанный канал».

Вот почему вы не должны создавать новые потоки или использовать ContentProvider#openPipeHelper (почему этот метод существует?), просто выполните всю свою работу в вызывающем потоке.


Ответ на вторую часть вашего вопроса также касается внутренних компонентов ContentProvider. Когда ваш провайдер вызывается из основного потока вызывающего приложения, ваш код не выполняется в основном потоке вашего процесса - он выполняется в пуле потоков Binder, как обычно. Но, чтобы сделать жизнь программистов проще, Android занимает несколько шагов, чтобы сделать это менее очевидно:

  1. Приоритет вашей нити установлен приоритет нити, что делает вызов (в том числе повышение приоритету пользовательского интерфейса, если вы получаете вызванный из потока пользовательского интерфейса).
  2. Android passes Текущие настройки Strict Mode (вещи, которые приводят к сбою приложений при работе в основном потоке) из вызывающего приложения в ваш поток. По завершении вызова все нарушения Strict Mode собираются, и written to Parcel отправляется обратно в вызывающее приложение.

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

Вы должны быть в состоянии избавиться от этой неприятной «помощи», используя android.os.StrictMode, но это не будет сделано, если файлы, о которых идет речь, слишком велики (ANR может все еще произойти в вызывающем процессе). Вместо того, чтобы загружать файлы в канал, возвращайтесь с openDocument a ParcelFileDescriptor for socket.

Почему Dropbox не страдает от этой проблемы? Поскольку Dropbox Core написан на C++, а Android Strict Mode в настоящее время является конструкцией только для Java, он не вписывается в собственный код. Если вы пишете на диск или загружаетесь из сети в основной поток, используя вызовы библиотеки C, ваше приложение не будет получать никаких последствий (кроме ANR, который запускается в потоке пользовательского интерфейса независимо от Strict Mode).

+0

'' Но что, если ваш ContentProvider хочет жить немного дольше, чтобы прочитать остальную часть файла с сервера в фоновом потоке? Ну, система скорее всего, убьет ваш процесс, потому что он не знает, что вы этого хотите. Ваш процесс умирает », так вы говорите, что вы не можете использовать« Курсор », возвращаемый методом« CP # query », в течение более длительного времени, потому что процесс, в котором размещается этот CP, будет убит? если нет (он не будет убит), какая разница между возвращаемыми 'Cursor' и' PFD'? – pskink

+0

@pskink Для одного, курсор из ContentProvider, является [интерфейсом AIDL сам по себе] (https://github.com/android/platform_frameworks_base/blob/4b1a8f46d6ec55796bf77fd8921a5a242a219278/core/java/android/database/CursorWindow.aidl). ParcelFileDescriptor [немного проще] (https://github.com/android/platform_frameworks_base/blob/4b1a8f46d6ec55796bf77fd8921a5a242a219278/core/java/android/os/ParcelFileDescriptor.java), AFAIK содержит ничего, кроме нативного дескриптора (и, необязательно, другого дескриптора для передачи метаданных). – user1643723

+0

@pskink Я не говорю, что это единственное возможное объяснение, просто самое простое. В отсутствие гарантий/документации от разработчиков Android-платформы я собираюсь предположить худшее. – user1643723

 Смежные вопросы

  • Нет связанных вопросов^_^