Binder线程中断意外行为

时间:2014-08-14 04:43:49

标签: android multithreading android-binder

Android ContentProvider使用“绑定线程”池来处理queryinsertcall等RPC。在我ContentProvider的实现中,我正在使用Thread#interrupt()执行可中断的长时间运行任务。我编写了一个监视程序线程,在某些情况下会中断当前正在执行的绑定程序线程。中断正确生效但现在我注意到,当android再次使用它来处理新的RPC时,绑定线程仍然设置了它的中断标志。这会导致新RPC的行为就像它被中断一样。我没想到这一点。我捕获InterruptedException但是如果看门狗线程在没有任何查看或清除标志时将中断标志设置为RPC的末尾,则会发生这种情况。

ExecutorService的一些临时测试表明,当它重用线程来处理任务时,它会在运行每个任务之前的某个时间重置中断标志。我在ThreadPoolExecutor课程中找到了这条评论:

 * 2. Before running any task, the lock is acquired to prevent
 * other pool interrupts while the task is executing, and
 * clearInterruptsForTaskRun called to ensure that unless pool is
 * stopping, this thread does not have its interrupt set.

为了纠正android的行为,我想在每个ContentProvider RPC的开头我用Thread.interrupted()清除中断标志,但这似乎不是最优雅的解决方案。

任何人都可以确认android绑定线程实际上是否正在处理线程中断与ExecutorService不同,并且可能是最好的解决办法,因此中断标志不会在不同的RPC之间传播?

以下是一些可以放在ContentProvider中以重现问题的代码:

@Override
public Bundle call(String method, String arg, Bundle extras) {
  Log.w(TAG, "Thread "+ Thread.currentThread().getName() + " before isInterrupted()=" + Thread.currentThread().isInterrupted());

  final Thread callThread = Thread.currentThread();

  if (method.equals("interrupt")) {
    new Thread("interrupter") {
      @Override
      public void run() {
        callThread.interrupt();
      }
    }.start();
  }

  try {
    Thread.sleep(500);
  } catch (InterruptedException e) {
    Log.w(TAG, e);
    Thread.currentThread().interrupt();
  }

  Log.w(TAG, "Thread "+ Thread.currentThread().getName() + " after isInterrupted()=" + Thread.currentThread().isInterrupted());

  return null;
}

然后在Activity中(确保它在与ContentProvider不同的进程中运行!)挂钩两个按钮,一个执行常规呼叫,另一个执行将触发的呼叫中断:

findViewById(R.id.call).setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    getContentResolver().call(CallContentProvider.CONTENT_URI, "", "", null);
  }
});

findViewById(R.id.callint).setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    getContentResolver().call(CallContentProvider.CONTENT_URI, "interrupt", "", null);
  }
});

1 个答案:

答案 0 :(得分:1)

所以这是迄今为止我最好的解决这个问题的方法:

private volatile Thread mRunningCallThread;

@Override
public Bundle call(String method, String arg, Bundle extras) {
    Thread.interrupted(); // Clear interrupted flag

    mRunningCallThread = Thread.currentThread(); // Thread to interrupt

    // Do stuff, allowing interruption by another thread

    mRunningCallThread = null;

    Thread.interrupted(); // Clear interrupted flag
}

字段mRunningCallThread是指向我可能中断的当前正在运行的线程的指针。