如何解决NDK库调用冻结UI线程的问题

时间:2010-11-02 08:48:26

标签: android multithreading android-ndk

我有一个可以通过NDK访问的C库。某些操作非常耗时,因此UI线程会冻结它们。我现在常见的做法是使用Runnable:

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

或类似的线程:

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

但问题是C库不是线程安全的:当我像这样包装它时会开始崩溃。所以问题是如何从UI线程分离仍然强制NDK调用一次运行一个。可能是同步的一些技巧会有帮助吗?我还想提一下,这个库是我的应用程序的核心底层,而不仅仅是一个辅助函数。所以它几乎从我的代码的每个部分调用。并且只有少数函数消耗时间,大多数函数很快将它们保存在主线程中。

3 个答案:

答案 0 :(得分:3)

最终解决方案如下:

  1. 将所有JNI本机方法定义为已同步。这将阻止C库崩溃但仍会冻结UI。
  2. 定义单线程执行程序:

    ExecutorService mExecutorThread = Executors.newSingleThreadExecutor();

  3. 在该线程中包装所有耗时的操作:

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

  4. 我必须做的最后一件事是稍微重新设计我的代码,以确保没有从onDraw()调用任何本机方法。我在事件处理程序中缓存了本机方法的结果,并在onDraw()中使用了缓存的值。

答案 1 :(得分:2)

  

但问题是C库不是线程安全的:当我像这样包装它时会开始崩溃。

然后对库中的所有操作使用单个专用后台线程。例如,使用IntentService访问库,调用startService()的活动将命令发送到IntentService

或者:

  1. 为您定义的某些LinkedBlockingQueue<Job>类/接口创建Job
  2. 有一个队列的单线程监视器
  3. 将作业发送到该队列以访问您的库
  4. 向队列发送特殊的“kill job”,导致monitor-the-queue循环退出,导致后台线程终止,一旦不再需要队列和线程
  5. 或者,花时间在C级别使库具有线程安全性。使C代码成为线程安全的问题已经讨论了大约20年,因此可能会有一些技巧可以使用来自所有这些经验。

答案 2 :(得分:0)

您可以使用Looper创建消息循环。然后通过Handler传递您的NDK呼叫,并且每个呼叫将依次运行。