blob: 9a1c92439876c574686ec2771aff3b026864d041 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;
import java.lang.Thread.UncaughtExceptionHandler;
/**
* Thread in Java with an Android Handler. This class is not thread safe.
*/
@JNINamespace("base::android")
@MainDex
public class JavaHandlerThread {
private final HandlerThread mThread;
private Throwable mUnhandledException;
/**
* Construct a java-only instance. Can be connected with native side later.
* Useful for cases where a java thread is needed before native library is loaded.
*/
public JavaHandlerThread(String name, int priority) {
mThread = new HandlerThread(name, priority);
}
@CalledByNative
private static JavaHandlerThread create(String name, int priority) {
return new JavaHandlerThread(name, priority);
}
public Looper getLooper() {
assert hasStarted();
return mThread.getLooper();
}
public void maybeStart() {
if (hasStarted()) return;
mThread.start();
}
@CalledByNative
private void startAndInitialize(final long nativeThread, final long nativeEvent) {
maybeStart();
new Handler(mThread.getLooper()).post(new Runnable() {
@Override
public void run() {
nativeInitializeThread(nativeThread, nativeEvent);
}
});
}
@CalledByNative
private void quitThreadSafely(final long nativeThread) {
// Allow pending java tasks to run, but don't run any delayed or newly queued up tasks.
new Handler(mThread.getLooper()).post(new Runnable() {
@Override
public void run() {
mThread.quit();
nativeOnLooperStopped(nativeThread);
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// When we can, signal that new tasks queued up won't be run.
mThread.getLooper().quitSafely();
}
}
@CalledByNative
private void joinThread() {
boolean joined = false;
while (!joined) {
try {
mThread.join();
joined = true;
} catch (InterruptedException e) {
}
}
}
private boolean hasStarted() {
return mThread.getState() != Thread.State.NEW;
}
@CalledByNative
private boolean isAlive() {
return mThread.isAlive();
}
// This should *only* be used for tests. In production we always need to call the original
// uncaught exception handler (the framework's) after any uncaught exception handling we do, as
// it generates crash dumps and kills the process.
@CalledByNative
private void listenForUncaughtExceptionsForTesting() {
mThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
mUnhandledException = e;
}
});
}
@CalledByNative
private Throwable getUncaughtExceptionIfAny() {
return mUnhandledException;
}
private native void nativeInitializeThread(long nativeJavaHandlerThread, long nativeEvent);
private native void nativeOnLooperStopped(long nativeJavaHandlerThread);
}