Skip to content

Commit

Permalink
Making a way to create thread with a Java Looper for Android
Browse files Browse the repository at this point in the history
We need to create a new message loop type for this as for
testing the Android UI message pump type is not the standard Java, but gets overridden to a different one that can handle nested message loops.

Using the new Java thread for the java bridge thread, so the thread used for AJI callbacks will have a prepared Looper.

BUG=b/8680913
TBR=jochen@chromium.org

Review URL: https://chromiumcodereview.appspot.com/18584006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@216349 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
kristianm@chromium.org committed Aug 8, 2013
1 parent 29aaa27 commit 349ad58
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 5 deletions.
2 changes: 2 additions & 0 deletions base/android/base_jni_registrar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "base/android/build_info.h"
#include "base/android/cpu_features.h"
#include "base/android/important_file_writer_android.h"
#include "base/android/java_handler_thread.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/android/memory_pressure_listener_android.h"
Expand Down Expand Up @@ -38,6 +39,7 @@ static RegistrationMethod kBaseRegisteredMethods[] = {
base::android::RegisterImportantFileWriterAndroid },
{ "MemoryPressureListenerAndroid",
base::android::MemoryPressureListenerAndroid::Register },
{ "JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings },
{ "PathService", base::android::RegisterPathService },
{ "PathUtils", base::android::RegisterPathUtils },
{ "SystemMessageHandler", base::MessagePumpForUI::RegisterBindings },
Expand Down
41 changes: 41 additions & 0 deletions base/android/java/src/org/chromium/base/JavaHandlerThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

/**
* This class is an internal detail of the native counterpart.
* It is instantiated and owned by the native object.
*/
@JNINamespace("base::android")
class JavaHandlerThread {
final HandlerThread mThread;

private JavaHandlerThread(String name) {
mThread = new HandlerThread(name);
}

@CalledByNative
private static JavaHandlerThread create(String name) {
return new JavaHandlerThread(name);
}

@CalledByNative
private void start(final int nativeThread, final int nativeEvent) {
mThread.start();
new Handler(mThread.getLooper()).post(new Runnable() {
@Override
public void run() {
nativeInitializeThread(nativeThread, nativeEvent);
}
});
}

private native void nativeInitializeThread(int nativeJavaHandlerThread, int nativeEvent);
}
62 changes: 62 additions & 0 deletions base/android/java_handler_thread.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.

#include "base/android/java_handler_thread.h"

#include <jni.h>

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "jni/JavaHandlerThread_jni.h"

namespace base {

namespace android {

JavaHandlerThread::JavaHandlerThread(const char* name) {
JNIEnv* env = base::android::AttachCurrentThread();

java_thread_.Reset(Java_JavaHandlerThread_create(
env, ConvertUTF8ToJavaString(env, name).Release()));
}

JavaHandlerThread::~JavaHandlerThread() {
}

void JavaHandlerThread::Start() {
// Check the thread has not already been started.
DCHECK(!message_loop_);

JNIEnv* env = base::android::AttachCurrentThread();
base::WaitableEvent initialize_event(false, false);
Java_JavaHandlerThread_start(env,
java_thread_.obj(),
reinterpret_cast<jint>(this),
reinterpret_cast<jint>(&initialize_event));
// Wait for thread to be initialized so it is ready to be used when Start
// returns.
base::ThreadRestrictions::ScopedAllowWait wait_allowed;
initialize_event.Wait();
}

void JavaHandlerThread::Stop() {
}

void JavaHandlerThread::InitializeThread(JNIEnv* env, jobject obj, jint event) {
// TYPE_JAVA to get the Android java style message loop.
message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_JAVA));
static_cast<MessageLoopForUI*>(message_loop_.get())->Start();
reinterpret_cast<base::WaitableEvent*>(event)->Signal();
}

// static
bool JavaHandlerThread::RegisterBindings(JNIEnv* env) {
return RegisterNativesImpl(env);
}

} // namespace android
} // namespace base
48 changes: 48 additions & 0 deletions base/android/java_handler_thread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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.

#ifndef BASE_THREADING_JAVA_THREAD_H_
#define BASE_THREADING_JAVA_THREAD_H_

#include <jni.h>

#include "base/android/scoped_java_ref.h"
#include "base/memory/scoped_ptr.h"

namespace base {

class MessageLoop;
class WaitableEvent;

namespace android {

// A Java Thread with a native message loop. To run tasks, post them
// to the message loop and they will be scheduled along with Java tasks
// on the thread.
// This is useful for callbacks where the receiver expects a thread
// with a prepared Looper.
class BASE_EXPORT JavaHandlerThread {
public:
JavaHandlerThread(const char* name);
virtual ~JavaHandlerThread();

base::MessageLoop* message_loop() const { return message_loop_.get(); }
void Start();
void Stop();

// Called from java on the newly created thread.
// Start() will not return before this methods has finished.
void InitializeThread(JNIEnv* env, jobject obj, jint event);

static bool RegisterBindings(JNIEnv* env);

private:
scoped_ptr<base::MessageLoop> message_loop_;
ScopedJavaGlobalRef<jobject> java_thread_;
};

} // namespace android
} // namespace base

#endif // BASE_THREADING_JAVA_THREAD_H_
1 change: 1 addition & 0 deletions base/base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,7 @@
'android/java/src/org/chromium/base/CpuFeatures.java',
'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
'android/java/src/org/chromium/base/MemoryPressureListener.java',
'android/java/src/org/chromium/base/JavaHandlerThread.java',
'android/java/src/org/chromium/base/PathService.java',
'android/java/src/org/chromium/base/PathUtils.java',
'android/java/src/org/chromium/base/PowerMonitor.java',
Expand Down
2 changes: 2 additions & 0 deletions base/base.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
'android/jni_string.h',
'android/memory_pressure_listener_android.cc',
'android/memory_pressure_listener_android.h',
'android/java_handler_thread.cc',
'android/java_handler_thread.h',
'android/path_service_android.cc',
'android/path_service_android.h',
'android/path_utils.cc',
Expand Down
6 changes: 5 additions & 1 deletion base/message_loop/message_loop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;
// time for every task that is added to the MessageLoop incoming queue.
bool AlwaysNotifyPump(MessageLoop::Type type) {
#if defined(OS_ANDROID)
return type == MessageLoop::TYPE_UI;
return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA;
#else
return false;
#endif
Expand Down Expand Up @@ -184,6 +184,10 @@ MessageLoop::MessageLoop(Type type)
pump_.reset(MESSAGE_PUMP_UI);
} else if (type_ == TYPE_IO) {
pump_.reset(MESSAGE_PUMP_IO);
#if defined(OS_ANDROID)
} else if (type_ == TYPE_JAVA) {
pump_.reset(MESSAGE_PUMP_UI);
#endif
} else {
DCHECK_EQ(TYPE_DEFAULT, type_);
pump_.reset(new MessagePumpDefault());
Expand Down
11 changes: 10 additions & 1 deletion base/message_loop/message_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,19 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// This type of ML also supports asynchronous IO. See also
// MessageLoopForIO.
//
// TYPE_JAVA
// This type of ML is backed by a Java message handler which is responsible
// for running the tasks added to the ML. This is only for use on Android.
// TYPE_JAVA behaves in essence like TYPE_UI, except during construction
// where it does not use the main thread specific pump factory.
//
enum Type {
TYPE_DEFAULT,
TYPE_UI,
TYPE_IO
TYPE_IO,
#if defined(OS_ANDROID)
TYPE_JAVA,
#endif // defined(OS_ANDROID)
};

// Normally, it is not necessary to instantiate a MessageLoop. Instead, it
Expand Down
5 changes: 5 additions & 0 deletions base/threading/thread_restrictions.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class AutoThread;

namespace base {

namespace android {
class JavaHandlerThread;
}

class SequencedWorkerPool;
class SimpleThread;
class Thread;
Expand Down Expand Up @@ -185,6 +189,7 @@ class BASE_EXPORT ThreadRestrictions {
friend class Thread;
friend class ThreadTestHelper;
friend class PlatformThread;
friend class android::JavaHandlerThread;

// END ALLOWED USAGE.
// BEGIN USAGE THAT NEEDS TO BE FIXED.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ class TestMessageLoop : public base::MessageLoop {
return BrowserThread::CurrentlyOn(BrowserThread::UI);
case base::MessageLoop::TYPE_IO:
return BrowserThread::CurrentlyOn(BrowserThread::IO);
#if defined(OS_ANDROID)
case base::MessageLoop::TYPE_JAVA: // fall-through
#endif // defined(OS_ANDROID)
case base::MessageLoop::TYPE_DEFAULT:
return !BrowserThread::CurrentlyOn(BrowserThread::UI) &&
!BrowserThread::CurrentlyOn(BrowserThread::IO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h"

#include "base/android/java_handler_thread.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/threading/thread.h"
#include "content/browser/renderer_host/java/java_bridge_channel_host.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/child/child_process.h"
Expand All @@ -17,12 +17,18 @@
#include "content/public/browser/render_process_host.h"
#include "third_party/WebKit/public/web/WebBindings.h"

#if !defined(OS_ANDROID)
#error "JavaBridge currently only supports OS_ANDROID"
#endif

namespace content {

namespace {
class JavaBridgeThread : public base::Thread {
// The JavaBridge needs to use a Java thread so the callback
// will happen on a thread with a prepared Looper.
class JavaBridgeThread : public base::android::JavaHandlerThread {
public:
JavaBridgeThread() : base::Thread("JavaBridge") {
JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
Start();
}
virtual ~JavaBridgeThread() {
Expand Down

0 comments on commit 349ad58

Please sign in to comment.