Как решить проблему вызовов библиотеки NDK, зависающих в потоке пользовательского интерфейса

У меня есть библиотека C, к которой я обращаюсь через NDK. Некоторые операции занимают много времени, поэтому поток пользовательского интерфейса на них зависает. Сейчас я обычно использую Runnable следующим образом:

myFixedThreadPool.execute(new Runnable() 
{
  public void run() 
  {
    NativeClass.callToNDKLibrary();
  };
});

или такие темы:

Thread t = new Thread() 
{
  public void run() 
  {
    NativeClass.callToNDKLibrary();
  }
};
t.start();  

но проблема в том, что библиотека C не является потокобезопасной: она начинает падать, когда я оборачиваю ее вот так. Таким образом, вопрос заключается в том, как отсоединиться от потока пользовательского интерфейса, по-прежнему заставляя вызовы NDK выполняться по одному. Может какой трюк с синхронизацией поможет? Я также хочу упомянуть, что эта библиотека является основным базовым слоем для моего приложения, а не просто вспомогательной функцией. Так что он вызывается почти из каждой части моего кода. И только несколько функций потребляют время, большинство из них быстро удерживают их в основном потоке.


person Andrey Novikov    schedule 02.11.2010    source источник


Ответы (3)


Окончательное решение выглядит следующим образом:

  1. Определите все собственные методы JNI как синхронизированные. Это предотвратит сбой библиотеки C, но все равно заморозит пользовательский интерфейс.
  2. Определите исполнителя одного потока:

    ExecutorService mExecutorThread = Executors.newSingleThreadExecutor();

  3. Оберните все трудоемкие операции в этом потоке:

    mExecutorThread.execute(new Runnable() { public void run() { NativeClass.callToNDKLibrary(); } });

Последнее, что мне нужно было сделать, это немного изменить код, чтобы убедиться, что ни один из нативных методов не вызывается из onDraw(). Я кэшировал результаты нативных методов в обработчиках событий и использовал эти кэшированные значения в onDraw().

person Andrey Novikov    schedule 16.11.2010

но проблема в том, что библиотека C не является потокобезопасной: она начинает падать, когда я оборачиваю ее вот так.

Затем используйте один выделенный фоновый поток для всех операций с библиотекой. Например, используйте IntentService для доступа к библиотеке, а действия вызывают startService() для отправки команд в IntentService.

Or:

  1. Создайте LinkedBlockingQueue<Job> для некоторого Job класса/интерфейса, который вы определяете
  2. Иметь один поток, контролирующий эту очередь
  3. Отправляйте задания в эту очередь, чтобы получить доступ к вашей библиотеке
  4. Отправьте в очередь специальное «задачу уничтожения», чтобы вызвать выход из цикла мониторинга очереди, что приведет к завершению фонового потока, когда вам больше не нужна очередь и поток.

Или потратьте время на то, чтобы сделать библиотеку потокобезопасной на уровне C. Вопрос о том, как сделать код C потокобезопасным, обсуждался около 20 лет, так что, вероятно, есть несколько приемов, которые вы можете использовать, исходя из всего этого опыта.

person CommonsWare    schedule 02.11.2010
comment
Спасибо, это дало мне подсказку, с чего начать думать. - person Andrey Novikov; 17.11.2010

Вы можете использовать Looper для создания цикла сообщений. Затем передайте вызовы NDK через Handler, и каждый из них будет выполняться в перемена.

person Dave Webb    schedule 02.11.2010