blob: 4db807a3a6eef9bf7cfff6481d1c1a6b5a5c15f0 [file] [log] [blame]
/*
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.appspot.apprtc.util;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Looper based executor class. This is needed because WebSocketClient from autobanh requires the
* thread to have a looper. The class is used in WebSocketRTCClient/WebSocketChannelClient.
*/
public class LooperExecutor extends Thread implements Executor {
private static final String TAG = "LooperExecutor";
// Object used to signal that looper thread has started and Handler instance
// associated with looper thread has been allocated.
private final Object looperStartedEvent = new Object();
private final List<Runnable> scheduledPeriodicRunnables = new LinkedList<Runnable>();
private Handler handler = null;
private boolean running = false;
private long threadId;
@Override
public void run() {
Looper.prepare();
synchronized (looperStartedEvent) {
Log.d(TAG, "Looper thread started.");
handler = new Handler();
threadId = Thread.currentThread().getId();
looperStartedEvent.notify();
}
Looper.loop();
}
public synchronized void requestStart() {
if (running) {
return;
}
running = true;
handler = null;
start();
// Wait for Hander allocation.
synchronized (looperStartedEvent) {
while (handler == null) {
try {
looperStartedEvent.wait();
} catch (InterruptedException e) {
Log.e(TAG, "Can not start looper thread");
running = false;
}
}
}
}
public synchronized void requestStop() {
if (!running) {
return;
}
running = false;
handler.post(new Runnable() {
@Override
public void run() {
handler.getLooper().quit();
Log.d(TAG, "Looper thread finished.");
}
});
}
// Checks if current thread is a looper thread.
public boolean checkOnLooperThread() {
return (Thread.currentThread().getId() == threadId);
}
public synchronized void scheduleAtFixedRate(final Runnable command, final long periodMillis) {
if (!running) {
Log.w(TAG, "Trying to schedule task for non running executor");
return;
}
Runnable runnable = new Runnable() {
@Override
public void run() {
if (running) {
command.run();
if (!handler.postDelayed(this, periodMillis)) {
Log.e(TAG, "Failed to post a delayed runnable in the chain.");
}
}
}
};
scheduledPeriodicRunnables.add(runnable);
if (!handler.postDelayed(runnable, periodMillis)) {
Log.e(TAG, "Failed to post a delayed runnable.");
}
}
public synchronized void cancelScheduledTasks() {
if (!running) {
Log.w(TAG, "Trying to cancel schedule tasks for non running executor");
return;
}
// Stop scheduled periodic tasks.
for (Runnable r : scheduledPeriodicRunnables) {
handler.removeCallbacks(r);
}
scheduledPeriodicRunnables.clear();
}
@Override
public synchronized void execute(final Runnable runnable) {
if (!running) {
Log.w(TAG, "Running looper executor without calling requestStart()");
return;
}
if (Thread.currentThread().getId() == threadId) {
runnable.run();
} else {
handler.post(runnable);
}
}
/**
* Access to the handler for testing purposes.
*/
Handler getHandler() {
return handler;
}
}