diff options
8 files changed, 197 insertions, 0 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsLifecycleNotifier.java b/android_webview/java/src/org/chromium/android_webview/AwContentsLifecycleNotifier.java new file mode 100644 index 0000000..6701c95 --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsLifecycleNotifier.java @@ -0,0 +1,74 @@ +// Copyright 2015 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.android_webview; + +import org.chromium.base.ObserverList; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * This class is intended to notify observers of the existence native instances of + * aw_contents. It receives a callback when native aw_contents are created or + * destroyed. Observers are notified when the first instance is created or the + * last instance is destroyed. + */ +@JNINamespace("android_webview") +public class AwContentsLifecycleNotifier { + + /** + * Observer interface to be implemented by deriving webview lifecycle observers. + */ + public static interface Observer { + public void onFirstWebViewCreated(); + public void onLastWebViewDestroyed(); + } + + private static final ObserverList<Observer> sLifecycleObservers = + new ObserverList<Observer>(); + private static int sNumWebViews = 0; + + private AwContentsLifecycleNotifier() {} + + public static void addObserver(Observer observer) { + sLifecycleObservers.addObserver(observer); + } + + public static void removeObserver(Observer observer) { + sLifecycleObservers.removeObserver(observer); + } + + public static boolean hasWebViewInstances() { + return sNumWebViews > 0; + } + + // Called on UI thread. + @CalledByNative + private static void onWebViewCreated() { + ThreadUtils.assertOnUiThread(); + assert sNumWebViews >= 0; + sNumWebViews++; + if (sNumWebViews == 1) { + // first webview created, notify observers. + for (Observer observer : sLifecycleObservers) { + observer.onFirstWebViewCreated(); + } + } + } + + // Called on UI thread. + @CalledByNative + private static void onWebViewDestroyed() { + ThreadUtils.assertOnUiThread(); + assert sNumWebViews > 0; + sNumWebViews--; + if (sNumWebViews == 0) { + // last webview destroyed, notify observers. + for (Observer observer : sLifecycleObservers) { + observer.onLastWebViewDestroyed(); + } + } + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsLifecycleNotifierTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsLifecycleNotifierTest.java new file mode 100644 index 0000000..2c7d408 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsLifecycleNotifierTest.java @@ -0,0 +1,57 @@ +// Copyright 2015 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.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.android_webview.AwContentsLifecycleNotifier; +import org.chromium.base.test.util.Feature; +import org.chromium.content.browser.test.util.CallbackHelper; + +/** + * AwContentsLifecycleNotifier tests. + */ +public class AwContentsLifecycleNotifierTest extends AwTestBase { + + private TestAwContentsClient mContentsClient = new TestAwContentsClient(); + + private static class LifecycleObserver implements AwContentsLifecycleNotifier.Observer { + public CallbackHelper mFirstWebViewCreatedCallback = new CallbackHelper(); + public CallbackHelper mLastWebViewDestroyedCallback = new CallbackHelper(); + + @Override + public void onFirstWebViewCreated() { + mFirstWebViewCreatedCallback.notifyCalled(); + } + + @Override + public void onLastWebViewDestroyed() { + mLastWebViewDestroyedCallback.notifyCalled(); + } + } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testNotifierCreate() throws Throwable { + LifecycleObserver observer = new LifecycleObserver(); + AwContentsLifecycleNotifier.addObserver(observer); + assertFalse(AwContentsLifecycleNotifier.hasWebViewInstances()); + + AwTestContainerView awTestContainerView = + createAwTestContainerViewOnMainSync(mContentsClient); + observer.mFirstWebViewCreatedCallback.waitForCallback(0, 1); + assertTrue(AwContentsLifecycleNotifier.hasWebViewInstances()); + + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().removeAllViews(); + } + }); + destroyAwContentsOnMainSync(awTestContainerView.getAwContents()); + observer.mLastWebViewDestroyedCallback.waitForCallback(0, 1); + assertFalse(AwContentsLifecycleNotifier.hasWebViewInstances()); + } +} diff --git a/android_webview/native/BUILD.gn b/android_webview/native/BUILD.gn index 320cbb3..cec2c0e 100644 --- a/android_webview/native/BUILD.gn +++ b/android_webview/native/BUILD.gn @@ -47,6 +47,8 @@ source_set("native") { "aw_contents_client_bridge.h", "aw_contents_io_thread_client_impl.cc", "aw_contents_io_thread_client_impl.h", + "aw_contents_lifecycle_notifier.cc", + "aw_contents_lifecycle_notifier.h", "aw_contents_statics.cc", "aw_contents_statics.h", "aw_debug.cc", @@ -118,6 +120,7 @@ generate_jni("native_jni") { "../java/src/org/chromium/android_webview/AwContentsBackgroundThreadClient.java", "../java/src/org/chromium/android_webview/AwContentsClientBridge.java", "../java/src/org/chromium/android_webview/AwContentsIoThreadClient.java", + "../java/src/org/chromium/android_webview/AwContentsLifecycleNotifier.java", "../java/src/org/chromium/android_webview/AwContentsStatics.java", "../java/src/org/chromium/android_webview/AwCookieManager.java", "../java/src/org/chromium/android_webview/AwDebug.java", diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc index 275e29b..a585d7e 100644 --- a/android_webview/native/android_webview_jni_registrar.cc +++ b/android_webview/native/android_webview_jni_registrar.cc @@ -10,6 +10,7 @@ #include "android_webview/native/aw_contents_background_thread_client.h" #include "android_webview/native/aw_contents_client_bridge.h" #include "android_webview/native/aw_contents_io_thread_client_impl.h" +#include "android_webview/native/aw_contents_lifecycle_notifier.h" #include "android_webview/native/aw_contents_statics.h" #include "android_webview/native/aw_debug.h" #include "android_webview/native/aw_dev_tools_server.h" @@ -59,6 +60,7 @@ static base::android::RegistrationMethod kWebViewRegisteredMethods[] = { { "InputStream", RegisterInputStream }, { "JavaBrowserViewRendererHelper", RegisterJavaBrowserViewRendererHelper }, { "AwMessagePortService", RegisterAwMessagePortService }, + { "AwContentsLifecycleNotifier", RegisterAwContentsLifecycleNotifier }, }; bool RegisterJni(JNIEnv* env) { diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index 4a829fa..8ec6260 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -22,6 +22,7 @@ #include "android_webview/native/aw_browser_dependency_factory.h" #include "android_webview/native/aw_contents_client_bridge.h" #include "android_webview/native/aw_contents_io_thread_client_impl.h" +#include "android_webview/native/aw_contents_lifecycle_notifier.h" #include "android_webview/native/aw_message_port_service_impl.h" #include "android_webview/native/aw_pdf_exporter.h" #include "android_webview/native/aw_picture.h" @@ -209,6 +210,7 @@ AwContents::AwContents(scoped_ptr<WebContents> web_contents) InitAutofillIfNecessary(autofill_manager_delegate->GetSaveFormData()); content::SynchronousCompositor::SetClientForWebContents( web_contents_.get(), &browser_view_renderer_); + AwContentsLifecycleNotifier::OnWebViewCreated(); } void AwContents::SetJavaPeers(JNIEnv* env, @@ -293,9 +295,12 @@ AwContents::~AwContents() { // Chromium, because the app process may continue to run for a long time // without ever using another WebView. if (instance_count == 0) { + // TODO(timvolodine): consider moving NotifyMemoryPressure to + // AwContentsLifecycleNotifier (crbug.com/522988). base::MemoryPressureListener::NotifyMemoryPressure( base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); } + AwContentsLifecycleNotifier::OnWebViewDestroyed(); } base::android::ScopedJavaLocalRef<jobject> diff --git a/android_webview/native/aw_contents_lifecycle_notifier.cc b/android_webview/native/aw_contents_lifecycle_notifier.cc new file mode 100644 index 0000000..0644ed7 --- /dev/null +++ b/android_webview/native/aw_contents_lifecycle_notifier.cc @@ -0,0 +1,27 @@ +// Copyright 2015 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 "android_webview/native/aw_contents_lifecycle_notifier.h" + +#include "jni/AwContentsLifecycleNotifier_jni.h" + +using base::android::AttachCurrentThread; + +namespace android_webview { + +bool RegisterAwContentsLifecycleNotifier(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// static +void AwContentsLifecycleNotifier::OnWebViewCreated() { + Java_AwContentsLifecycleNotifier_onWebViewCreated(AttachCurrentThread()); +} + +// static +void AwContentsLifecycleNotifier::OnWebViewDestroyed() { + Java_AwContentsLifecycleNotifier_onWebViewDestroyed(AttachCurrentThread()); +} + +} // namespace android_webview diff --git a/android_webview/native/aw_contents_lifecycle_notifier.h b/android_webview/native/aw_contents_lifecycle_notifier.h new file mode 100644 index 0000000..7965f11 --- /dev/null +++ b/android_webview/native/aw_contents_lifecycle_notifier.h @@ -0,0 +1,26 @@ +// Copyright 2015 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 ANDROID_WEBVIEW_NATIVE_AW_CONTENTS_LIFECYCLE_OBSERVER_H_ +#define ANDROID_WEBVIEW_NATIVE_AW_CONTENTS_LIFECYCLE_OBSERVER_H_ + +#include "base/android/jni_android.h" +#include "base/macros.h" + +namespace android_webview { + +class AwContentsLifecycleNotifier { + public: + static void OnWebViewCreated(); + static void OnWebViewDestroyed(); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(AwContentsLifecycleNotifier); +}; + +bool RegisterAwContentsLifecycleNotifier(JNIEnv* env); + +} // namespace android_webview + +#endif diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp index 020f0a8..97d941e 100644 --- a/android_webview/native/webview_native.gyp +++ b/android_webview/native/webview_native.gyp @@ -50,6 +50,8 @@ 'aw_contents_client_bridge.h', 'aw_contents_io_thread_client_impl.cc', 'aw_contents_io_thread_client_impl.h', + 'aw_contents_lifecycle_notifier.cc', + 'aw_contents_lifecycle_notifier.h', 'aw_contents_statics.cc', 'aw_contents_statics.h', 'aw_debug.cc', @@ -135,6 +137,7 @@ '../java/src/org/chromium/android_webview/AwContentsBackgroundThreadClient.java', '../java/src/org/chromium/android_webview/AwContentsClientBridge.java', '../java/src/org/chromium/android_webview/AwContentsIoThreadClient.java', + '../java/src/org/chromium/android_webview/AwContentsLifecycleNotifier.java', '../java/src/org/chromium/android_webview/AwContentsStatics.java', '../java/src/org/chromium/android_webview/AwCookieManager.java', '../java/src/org/chromium/android_webview/AwDebug.java', |