Revision: 9049
Author: rj...@google.com
Date: Wed Oct 13 09:41:33 2010
Log: Fixes
http://code.google.com/p/google-web-toolkit/issues/detail?id=5375,
calling setDisplay(null) from a PlaceChangeEvent.Handler will cause an
NPE in the ActivityManager's own PlaceChangeEvent.Handler
Also undoes demo-time hack that left stopped widgets in place to
reduce flicker. Apps will have to reduce flicker themselves.
Review at http://gwt-code-reviews.appspot.com/988801
Review by: robertvaw...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9049
Modified:
/trunk/user/src/com/google/gwt/activity/shared/ActivityManager.java
/trunk/user/src/com/google/gwt/activity/shared/ActivityMapper.java
/trunk/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
=======================================
--- /trunk/user/src/com/google/gwt/activity/shared/ActivityManager.java Tue
Oct 12 10:53:11 2010
+++ /trunk/user/src/com/google/gwt/activity/shared/ActivityManager.java Wed
Oct 13 09:41:33 2010
@@ -1,12 +1,12 @@
/*
* Copyright 2010 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not
* use this file except in compliance with the License. You may obtain a
copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -49,7 +49,7 @@
public void setWidget(IsWidget view) {
if (this.activity == ActivityManager.this.currentActivity) {
startingNext = false;
- display.setWidget(view);
+ showWidget(view);
}
}
}
@@ -74,7 +74,7 @@
/**
* Create an ActivityManager. Next call {...@link #setDisplay}.
- *
+ *
* @param mapper finds the {...@link Activity} for a given
* {...@link com.google.gwt.place.shared.Place}
* @param eventBus source of {...@link PlaceChangeEvent} and
@@ -89,11 +89,17 @@
/**
* Deactivate the current activity, find the next one from our
ActivityMapper,
* and start it.
- *
+ * <p>
+ * The current activity's widget will be hidden immediately, which can
cause
+ * flicker if the next activity provides its widget asynchronously. That
can
+ * be minimized by decent caching. Perenially slow activities might
mitigate
+ * this by providing a widget immediately, with some kind of "loading"
+ * treatment.
+ *
* @see
com.google.gwt.place.shared.PlaceChangeEvent.Handler#onPlaceChange(PlaceChangeEvent)
*/
public void onPlaceChange(PlaceChangeEvent event) {
- Activity nextActivity = mapper.getActivity(event.getNewPlace());
+ Activity nextActivity = getNextActivity(event);
Throwable caughtOnStop = null;
Throwable caughtOnStart = null;
@@ -113,11 +119,7 @@
currentActivity = NULL_ACTIVITY;
startingNext = false;
} else if (!currentActivity.equals(NULL_ACTIVITY)) {
- /*
- * TODO until caching is in place, relying on stopped activities to
be
- * good citizens to reduce flicker. This makes me very nervous.
- */
- // display.showActivityWidget(null);
+ showWidget(null);
/*
* Kill off the activity's handlers, so it doesn't have to worry
about
@@ -141,7 +143,7 @@
currentActivity = nextActivity;
if (currentActivity.equals(NULL_ACTIVITY)) {
- display.setWidget(null);
+ showWidget(null);
return;
}
@@ -150,7 +152,7 @@
/*
* Now start the thing. Wrap the actual display with a per-call
instance
* that protects the display from canceled or stopped activities, and
which
- * maintain our startingNext state.
+ * maintains our startingNext state.
*/
try {
currentActivity.start(new ProtectedDisplay(currentActivity),
@@ -174,7 +176,7 @@
/**
* Reject the place change if the current activity is not willing to
stop.
- *
+ *
* @see
com.google.gwt.place.shared.PlaceChangeRequestEvent.Handler#onPlaceChangeRequest(PlaceChangeRequestEvent)
*/
public void onPlaceChangeRequest(PlaceChangeRequestEvent event) {
@@ -190,7 +192,7 @@
* If you are disposing of an ActivityManager, it is important to call
* setDisplay(null) to get it to deregister from the event bus, so that
it can
* be garbage collected.
- *
+ *
* @param display an instance of AcceptsOneWidget
*/
public void setDisplay(AcceptsOneWidget display) {
@@ -201,6 +203,24 @@
updateHandlers(willBeActive);
}
}
+
+ private Activity getNextActivity(PlaceChangeEvent event) {
+ if (display == null) {
+ /*
+ * Display may have been nulled during PlaceChangeEvent dispatch.
Don't
+ * bother the mapper, just return a null to ensure we shut down the
+ * current activity
+ */
+ return null;
+ }
+ return mapper.getActivity(event.getNewPlace());
+ }
+
+ private void showWidget(IsWidget view) {
+ if (display != null) {
+ display.setWidget(view);
+ }
+ }
private void updateHandlers(boolean activate) {
if (activate) {
=======================================
--- /trunk/user/src/com/google/gwt/activity/shared/ActivityMapper.java Tue
Oct 12 10:53:11 2010
+++ /trunk/user/src/com/google/gwt/activity/shared/ActivityMapper.java Wed
Oct 13 09:41:33 2010
@@ -23,7 +23,7 @@
*/
public interface ActivityMapper {
/**
- * Returns the activity to run for the given {...@link Place}.
+ * Returns the activity to run for the given {...@link Place}, or null.
*
* @param place a Place object
*/
=======================================
---
/trunk/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
Tue Oct 5 17:59:14 2010
+++
/trunk/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
Wed Oct 13 09:41:33 2010
@@ -189,11 +189,7 @@
assertNull(asyncActivity2.display);
eventBus.fireEvent(new PlaceChangeEvent(place2));
- /*
- * TODO until caching is in place, relying on stopped activities to be
good
- * citizens to reduce flicker. This makes me very nervous.
- */
- // assertNull(realDisplay.widget);
+ assertNull(realDisplay.widget);
assertFalse(asyncActivity1.canceled);
assertTrue(asyncActivity1.stopped);
assertFalse(asyncActivity2.stopped);
@@ -354,6 +350,60 @@
assertNotNull(activity2.display);
assertEquals(0, eventBus.getCount(Event.TYPE));
}
+
+ /**
+ * http://code.google.com/p/google-web-toolkit/issues/detail?id=5375
+ */
+ public void testNullDisplayOnPlaceChange() {
+ manager.setDisplay(realDisplay);
+
+ // Start an activity
+ manager.onPlaceChange(new PlaceChangeEvent(place1));
+
+ /*
+ * Now we're going to place2. During PlaceChangeEvent dispatch,
+ * someone kills the manager's display.
+ */
+ manager.setDisplay(null);
+
+ // Now the place change event reaches the manager
+ manager.onPlaceChange(new PlaceChangeEvent(place2));
+
+ assertNull(activity2.display);
+ assertTrue(activity1.stopped);
+ }
+
+ public void testNullDisplayBeforeAsyncStart() {
+ final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView());
+ final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView());
+
+ ActivityMapper map = new ActivityMapper() {
+ public Activity getActivity(Place place) {
+ if (place.equals(place1)) {
+ return asyncActivity1;
+ }
+ if (place.equals(place2)) {
+ return asyncActivity2;
+ }
+
+ return null;
+ }
+ };
+
+ manager = new ActivityManager(map, eventBus);
+ manager.setDisplay(realDisplay);
+
+ // Start an activity
+ manager.onPlaceChange(new PlaceChangeEvent(place1));
+
+ // Kill the manager
+ manager.setDisplay(null);
+
+ // The activity is ready to play
+ asyncActivity1.finish();
+
+ // Ta da, no NPE
+ }
public void testRejected() {
manager.setDisplay(realDisplay);
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors