mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
The Android FML backend. (#3489)
This commit is contained in:
parent
117679f92e
commit
30ab542443
184
fml/platform/android/jni_util.cc
Normal file
184
fml/platform/android/jni_util.cc
Normal file
@ -0,0 +1,184 @@
|
||||
// Copyright 2017 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 "flutter/fml/platform/android/jni_util.h"
|
||||
|
||||
#include <codecvt>
|
||||
#include <string>
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
static JavaVM* g_jvm = nullptr;
|
||||
static ScopedJavaGlobalRef<jobject>* g_android_application_context = nullptr;
|
||||
|
||||
#define ASSERT_NO_EXCEPTION() FTL_CHECK(env->ExceptionCheck() == JNI_FALSE);
|
||||
|
||||
void InitJavaVM(JavaVM* vm) {
|
||||
FTL_DCHECK(g_jvm == nullptr);
|
||||
g_jvm = vm;
|
||||
}
|
||||
|
||||
JNIEnv* AttachCurrentThread() {
|
||||
FTL_DCHECK(g_jvm != nullptr)
|
||||
<< "Trying to attach to current thread without calling InitJavaVM first.";
|
||||
JNIEnv* env = nullptr;
|
||||
jint ret = g_jvm->AttachCurrentThread(&env, nullptr);
|
||||
FTL_DCHECK(JNI_OK == ret);
|
||||
return env;
|
||||
}
|
||||
|
||||
void DetachFromVM() {
|
||||
if (g_jvm) {
|
||||
g_jvm->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
void InitAndroidApplicationContext(const JavaRef<jobject>& context) {
|
||||
FTL_DCHECK(g_android_application_context == nullptr);
|
||||
g_android_application_context = new ScopedJavaGlobalRef<jobject>(context);
|
||||
FTL_DCHECK(g_android_application_context->obj() != nullptr);
|
||||
}
|
||||
|
||||
const jobject GetAndroidApplicationContext() {
|
||||
jobject object = g_android_application_context->obj();
|
||||
FTL_DCHECK(object != nullptr)
|
||||
<< "Trying to get Android application context without first calling "
|
||||
"InitAndroidApplicationContext.";
|
||||
return object;
|
||||
}
|
||||
|
||||
static std::string UTF16StringToUTF8String(const char16_t* chars, size_t len) {
|
||||
std::u16string u16_string(chars, len);
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
|
||||
.to_bytes(u16_string);
|
||||
}
|
||||
|
||||
std::string JavaStringToString(JNIEnv* env, jstring str) {
|
||||
if (env == nullptr || str == nullptr) {
|
||||
return "";
|
||||
}
|
||||
const jchar* chars = env->GetStringChars(str, NULL);
|
||||
if (chars == nullptr) {
|
||||
return "";
|
||||
}
|
||||
std::string u8_string = UTF16StringToUTF8String(
|
||||
reinterpret_cast<const char16_t*>(chars), env->GetStringLength(str));
|
||||
env->ReleaseStringChars(str, chars);
|
||||
ASSERT_NO_EXCEPTION();
|
||||
return u8_string;
|
||||
}
|
||||
|
||||
static std::u16string UTF8StringToUTF16String(const std::string& string) {
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
|
||||
.from_bytes(string);
|
||||
}
|
||||
|
||||
ScopedJavaLocalRef<jstring> StringToJavaString(JNIEnv* env,
|
||||
const std::string& u8_string) {
|
||||
std::u16string u16_string = UTF8StringToUTF16String(u8_string);
|
||||
auto result = ScopedJavaLocalRef<jstring>(
|
||||
env, env->NewString(reinterpret_cast<const jchar*>(u16_string.data()),
|
||||
u16_string.length()));
|
||||
ASSERT_NO_EXCEPTION();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> StringArrayToVector(JNIEnv* env, jobjectArray array) {
|
||||
std::vector<std::string> out;
|
||||
if (env == nullptr || array == nullptr) {
|
||||
return out;
|
||||
}
|
||||
|
||||
jsize length = env->GetArrayLength(array);
|
||||
|
||||
if (length == -1) {
|
||||
return out;
|
||||
}
|
||||
|
||||
out.resize(length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
ScopedJavaLocalRef<jstring> java_string(
|
||||
env, static_cast<jstring>(env->GetObjectArrayElement(array, i)));
|
||||
out[i] = JavaStringToString(env, java_string.obj());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
ScopedJavaLocalRef<jobjectArray> VectorToStringArray(
|
||||
JNIEnv* env,
|
||||
const std::vector<std::string>& vector) {
|
||||
FTL_DCHECK(env);
|
||||
ScopedJavaLocalRef<jclass> string_clazz(env,
|
||||
env->FindClass("java/lang/String"));
|
||||
FTL_DCHECK(!string_clazz.is_null());
|
||||
jobjectArray joa =
|
||||
env->NewObjectArray(vector.size(), string_clazz.obj(), NULL);
|
||||
ASSERT_NO_EXCEPTION();
|
||||
for (size_t i = 0; i < vector.size(); ++i) {
|
||||
ScopedJavaLocalRef<jstring> item = StringToJavaString(env, vector[i]);
|
||||
env->SetObjectArrayElement(joa, i, item.obj());
|
||||
}
|
||||
return ScopedJavaLocalRef<jobjectArray>(env, joa);
|
||||
}
|
||||
|
||||
bool HasException(JNIEnv* env) {
|
||||
return env->ExceptionCheck() != JNI_FALSE;
|
||||
}
|
||||
|
||||
bool ClearException(JNIEnv* env) {
|
||||
if (!HasException(env))
|
||||
return false;
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
|
||||
ScopedJavaLocalRef<jclass> throwable_clazz(
|
||||
env, env->FindClass("java/lang/Throwable"));
|
||||
|
||||
jmethodID throwable_printstacktrace = env->GetMethodID(
|
||||
throwable_clazz.obj(), "printStackTrace", "(Ljava/io/PrintStream;)V");
|
||||
|
||||
// Create an instance of ByteArrayOutputStream.
|
||||
ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz(
|
||||
env, env->FindClass("java/io/ByteArrayOutputStream"));
|
||||
|
||||
jmethodID bytearray_output_stream_constructor =
|
||||
env->GetMethodID(bytearray_output_stream_clazz.obj(), "<init>", "()V");
|
||||
jmethodID bytearray_output_stream_tostring = env->GetMethodID(
|
||||
bytearray_output_stream_clazz.obj(), "toString", "()Ljava/lang/String;");
|
||||
ScopedJavaLocalRef<jobject> bytearray_output_stream(
|
||||
env, env->NewObject(bytearray_output_stream_clazz.obj(),
|
||||
bytearray_output_stream_constructor));
|
||||
|
||||
// Create an instance of PrintStream.
|
||||
ScopedJavaLocalRef<jclass> printstream_clazz(
|
||||
env, env->FindClass("java/io/PrintStream"));
|
||||
|
||||
jmethodID printstream_constructor = env->GetMethodID(
|
||||
printstream_clazz.obj(), "<init>", "(Ljava/io/OutputStream;)V");
|
||||
ScopedJavaLocalRef<jobject> printstream(
|
||||
env, env->NewObject(printstream_clazz.obj(), printstream_constructor,
|
||||
bytearray_output_stream.obj()));
|
||||
|
||||
// Call Throwable.printStackTrace(PrintStream)
|
||||
env->CallVoidMethod(java_throwable, throwable_printstacktrace,
|
||||
printstream.obj());
|
||||
|
||||
// Call ByteArrayOutputStream.toString()
|
||||
ScopedJavaLocalRef<jstring> exception_string(
|
||||
env,
|
||||
static_cast<jstring>(env->CallObjectMethod(
|
||||
bytearray_output_stream.obj(), bytearray_output_stream_tostring)));
|
||||
|
||||
return JavaStringToString(env, exception_string.obj());
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
48
fml/platform/android/jni_util.h
Normal file
48
fml/platform/android/jni_util.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017 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 FLUTTER_FML_PLATFORM_ANDROID_JNI_UTIL_H_
|
||||
#define FLUTTER_FML_PLATFORM_ANDROID_JNI_UTIL_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/platform/android/scoped_java_ref.h"
|
||||
#include "lib/ftl/macros.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
void InitJavaVM(JavaVM* vm);
|
||||
|
||||
JNIEnv* AttachCurrentThread();
|
||||
|
||||
void DetachFromVM();
|
||||
|
||||
void InitAndroidApplicationContext(const JavaRef<jobject>& context);
|
||||
|
||||
const jobject GetAndroidApplicationContext();
|
||||
|
||||
std::string JavaStringToString(JNIEnv* env, jstring string);
|
||||
|
||||
ScopedJavaLocalRef<jstring> StringToJavaString(JNIEnv* env,
|
||||
const std::string& str);
|
||||
|
||||
std::vector<std::string> StringArrayToVector(JNIEnv* env, jobjectArray jargs);
|
||||
|
||||
ScopedJavaLocalRef<jobjectArray> VectorToStringArray(
|
||||
JNIEnv* env,
|
||||
const std::vector<std::string>& vector);
|
||||
|
||||
bool HasException(JNIEnv* env);
|
||||
|
||||
bool ClearException(JNIEnv* env);
|
||||
|
||||
std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable);
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
|
||||
#endif // FLUTTER_FML_PLATFORM_ANDROID_JNI_UTIL_H_
|
||||
67
fml/platform/android/jni_weak_ref.cc
Normal file
67
fml/platform/android/jni_weak_ref.cc
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2017 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 "flutter/fml/platform/android/jni_weak_ref.h"
|
||||
|
||||
#include "flutter/fml/platform/android/jni_util.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() : obj_(NULL) {}
|
||||
|
||||
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
|
||||
const JavaObjectWeakGlobalRef& orig)
|
||||
: obj_(NULL) {
|
||||
Assign(orig);
|
||||
}
|
||||
|
||||
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj)
|
||||
: obj_(env->NewWeakGlobalRef(obj)) {
|
||||
FTL_DCHECK(obj_);
|
||||
}
|
||||
|
||||
JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) {
|
||||
Assign(rhs);
|
||||
}
|
||||
|
||||
void JavaObjectWeakGlobalRef::reset() {
|
||||
if (obj_) {
|
||||
AttachCurrentThread()->DeleteWeakGlobalRef(obj_);
|
||||
obj_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedJavaLocalRef<jobject> JavaObjectWeakGlobalRef::get(JNIEnv* env) const {
|
||||
return GetRealObject(env, obj_);
|
||||
}
|
||||
|
||||
ScopedJavaLocalRef<jobject> GetRealObject(JNIEnv* env, jweak obj) {
|
||||
jobject real = NULL;
|
||||
if (obj) {
|
||||
real = env->NewLocalRef(obj);
|
||||
if (!real)
|
||||
FTL_DLOG(ERROR) << "The real object has been deleted!";
|
||||
}
|
||||
return ScopedJavaLocalRef<jobject>(env, real);
|
||||
}
|
||||
|
||||
void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) {
|
||||
if (&other == this)
|
||||
return;
|
||||
|
||||
JNIEnv* env = AttachCurrentThread();
|
||||
if (obj_)
|
||||
env->DeleteWeakGlobalRef(obj_);
|
||||
|
||||
obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : NULL;
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
50
fml/platform/android/jni_weak_ref.h
Normal file
50
fml/platform/android/jni_weak_ref.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2017 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 FLUTTER_FML_PLATFORM_ANDROID_JNI_WEAK_REF_H_
|
||||
#define FLUTTER_FML_PLATFORM_ANDROID_JNI_WEAK_REF_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "flutter/fml/platform/android/scoped_java_ref.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
// Manages WeakGlobalRef lifecycle.
|
||||
// This class is not thread-safe w.r.t. get() and reset(). Multiple threads may
|
||||
// safely use get() concurrently, but if the user calls reset() (or of course,
|
||||
// calls the destructor) they'll need to provide their own synchronization.
|
||||
class JavaObjectWeakGlobalRef {
|
||||
public:
|
||||
JavaObjectWeakGlobalRef();
|
||||
|
||||
JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig);
|
||||
|
||||
JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj);
|
||||
|
||||
virtual ~JavaObjectWeakGlobalRef();
|
||||
|
||||
void operator=(const JavaObjectWeakGlobalRef& rhs);
|
||||
|
||||
ScopedJavaLocalRef<jobject> get(JNIEnv* env) const;
|
||||
|
||||
bool is_empty() const { return obj_ == NULL; }
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void Assign(const JavaObjectWeakGlobalRef& rhs);
|
||||
|
||||
jweak obj_;
|
||||
};
|
||||
|
||||
// Get the real object stored in the weak reference returned as a
|
||||
// ScopedJavaLocalRef.
|
||||
ScopedJavaLocalRef<jobject> GetRealObject(JNIEnv* env, jweak obj);
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
|
||||
#endif // FLUTTER_FML_PLATFORM_ANDROID_JNI_WEAK_REF_H_
|
||||
97
fml/platform/android/message_loop_android.cc
Normal file
97
fml/platform/android/message_loop_android.cc
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2017 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 "flutter/fml/platform/android/message_loop_android.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "flutter/fml/platform/linux/timerfd.h"
|
||||
#include "lib/ftl/files/eintr_wrapper.h"
|
||||
|
||||
namespace fml {
|
||||
|
||||
static constexpr int kClockType = CLOCK_MONOTONIC;
|
||||
|
||||
static ALooper* AcquireLooperForThread() {
|
||||
ALooper* looper = ALooper_forThread();
|
||||
|
||||
if (looper == nullptr) {
|
||||
// No looper has been configured for the current thread. Create one and
|
||||
// return the same.
|
||||
looper = ALooper_prepare(0);
|
||||
}
|
||||
|
||||
// The thread already has a looper. Acquire a reference to the same and return
|
||||
// it.
|
||||
ALooper_acquire(looper);
|
||||
return looper;
|
||||
}
|
||||
|
||||
MessageLoopAndroid::MessageLoopAndroid()
|
||||
: looper_(AcquireLooperForThread()),
|
||||
timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)),
|
||||
running_(false) {
|
||||
FTL_CHECK(looper_.is_valid());
|
||||
FTL_CHECK(timer_fd_.is_valid());
|
||||
|
||||
static const int kWakeEvents = ALOOPER_EVENT_INPUT;
|
||||
|
||||
ALooper_callbackFunc read_event_fd = [](int, int events, void* data) -> int {
|
||||
if (events & kWakeEvents) {
|
||||
reinterpret_cast<MessageLoopAndroid*>(data)->OnEventFired();
|
||||
}
|
||||
return 1; // continue receiving callbacks
|
||||
};
|
||||
|
||||
int add_result = ::ALooper_addFd(looper_.get(), // looper
|
||||
timer_fd_.get(), // fd
|
||||
ALOOPER_POLL_CALLBACK, // ident
|
||||
kWakeEvents, // events
|
||||
read_event_fd, // callback
|
||||
this // baton
|
||||
);
|
||||
FTL_CHECK(add_result == 1);
|
||||
}
|
||||
|
||||
MessageLoopAndroid::~MessageLoopAndroid() {
|
||||
int remove_result = ::ALooper_removeFd(looper_.get(), timer_fd_.get());
|
||||
FTL_CHECK(remove_result == 1);
|
||||
}
|
||||
|
||||
void MessageLoopAndroid::Run() {
|
||||
FTL_DCHECK(looper_.get() == ALooper_forThread());
|
||||
|
||||
running_ = true;
|
||||
|
||||
while (running_) {
|
||||
int result = ::ALooper_pollOnce(-1, // infinite timeout
|
||||
nullptr, // out fd,
|
||||
nullptr, // out events,
|
||||
nullptr // out data
|
||||
);
|
||||
if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) {
|
||||
// This handles the case where the loop is terminated using ALooper APIs.
|
||||
running_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLoopAndroid::Terminate() {
|
||||
running_ = false;
|
||||
ALooper_wake(looper_.get());
|
||||
}
|
||||
|
||||
void MessageLoopAndroid::WakeUp(ftl::TimePoint time_point) {
|
||||
bool result = TimerRearm(timer_fd_.get(), time_point);
|
||||
FTL_DCHECK(result);
|
||||
}
|
||||
|
||||
void MessageLoopAndroid::OnEventFired() {
|
||||
if (TimerDrain(timer_fd_.get())) {
|
||||
RunExpiredTasksNow();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fml
|
||||
50
fml/platform/android/message_loop_android.h
Normal file
50
fml/platform/android/message_loop_android.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2017 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 FLUTTER_FML_PLATFORM_ANDROID_MESSAGE_LOOP_ANDROID_H_
|
||||
#define FLUTTER_FML_PLATFORM_ANDROID_MESSAGE_LOOP_ANDROID_H_
|
||||
|
||||
#include <android/looper.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "flutter/fml/message_loop_impl.h"
|
||||
#include "lib/ftl/files/unique_fd.h"
|
||||
#include "lib/ftl/macros.h"
|
||||
#include "lib/ftl/memory/unique_object.h"
|
||||
|
||||
namespace fml {
|
||||
|
||||
struct UniqueLooperTraits {
|
||||
static ALooper* InvalidValue() { return nullptr; }
|
||||
static bool IsValid(ALooper* value) { return value != nullptr; }
|
||||
static void Free(ALooper* value) { ::ALooper_release(value); }
|
||||
};
|
||||
|
||||
class MessageLoopAndroid : public MessageLoopImpl {
|
||||
private:
|
||||
ftl::UniqueObject<ALooper*, UniqueLooperTraits> looper_;
|
||||
ftl::UniqueFD timer_fd_;
|
||||
bool running_;
|
||||
|
||||
MessageLoopAndroid();
|
||||
|
||||
~MessageLoopAndroid() override;
|
||||
|
||||
void Run() override;
|
||||
|
||||
void Terminate() override;
|
||||
|
||||
void WakeUp(ftl::TimePoint time_point) override;
|
||||
|
||||
void OnEventFired();
|
||||
|
||||
FRIEND_MAKE_REF_COUNTED(MessageLoopAndroid);
|
||||
FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopAndroid);
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(MessageLoopAndroid);
|
||||
};
|
||||
|
||||
} // namespace fml
|
||||
|
||||
#endif // FLUTTER_FML_PLATFORM_ANDROID_MESSAGE_LOOP_ANDROID_H_
|
||||
89
fml/platform/android/scoped_java_ref.cc
Normal file
89
fml/platform/android/scoped_java_ref.cc
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2017 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 "flutter/fml/platform/android/scoped_java_ref.h"
|
||||
|
||||
#include "flutter/fml/platform/android/jni_util.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
static const int kDefaultLocalFrameCapacity = 16;
|
||||
|
||||
ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env) : env_(env) {
|
||||
int failed = env_->PushLocalFrame(kDefaultLocalFrameCapacity);
|
||||
FTL_DCHECK(!failed);
|
||||
}
|
||||
|
||||
ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env, int capacity)
|
||||
: env_(env) {
|
||||
int failed = env_->PushLocalFrame(capacity);
|
||||
FTL_DCHECK(!failed);
|
||||
}
|
||||
|
||||
ScopedJavaLocalFrame::~ScopedJavaLocalFrame() {
|
||||
env_->PopLocalFrame(NULL);
|
||||
}
|
||||
|
||||
JavaRef<jobject>::JavaRef() : obj_(NULL) {}
|
||||
|
||||
JavaRef<jobject>::JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {
|
||||
if (obj) {
|
||||
FTL_DCHECK(env && env->GetObjectRefType(obj) == JNILocalRefType);
|
||||
}
|
||||
}
|
||||
|
||||
JavaRef<jobject>::~JavaRef() = default;
|
||||
|
||||
JNIEnv* JavaRef<jobject>::SetNewLocalRef(JNIEnv* env, jobject obj) {
|
||||
if (!env) {
|
||||
env = AttachCurrentThread();
|
||||
} else {
|
||||
FTL_DCHECK(env == AttachCurrentThread()); // Is |env| on correct thread.
|
||||
}
|
||||
if (obj)
|
||||
obj = env->NewLocalRef(obj);
|
||||
if (obj_)
|
||||
env->DeleteLocalRef(obj_);
|
||||
obj_ = obj;
|
||||
return env;
|
||||
}
|
||||
|
||||
void JavaRef<jobject>::SetNewGlobalRef(JNIEnv* env, jobject obj) {
|
||||
if (!env) {
|
||||
env = AttachCurrentThread();
|
||||
} else {
|
||||
FTL_DCHECK(env == AttachCurrentThread()); // Is |env| on correct thread.
|
||||
}
|
||||
if (obj)
|
||||
obj = env->NewGlobalRef(obj);
|
||||
if (obj_)
|
||||
env->DeleteGlobalRef(obj_);
|
||||
obj_ = obj;
|
||||
}
|
||||
|
||||
void JavaRef<jobject>::ResetLocalRef(JNIEnv* env) {
|
||||
if (obj_) {
|
||||
FTL_DCHECK(env == AttachCurrentThread()); // Is |env| on correct thread.
|
||||
env->DeleteLocalRef(obj_);
|
||||
obj_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void JavaRef<jobject>::ResetGlobalRef() {
|
||||
if (obj_) {
|
||||
AttachCurrentThread()->DeleteGlobalRef(obj_);
|
||||
obj_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
jobject JavaRef<jobject>::ReleaseInternal() {
|
||||
jobject obj = obj_;
|
||||
obj_ = NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
199
fml/platform/android/scoped_java_ref.h
Normal file
199
fml/platform/android/scoped_java_ref.h
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright 2017 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 FLUTTER_FML_PLATFORM_ANDROID_SCOPED_JAVA_REF_H_
|
||||
#define FLUTTER_FML_PLATFORM_ANDROID_SCOPED_JAVA_REF_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lib/ftl/macros.h"
|
||||
|
||||
namespace fml {
|
||||
namespace jni {
|
||||
|
||||
// Creates a new local reference frame, in which at least a given number of
|
||||
// local references can be created. Note that local references already created
|
||||
// in previous local frames are still valid in the current local frame.
|
||||
class ScopedJavaLocalFrame {
|
||||
public:
|
||||
explicit ScopedJavaLocalFrame(JNIEnv* env);
|
||||
ScopedJavaLocalFrame(JNIEnv* env, int capacity);
|
||||
~ScopedJavaLocalFrame();
|
||||
|
||||
private:
|
||||
// This class is only good for use on the thread it was created on so
|
||||
// it's safe to cache the non-threadsafe JNIEnv* inside this object.
|
||||
JNIEnv* env_;
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(ScopedJavaLocalFrame);
|
||||
};
|
||||
|
||||
// Forward declare the generic java reference template class.
|
||||
template <typename T>
|
||||
class JavaRef;
|
||||
|
||||
// Template specialization of JavaRef, which acts as the base class for all
|
||||
// other JavaRef<> template types. This allows you to e.g. pass
|
||||
// ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
|
||||
template <>
|
||||
class JavaRef<jobject> {
|
||||
public:
|
||||
jobject obj() const { return obj_; }
|
||||
|
||||
bool is_null() const { return obj_ == NULL; }
|
||||
|
||||
protected:
|
||||
// Initializes a NULL reference.
|
||||
JavaRef();
|
||||
|
||||
// Takes ownership of the |obj| reference passed; requires it to be a local
|
||||
// reference type.
|
||||
JavaRef(JNIEnv* env, jobject obj);
|
||||
|
||||
~JavaRef();
|
||||
|
||||
// The following are implementation detail convenience methods, for
|
||||
// use by the sub-classes.
|
||||
JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
|
||||
void SetNewGlobalRef(JNIEnv* env, jobject obj);
|
||||
void ResetLocalRef(JNIEnv* env);
|
||||
void ResetGlobalRef();
|
||||
jobject ReleaseInternal();
|
||||
|
||||
private:
|
||||
jobject obj_;
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(JavaRef);
|
||||
};
|
||||
|
||||
// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
|
||||
// for allowing functions to accept a reference without having to mandate
|
||||
// whether it is a local or global type.
|
||||
template <typename T>
|
||||
class JavaRef : public JavaRef<jobject> {
|
||||
public:
|
||||
T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
|
||||
|
||||
protected:
|
||||
JavaRef() {}
|
||||
~JavaRef() {}
|
||||
|
||||
JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
|
||||
|
||||
private:
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(JavaRef);
|
||||
};
|
||||
|
||||
// Holds a local reference to a Java object. The local reference is scoped
|
||||
// to the lifetime of this object.
|
||||
// Instances of this class may hold onto any JNIEnv passed into it until
|
||||
// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
|
||||
// thread, objects of this class must be created, used, and destroyed, on a
|
||||
// single thread.
|
||||
// Therefore, this class should only be used as a stack-based object and from a
|
||||
// single thread. If you wish to have the reference outlive the current
|
||||
// callstack (e.g. as a class member) or you wish to pass it across threads,
|
||||
// use a ScopedJavaGlobalRef instead.
|
||||
template <typename T>
|
||||
class ScopedJavaLocalRef : public JavaRef<T> {
|
||||
public:
|
||||
ScopedJavaLocalRef() : env_(NULL) {}
|
||||
|
||||
// Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned
|
||||
// by value as this is the normal usage pattern.
|
||||
ScopedJavaLocalRef(const ScopedJavaLocalRef<T>& other) : env_(other.env_) {
|
||||
this->SetNewLocalRef(env_, other.obj());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
explicit ScopedJavaLocalRef(const U& other) : env_(NULL) {
|
||||
this->Reset(other);
|
||||
}
|
||||
|
||||
// Assumes that |obj| is a local reference to a Java object and takes
|
||||
// ownership of this local reference.
|
||||
ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
|
||||
|
||||
~ScopedJavaLocalRef() { this->Reset(); }
|
||||
|
||||
// Overloaded assignment operator defined for consistency with the implicit
|
||||
// copy constructor.
|
||||
void operator=(const ScopedJavaLocalRef<T>& other) { this->Reset(other); }
|
||||
|
||||
void Reset() { this->ResetLocalRef(env_); }
|
||||
|
||||
template <typename U>
|
||||
void Reset(const ScopedJavaLocalRef<U>& other) {
|
||||
// We can copy over env_ here as |other| instance must be from the same
|
||||
// thread as |this| local ref. (See class comment for multi-threading
|
||||
// limitations, and alternatives).
|
||||
this->Reset(other.env_, other.obj());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void Reset(const U& other) {
|
||||
// If |env_| was not yet set (is still NULL) it will be attached to the
|
||||
// current thread in SetNewLocalRef().
|
||||
this->Reset(env_, other.obj());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void Reset(JNIEnv* env, U obj) {
|
||||
env_ = this->SetNewLocalRef(env, obj);
|
||||
}
|
||||
|
||||
// Releases the local reference to the caller. The caller *must* delete the
|
||||
// local reference when it is done with it.
|
||||
T Release() { return static_cast<T>(this->ReleaseInternal()); }
|
||||
|
||||
private:
|
||||
// This class is only good for use on the thread it was created on so
|
||||
// it's safe to cache the non-threadsafe JNIEnv* inside this object.
|
||||
JNIEnv* env_;
|
||||
};
|
||||
|
||||
// Holds a global reference to a Java object. The global reference is scoped
|
||||
// to the lifetime of this object. This class does not hold onto any JNIEnv*
|
||||
// passed to it, hence it is safe to use across threads (within the constraints
|
||||
// imposed by the underlying Java object that it references).
|
||||
template <typename T>
|
||||
class ScopedJavaGlobalRef : public JavaRef<T> {
|
||||
public:
|
||||
ScopedJavaGlobalRef() {}
|
||||
|
||||
explicit ScopedJavaGlobalRef(const ScopedJavaGlobalRef<T>& other) {
|
||||
this->Reset(other);
|
||||
}
|
||||
|
||||
ScopedJavaGlobalRef(JNIEnv* env, T obj) { this->Reset(env, obj); }
|
||||
|
||||
template <typename U>
|
||||
explicit ScopedJavaGlobalRef(const U& other) {
|
||||
this->Reset(other);
|
||||
}
|
||||
|
||||
~ScopedJavaGlobalRef() { this->Reset(); }
|
||||
|
||||
void Reset() { this->ResetGlobalRef(); }
|
||||
|
||||
template <typename U>
|
||||
void Reset(const U& other) {
|
||||
this->Reset(NULL, other.obj());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void Reset(JNIEnv* env, U obj) {
|
||||
this->SetNewGlobalRef(env, obj);
|
||||
}
|
||||
|
||||
// Releases the global reference to the caller. The caller *must* delete the
|
||||
// global reference when it is done with it.
|
||||
T Release() { return static_cast<T>(this->ReleaseInternal()); }
|
||||
};
|
||||
|
||||
} // namespace jni
|
||||
} // namespace fml
|
||||
|
||||
#endif // FLUTTER_FML_PLATFORM_ANDROID_SCOPED_JAVA_REF_H_
|
||||
@ -1915,6 +1915,14 @@ FILE: ../../../flutter/fml/message_loop.h
|
||||
FILE: ../../../flutter/fml/message_loop_impl.cc
|
||||
FILE: ../../../flutter/fml/message_loop_impl.h
|
||||
FILE: ../../../flutter/fml/message_loop_unittests.cc
|
||||
FILE: ../../../flutter/fml/platform/android/jni_util.cc
|
||||
FILE: ../../../flutter/fml/platform/android/jni_util.h
|
||||
FILE: ../../../flutter/fml/platform/android/jni_weak_ref.cc
|
||||
FILE: ../../../flutter/fml/platform/android/jni_weak_ref.h
|
||||
FILE: ../../../flutter/fml/platform/android/message_loop_android.cc
|
||||
FILE: ../../../flutter/fml/platform/android/message_loop_android.h
|
||||
FILE: ../../../flutter/fml/platform/android/scoped_java_ref.cc
|
||||
FILE: ../../../flutter/fml/platform/android/scoped_java_ref.h
|
||||
FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc
|
||||
FILE: ../../../flutter/fml/platform/darwin/cf_utils.h
|
||||
FILE: ../../../flutter/fml/platform/linux/message_loop_linux.cc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user