1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package android.fragment.cts; 19 20 import static junit.framework.Assert.assertNotNull; 21 import static junit.framework.Assert.fail; 22 import static junit.framework.TestCase.assertEquals; 23 import static junit.framework.TestCase.assertFalse; 24 import static junit.framework.TestCase.assertNotSame; 25 import static junit.framework.TestCase.assertNull; 26 import static junit.framework.TestCase.assertSame; 27 import static junit.framework.TestCase.assertTrue; 28 29 import static org.mockito.Mockito.mock; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 33 import android.app.Activity; 34 import android.app.Fragment; 35 import android.app.FragmentController; 36 import android.app.FragmentHostCallback; 37 import android.app.FragmentManager; 38 import android.app.FragmentManager.FragmentLifecycleCallbacks; 39 import android.app.FragmentManagerNonConfig; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.os.Bundle; 43 import android.os.Parcelable; 44 import android.support.test.rule.ActivityTestRule; 45 import android.support.test.runner.AndroidJUnit4; 46 import android.test.suitebuilder.annotation.MediumTest; 47 import android.util.Pair; 48 import android.view.LayoutInflater; 49 import android.view.Menu; 50 import android.view.View; 51 import android.view.ViewGroup; 52 import android.view.Window; 53 import android.widget.TextView; 54 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.io.FileDescriptor; 60 import java.io.PrintWriter; 61 62 @MediumTest 63 @RunWith(AndroidJUnit4.class) 64 public class FragmentLifecycleTest { 65 66 @Rule 67 public ActivityTestRule<FragmentTestActivity> mActivityRule = 68 new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class); 69 70 @Test basicLifecycle()71 public void basicLifecycle() throws Throwable { 72 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 73 final StrictFragment strictFragment = new StrictFragment(); 74 75 // Add fragment; StrictFragment will throw if it detects any violation 76 // in standard lifecycle method ordering or expected preconditions. 77 fm.beginTransaction().add(strictFragment, "EmptyHeadless").commit(); 78 executePendingTransactions(fm); 79 80 assertTrue("fragment is not added", strictFragment.isAdded()); 81 assertFalse("fragment is detached", strictFragment.isDetached()); 82 assertTrue("fragment is not resumed", strictFragment.isResumed()); 83 84 // Test removal as well; StrictFragment will throw here too. 85 fm.beginTransaction().remove(strictFragment).commit(); 86 executePendingTransactions(fm); 87 88 assertFalse("fragment is added", strictFragment.isAdded()); 89 assertFalse("fragment is resumed", strictFragment.isResumed()); 90 91 // This one is perhaps counterintuitive; "detached" means specifically detached 92 // but still managed by a FragmentManager. The .remove call above 93 // should not enter this state. 94 assertFalse("fragment is detached", strictFragment.isDetached()); 95 } 96 97 @Test detachment()98 public void detachment() throws Throwable { 99 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 100 final StrictFragment f1 = new StrictFragment(); 101 final StrictFragment f2 = new StrictFragment(); 102 103 fm.beginTransaction().add(f1, "1").add(f2, "2").commit(); 104 executePendingTransactions(fm); 105 106 assertTrue("fragment 1 is not added", f1.isAdded()); 107 assertTrue("fragment 2 is not added", f2.isAdded()); 108 109 // Test detaching fragments using StrictFragment to throw on errors. 110 fm.beginTransaction().detach(f1).detach(f2).commit(); 111 executePendingTransactions(fm); 112 113 assertTrue("fragment 1 is not detached", f1.isDetached()); 114 assertTrue("fragment 2 is not detached", f2.isDetached()); 115 assertFalse("fragment 1 is added", f1.isAdded()); 116 assertFalse("fragment 2 is added", f2.isAdded()); 117 118 // Only reattach f1; leave v2 detached. 119 fm.beginTransaction().attach(f1).commit(); 120 executePendingTransactions(fm); 121 122 assertTrue("fragment 1 is not added", f1.isAdded()); 123 assertFalse("fragment 1 is detached", f1.isDetached()); 124 assertTrue("fragment 2 is not detached", f2.isDetached()); 125 126 // Remove both from the FragmentManager. 127 fm.beginTransaction().remove(f1).remove(f2).commit(); 128 executePendingTransactions(fm); 129 130 assertFalse("fragment 1 is added", f1.isAdded()); 131 assertFalse("fragment 2 is added", f2.isAdded()); 132 assertFalse("fragment 1 is detached", f1.isDetached()); 133 assertFalse("fragment 2 is detached", f2.isDetached()); 134 } 135 136 @Test basicBackStack()137 public void basicBackStack() throws Throwable { 138 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 139 final StrictFragment f1 = new StrictFragment(); 140 final StrictFragment f2 = new StrictFragment(); 141 142 // Add a fragment normally to set up 143 fm.beginTransaction().add(f1, "1").commit(); 144 executePendingTransactions(fm); 145 146 assertTrue("fragment 1 is not added", f1.isAdded()); 147 148 // Remove the first one and add a second. We're not using replace() here since 149 // these fragments are headless and as of this test writing, replace() only works 150 // for fragments with views and a container view id. 151 // Add it to the back stack so we can pop it afterwards. 152 fm.beginTransaction().remove(f1).add(f2, "2").addToBackStack("stack1").commit(); 153 executePendingTransactions(fm); 154 155 assertFalse("fragment 1 is added", f1.isAdded()); 156 assertTrue("fragment 2 is not added", f2.isAdded()); 157 158 // Test popping the stack 159 fm.popBackStack(); 160 executePendingTransactions(fm); 161 162 assertFalse("fragment 2 is added", f2.isAdded()); 163 assertTrue("fragment 1 is not added", f1.isAdded()); 164 } 165 166 @Test attachBackStack()167 public void attachBackStack() throws Throwable { 168 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 169 final StrictFragment f1 = new StrictFragment(); 170 final StrictFragment f2 = new StrictFragment(); 171 172 // Add a fragment normally to set up 173 fm.beginTransaction().add(f1, "1").commit(); 174 executePendingTransactions(fm); 175 176 assertTrue("fragment 1 is not added", f1.isAdded()); 177 178 fm.beginTransaction().detach(f1).add(f2, "2").addToBackStack("stack1").commit(); 179 executePendingTransactions(fm); 180 181 assertTrue("fragment 1 is not detached", f1.isDetached()); 182 assertFalse("fragment 2 is detached", f2.isDetached()); 183 assertFalse("fragment 1 is added", f1.isAdded()); 184 assertTrue("fragment 2 is not added", f2.isAdded()); 185 } 186 187 @Test viewLifecycle()188 public void viewLifecycle() throws Throwable { 189 // Test basic lifecycle when the fragment creates a view 190 191 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 192 final StrictViewFragment f1 = new StrictViewFragment(); 193 194 fm.beginTransaction().add(android.R.id.content, f1).commit(); 195 executePendingTransactions(fm); 196 197 assertTrue("fragment 1 is not added", f1.isAdded()); 198 final View view = f1.getView(); 199 assertNotNull("fragment 1 returned null from getView", view); 200 assertTrue("fragment 1's view is not attached to a window", view.isAttachedToWindow()); 201 202 fm.beginTransaction().remove(f1).commit(); 203 executePendingTransactions(fm); 204 205 assertFalse("fragment 1 is added", f1.isAdded()); 206 assertNull("fragment 1 returned non-null from getView after removal", f1.getView()); 207 assertFalse("fragment 1's previous view is still attached to a window", 208 view.isAttachedToWindow()); 209 } 210 211 @Test viewReplace()212 public void viewReplace() throws Throwable { 213 // Replace one view with another, then reverse it with the back stack 214 215 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 216 final StrictViewFragment f1 = new StrictViewFragment(); 217 final StrictViewFragment f2 = new StrictViewFragment(); 218 219 fm.beginTransaction().add(android.R.id.content, f1).commit(); 220 executePendingTransactions(fm); 221 222 assertTrue("fragment 1 is not added", f1.isAdded()); 223 224 View origView1 = f1.getView(); 225 assertNotNull("fragment 1 returned null view", origView1); 226 assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow()); 227 228 fm.beginTransaction().replace(android.R.id.content, f2).addToBackStack("stack1").commit(); 229 executePendingTransactions(fm); 230 231 assertFalse("fragment 1 is added", f1.isAdded()); 232 assertTrue("fragment 2 is added", f2.isAdded()); 233 assertNull("fragment 1 returned non-null view", f1.getView()); 234 assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow()); 235 View origView2 = f2.getView(); 236 assertNotNull("fragment 2 returned null view", origView2); 237 assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow()); 238 239 fm.popBackStack(); 240 executePendingTransactions(fm); 241 242 assertTrue("fragment 1 is not added", f1.isAdded()); 243 assertFalse("fragment 2 is added", f2.isAdded()); 244 assertNull("fragment 2 returned non-null view", f2.getView()); 245 assertFalse("fragment 2's view still attached", origView2.isAttachedToWindow()); 246 View newView1 = f1.getView(); 247 assertNotSame("fragment 1 had same view from last attachment", origView1, newView1); 248 assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow()); 249 } 250 251 @Test viewReplaceMultiple()252 public void viewReplaceMultiple() throws Throwable { 253 // Replace several views with one, then reverse it with the back stack 254 255 final FragmentManager fm = mActivityRule.getActivity().getFragmentManager(); 256 final StrictViewFragment f1 = new StrictViewFragment(); 257 final StrictViewFragment f2 = new StrictViewFragment(); 258 final StrictViewFragment f3 = new StrictViewFragment(); 259 260 fm.beginTransaction().add(android.R.id.content, f1).commit(); 261 fm.beginTransaction().add(android.R.id.content, f2).commit(); 262 executePendingTransactions(fm); 263 264 assertTrue("fragment 1 is not added", f1.isAdded()); 265 assertTrue("fragment 2 is not added", f2.isAdded()); 266 267 View origView1 = f1.getView(); 268 assertNotNull("fragment 1 returned null view", origView1); 269 assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow()); 270 assertSame(origView1, ((ViewGroup)origView1.getParent()).getChildAt(0)); 271 272 View origView2 = f2.getView(); 273 assertNotNull("fragment 2 returned null view", origView2); 274 assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow()); 275 assertSame(origView2, ((ViewGroup)origView1.getParent()).getChildAt(1)); 276 277 fm.beginTransaction().replace(android.R.id.content, f3).addToBackStack("stack1").commit(); 278 executePendingTransactions(fm); 279 280 assertFalse("fragment 1 is added", f1.isAdded()); 281 assertFalse("fragment 2 is added", f2.isAdded()); 282 assertTrue("fragment 3 is added", f3.isAdded()); 283 assertNull("fragment 1 returned non-null view", f1.getView()); 284 assertNull("fragment 2 returned non-null view", f2.getView()); 285 assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow()); 286 assertFalse("fragment 2's old view still attached", origView2.isAttachedToWindow()); 287 View origView3 = f3.getView(); 288 assertNotNull("fragment 3 returned null view", origView3); 289 assertTrue("fragment 3's view not attached", origView3.isAttachedToWindow()); 290 291 fm.popBackStack(); 292 executePendingTransactions(fm); 293 294 assertTrue("fragment 1 is not added", f1.isAdded()); 295 assertTrue("fragment 2 is not added", f2.isAdded()); 296 assertFalse("fragment 3 is added", f3.isAdded()); 297 assertNull("fragment 3 returned non-null view", f3.getView()); 298 assertFalse("fragment 3's view still attached", origView3.isAttachedToWindow()); 299 View newView1 = f1.getView(); 300 View newView2 = f2.getView(); 301 assertNotSame("fragment 1 had same view from last attachment", origView1, newView1); 302 assertNotSame("fragment 2 had same view from last attachment", origView2, newView1); 303 assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow()); 304 assertTrue("fragment 2's view not attached", newView2.isAttachedToWindow()); 305 assertSame(newView1, ((ViewGroup)newView1.getParent()).getChildAt(0)); 306 assertSame(newView2, ((ViewGroup)newView1.getParent()).getChildAt(1)); 307 } 308 309 /** 310 * This tests that fragments call onDestroy when the activity finishes. 311 */ 312 @Test fragmentDestroyedOnFinish()313 public void fragmentDestroyedOnFinish() throws Throwable { 314 final FragmentController fc = FragmentTestUtil.createController(mActivityRule); 315 FragmentTestUtil.resume(mActivityRule, fc, null); 316 final StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.text_a); 317 final StrictViewFragment fragmentB = StrictViewFragment.create(R.layout.text_b); 318 mActivityRule.runOnUiThread(() -> { 319 FragmentManager fm = fc.getFragmentManager(); 320 321 fm.beginTransaction() 322 .add(android.R.id.content, fragmentA) 323 .commit(); 324 fm.executePendingTransactions(); 325 fm.beginTransaction() 326 .replace(android.R.id.content, fragmentB) 327 .addToBackStack(null) 328 .commit(); 329 fm.executePendingTransactions(); 330 }); 331 FragmentTestUtil.destroy(mActivityRule, fc); 332 assertTrue(fragmentB.mCalledOnDestroy); 333 assertTrue(fragmentA.mCalledOnDestroy); 334 } 335 336 /** 337 * This test confirms that as long as a parent fragment has called super.onCreate, 338 * any child fragments added, committed and with transactions executed will be brought 339 * to at least the CREATED state by the time the parent fragment receives onCreateView. 340 * This means the child fragment will have received onAttach/onCreate. 341 */ 342 @Test 343 @MediumTest childFragmentManagerAttach()344 public void childFragmentManagerAttach() throws Throwable { 345 mActivityRule.runOnUiThread(new Runnable() { 346 public void run() { 347 FragmentController fc = FragmentController.createController( 348 new HostCallbacks(mActivityRule.getActivity())); 349 fc.attachHost(null); 350 fc.dispatchCreate(); 351 352 FragmentLifecycleCallbacks mockLc = mock(FragmentLifecycleCallbacks.class); 353 FragmentLifecycleCallbacks mockRecursiveLc = mock(FragmentLifecycleCallbacks.class); 354 355 FragmentManager fm = fc.getFragmentManager(); 356 fm.registerFragmentLifecycleCallbacks(mockLc, false); 357 fm.registerFragmentLifecycleCallbacks(mockRecursiveLc, true); 358 359 ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment(); 360 fm.beginTransaction() 361 .add(android.R.id.content, fragment) 362 .commitNow(); 363 364 verify(mockLc, times(1)).onFragmentCreated(fm, fragment, null); 365 366 fc.dispatchActivityCreated(); 367 368 Fragment childFragment = fragment.getChildFragment(); 369 370 verify(mockLc, times(1)).onFragmentActivityCreated(fm, fragment, null); 371 verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, fragment, null); 372 verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, childFragment, null); 373 374 fc.dispatchStart(); 375 376 verify(mockLc, times(1)).onFragmentStarted(fm, fragment); 377 verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, fragment); 378 verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, childFragment); 379 380 fc.dispatchResume(); 381 382 verify(mockLc, times(1)).onFragmentResumed(fm, fragment); 383 verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, fragment); 384 verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, childFragment); 385 386 // Confirm that the parent fragment received onAttachFragment 387 assertTrue("parent fragment did not receive onAttachFragment", 388 fragment.mCalledOnAttachFragment); 389 390 fc.dispatchStop(); 391 392 verify(mockLc, times(1)).onFragmentStopped(fm, fragment); 393 verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, fragment); 394 verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, childFragment); 395 396 fc.dispatchDestroy(); 397 398 verify(mockLc, times(1)).onFragmentDestroyed(fm, fragment); 399 verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, fragment); 400 verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, childFragment); 401 } 402 }); 403 } 404 405 /** 406 * Test to ensure that when dispatch* is called that the fragment manager 407 * doesn't cause the contained fragment states to change even if no state changes. 408 */ 409 @Test noPrematureStateChange()410 public void noPrematureStateChange() throws Throwable { 411 final FragmentController fc = FragmentTestUtil.createController(mActivityRule); 412 FragmentTestUtil.resume(mActivityRule, fc, null); 413 414 mActivityRule.runOnUiThread(() -> { 415 fc.getFragmentManager().beginTransaction() 416 .add(new StrictFragment(), "1") 417 .commitNow(); 418 }); 419 420 Pair<Parcelable, FragmentManagerNonConfig> savedState = 421 FragmentTestUtil.destroy(mActivityRule, fc); 422 423 final FragmentController fragmentController = FragmentTestUtil.createController(mActivityRule); 424 425 mActivityRule.runOnUiThread(() -> { 426 fragmentController.attachHost(null); 427 fragmentController.dispatchCreate(); 428 fragmentController.dispatchActivityCreated(); 429 fragmentController.noteStateNotSaved(); 430 fragmentController.execPendingActions(); 431 fragmentController.dispatchStart(); 432 fragmentController.reportLoaderStart(); 433 fragmentController.dispatchResume(); 434 fragmentController.restoreAllState(savedState.first, savedState.second); 435 fragmentController.dispatchResume(); 436 }); 437 438 FragmentManager fm = fragmentController.getFragmentManager(); 439 440 StrictFragment fragment1 = (StrictFragment) fm.findFragmentByTag("1"); 441 442 assertNotNull(fragment1); 443 assertFalse(fragment1.mCalledOnResume); 444 } 445 446 @Test testIsStateSaved()447 public void testIsStateSaved() throws Throwable { 448 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 449 FragmentTestUtil.resume(mActivityRule, fc, null); 450 FragmentManager fm = fc.getFragmentManager(); 451 452 mActivityRule.runOnUiThread(new Runnable() { 453 @Override 454 public void run() { 455 Fragment f = new StrictFragment(); 456 fm.beginTransaction() 457 .add(f, "1") 458 .commitNow(); 459 460 assertFalse("fragment reported state saved while resumed", 461 f.isStateSaved()); 462 463 fc.dispatchPause(); 464 fc.saveAllState(); 465 466 assertTrue("fragment reported state not saved after saveAllState", 467 f.isStateSaved()); 468 469 fc.dispatchStop(); 470 471 assertTrue("fragment reported state not saved after stop", 472 f.isStateSaved()); 473 474 fc.dispatchDestroy(); 475 476 assertFalse("fragment reported state saved after destroy", 477 f.isStateSaved()); 478 } 479 }); 480 } 481 482 @Test testSetArgumentsLifecycle()483 public void testSetArgumentsLifecycle() throws Throwable { 484 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 485 FragmentTestUtil.resume(mActivityRule, fc, null); 486 FragmentManager fm = fc.getFragmentManager(); 487 488 mActivityRule.runOnUiThread(new Runnable() { 489 @Override 490 public void run() { 491 Fragment f = new StrictFragment(); 492 f.setArguments(new Bundle()); 493 494 fm.beginTransaction() 495 .add(f, "1") 496 .commitNow(); 497 498 f.setArguments(new Bundle()); 499 500 fc.dispatchPause(); 501 fc.saveAllState(); 502 503 boolean threw = false; 504 try { 505 f.setArguments(new Bundle()); 506 } catch (IllegalStateException ise) { 507 threw = true; 508 } 509 assertTrue("fragment allowed setArguments after state save", threw); 510 511 fc.dispatchStop(); 512 513 threw = false; 514 try { 515 f.setArguments(new Bundle()); 516 } catch (IllegalStateException ise) { 517 threw = true; 518 } 519 assertTrue("fragment allowed setArguments after stop", threw); 520 521 fc.dispatchDestroy(); 522 523 // Fully destroyed, so fragments have been removed. 524 f.setArguments(new Bundle()); 525 } 526 }); 527 528 } 529 530 /* 531 * Test that target fragments are in a useful state when we restore them, even if they're 532 * on the back stack. 533 */ 534 535 @Test targetFragmentRestoreLifecycleStateBackStack()536 public void targetFragmentRestoreLifecycleStateBackStack() throws Throwable { 537 mActivityRule.runOnUiThread(new Runnable() { 538 @Override 539 public void run() { 540 final FragmentController fc1 = FragmentController.createController( 541 new HostCallbacks(mActivityRule.getActivity())); 542 543 final FragmentManager fm1 = fc1.getFragmentManager(); 544 545 fc1.attachHost(null); 546 fc1.dispatchCreate(); 547 548 final Fragment target = new TargetFragment(); 549 fm1.beginTransaction().add(target, "target").commitNow(); 550 551 final Fragment referrer = new ReferrerFragment(); 552 referrer.setTargetFragment(target, 0); 553 554 fm1.beginTransaction() 555 .remove(target) 556 .add(referrer, "referrer") 557 .addToBackStack(null) 558 .commit(); 559 560 fc1.dispatchActivityCreated(); 561 fc1.noteStateNotSaved(); 562 fc1.execPendingActions(); 563 fc1.doLoaderStart(); 564 fc1.dispatchStart(); 565 fc1.reportLoaderStart(); 566 fc1.dispatchResume(); 567 fc1.execPendingActions(); 568 569 // Bring the state back down to destroyed, simulating an activity restart 570 fc1.dispatchPause(); 571 final Parcelable savedState = fc1.saveAllState(); 572 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig(); 573 fc1.dispatchStop(); 574 fc1.dispatchDestroy(); 575 576 final FragmentController fc2 = FragmentController.createController( 577 new HostCallbacks(mActivityRule.getActivity())); 578 579 fc2.attachHost(null); 580 fc2.restoreAllState(savedState, nonconf); 581 fc2.dispatchCreate(); 582 583 fc2.dispatchActivityCreated(); 584 fc2.noteStateNotSaved(); 585 fc2.execPendingActions(); 586 fc2.doLoaderStart(); 587 fc2.dispatchStart(); 588 fc2.reportLoaderStart(); 589 fc2.dispatchResume(); 590 fc2.execPendingActions(); 591 592 // Bring the state back down to destroyed before we finish the test 593 fc2.dispatchPause(); 594 fc2.saveAllState(); 595 fc2.dispatchStop(); 596 fc2.dispatchDestroy(); 597 } 598 }); 599 } 600 601 @Test targetFragmentRestoreLifecycleStateManagerOrder()602 public void targetFragmentRestoreLifecycleStateManagerOrder() throws Throwable { 603 mActivityRule.runOnUiThread(new Runnable() { 604 @Override 605 public void run() { 606 final FragmentController fc1 = FragmentController.createController( 607 new HostCallbacks(mActivityRule.getActivity())); 608 609 final FragmentManager fm1 = fc1.getFragmentManager(); 610 611 fc1.attachHost(null); 612 fc1.dispatchCreate(); 613 614 final Fragment target1 = new TargetFragment(); 615 final Fragment referrer1 = new ReferrerFragment(); 616 referrer1.setTargetFragment(target1, 0); 617 618 fm1.beginTransaction().add(target1, "target1").add(referrer1, "referrer1").commitNow(); 619 620 final Fragment target2 = new TargetFragment(); 621 final Fragment referrer2 = new ReferrerFragment(); 622 referrer2.setTargetFragment(target2, 0); 623 624 // Order shouldn't matter. 625 fm1.beginTransaction().add(referrer2, "referrer2").add(target2, "target2").commitNow(); 626 627 fc1.dispatchActivityCreated(); 628 fc1.noteStateNotSaved(); 629 fc1.execPendingActions(); 630 fc1.doLoaderStart(); 631 fc1.dispatchStart(); 632 fc1.reportLoaderStart(); 633 fc1.dispatchResume(); 634 fc1.execPendingActions(); 635 636 // Bring the state back down to destroyed, simulating an activity restart 637 fc1.dispatchPause(); 638 final Parcelable savedState = fc1.saveAllState(); 639 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig(); 640 fc1.dispatchStop(); 641 fc1.dispatchDestroy(); 642 643 final FragmentController fc2 = FragmentController.createController( 644 new HostCallbacks(mActivityRule.getActivity())); 645 646 fc2.attachHost(null); 647 fc2.restoreAllState(savedState, nonconf); 648 fc2.dispatchCreate(); 649 650 fc2.dispatchActivityCreated(); 651 fc2.noteStateNotSaved(); 652 fc2.execPendingActions(); 653 fc2.doLoaderStart(); 654 fc2.dispatchStart(); 655 fc2.reportLoaderStart(); 656 fc2.dispatchResume(); 657 fc2.execPendingActions(); 658 659 // Bring the state back down to destroyed before we finish the test 660 fc2.dispatchPause(); 661 fc2.saveAllState(); 662 fc2.dispatchStop(); 663 fc2.dispatchDestroy(); 664 } 665 }); 666 } 667 668 // Make sure that executing transactions during activity lifecycle events 669 // is properly prevented. 670 @Test preventReentrantCalls()671 public void preventReentrantCalls() throws Throwable { 672 testLifecycleTransitionFailure(StrictFragment.ATTACHED, StrictFragment.CREATED); 673 testLifecycleTransitionFailure(StrictFragment.CREATED, StrictFragment.ACTIVITY_CREATED); 674 testLifecycleTransitionFailure(StrictFragment.ACTIVITY_CREATED, StrictFragment.STARTED); 675 testLifecycleTransitionFailure(StrictFragment.STARTED, StrictFragment.RESUMED); 676 677 testLifecycleTransitionFailure(StrictFragment.RESUMED, StrictFragment.STARTED); 678 testLifecycleTransitionFailure(StrictFragment.STARTED, StrictFragment.CREATED); 679 testLifecycleTransitionFailure(StrictFragment.CREATED, StrictFragment.ATTACHED); 680 testLifecycleTransitionFailure(StrictFragment.ATTACHED, StrictFragment.DETACHED); 681 } 682 testLifecycleTransitionFailure(int fromState, int toState)683 private void testLifecycleTransitionFailure(int fromState, int toState) throws Throwable { 684 mActivityRule.runOnUiThread(() -> { 685 final FragmentController fc1 = FragmentController.createController( 686 new HostCallbacks(mActivityRule.getActivity())); 687 FragmentTestUtil.resume(mActivityRule, fc1, null); 688 689 final FragmentManager fm1 = fc1.getFragmentManager(); 690 691 final Fragment reentrantFragment = ReentrantFragment.create(fromState, toState); 692 693 fm1.beginTransaction() 694 .add(reentrantFragment, "reentrant") 695 .commit(); 696 try { 697 fm1.executePendingTransactions(); 698 } catch (IllegalStateException e) { 699 fail("An exception shouldn't happen when initially adding the fragment"); 700 } 701 702 // Now shut down the fragment controller. When fromState > toState, this should 703 // result in an exception 704 Pair<Parcelable, FragmentManagerNonConfig> savedState = null; 705 try { 706 savedState = FragmentTestUtil.destroy(mActivityRule, fc1); 707 if (fromState > toState) { 708 fail("Expected IllegalStateException when moving from " 709 + StrictFragment.stateToString(fromState) + " to " 710 + StrictFragment.stateToString(toState)); 711 } 712 } catch (IllegalStateException e) { 713 if (fromState < toState) { 714 fail("Unexpected IllegalStateException when moving from " 715 + StrictFragment.stateToString(fromState) + " to " 716 + StrictFragment.stateToString(toState)); 717 } 718 return; // test passed! 719 } 720 721 // now restore from saved state. This will be reached when 722 // fromState < toState. We want to catch the fragment while it 723 // is being restored as the fragment controller state is being brought up. 724 725 final FragmentController fc2 = FragmentController.createController( 726 new HostCallbacks(mActivityRule.getActivity())); 727 try { 728 FragmentTestUtil.resume(mActivityRule, fc2, savedState); 729 730 fail("Expected IllegalStateException when moving from " 731 + StrictFragment.stateToString(fromState) + " to " 732 + StrictFragment.stateToString(toState)); 733 } catch (IllegalStateException e) { 734 // expected, so the test passed! 735 } 736 }); 737 } 738 739 @Test targetFragmentNoCycles()740 public void targetFragmentNoCycles() throws Throwable { 741 final Fragment one = new Fragment(); 742 final Fragment two = new Fragment(); 743 final Fragment three = new Fragment(); 744 745 try { 746 one.setTargetFragment(two, 0); 747 two.setTargetFragment(three, 0); 748 three.setTargetFragment(one, 0); 749 assertTrue("creating a fragment target cycle did not throw IllegalArgumentException", 750 false); 751 } catch (IllegalArgumentException e) { 752 // Success! 753 } 754 } 755 756 @Test targetFragmentSetClear()757 public void targetFragmentSetClear() throws Throwable { 758 final Fragment one = new Fragment(); 759 final Fragment two = new Fragment(); 760 761 one.setTargetFragment(two, 0); 762 one.setTargetFragment(null, 0); 763 } 764 765 /** 766 * When a fragment is saved in non-config, it should be restored to the same index. 767 */ 768 @Test restoreNonConfig()769 public void restoreNonConfig() throws Throwable { 770 mActivityRule.runOnUiThread(() -> { 771 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 772 FragmentTestUtil.resume(mActivityRule, fc, null); 773 FragmentManager fm = fc.getFragmentManager(); 774 775 Fragment fragment1 = new StrictFragment(); 776 fm.beginTransaction() 777 .add(fragment1, "1") 778 .addToBackStack(null) 779 .commit(); 780 fm.executePendingTransactions(); 781 Fragment fragment2 = new StrictFragment(); 782 fragment2.setRetainInstance(true); 783 fragment2.setTargetFragment(fragment1, 0); 784 Fragment fragment3 = new StrictFragment(); 785 fm.beginTransaction() 786 .remove(fragment1) 787 .add(fragment2, "2") 788 .add(fragment3, "3") 789 .addToBackStack(null) 790 .commit(); 791 fm.executePendingTransactions(); 792 793 Pair<Parcelable, FragmentManagerNonConfig> savedState = 794 FragmentTestUtil.destroy(mActivityRule, fc); 795 796 fc = FragmentTestUtil.createController(mActivityRule); 797 FragmentTestUtil.resume(mActivityRule, fc, savedState); 798 boolean foundFragment2 = false; 799 for (Fragment fragment : fc.getFragmentManager().getFragments()) { 800 if (fragment == fragment2) { 801 foundFragment2 = true; 802 assertNotNull(fragment.getTargetFragment()); 803 assertEquals("1", fragment.getTargetFragment().getTag()); 804 } else { 805 assertFalse("2".equals(fragment.getTag())); 806 } 807 } 808 assertTrue(foundFragment2); 809 }); 810 } 811 812 /** 813 * Check that retained fragments in the backstack correctly restored after two "configChanges" 814 */ 815 @Test retainedFragmentInBackstack()816 public void retainedFragmentInBackstack() throws Throwable { 817 mActivityRule.runOnUiThread(() -> { 818 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 819 FragmentTestUtil.resume(mActivityRule, fc, null); 820 FragmentManager fm = fc.getFragmentManager(); 821 822 Fragment fragment1 = new StrictFragment(); 823 fm.beginTransaction() 824 .add(fragment1, "1") 825 .addToBackStack(null) 826 .commit(); 827 fm.executePendingTransactions(); 828 829 Fragment child = new StrictFragment(); 830 child.setRetainInstance(true); 831 fragment1.getChildFragmentManager().beginTransaction() 832 .add(child, "child").commit(); 833 fragment1.getChildFragmentManager().executePendingTransactions(); 834 835 Fragment fragment2 = new StrictFragment(); 836 fm.beginTransaction() 837 .remove(fragment1) 838 .add(fragment2, "2") 839 .addToBackStack(null) 840 .commit(); 841 fm.executePendingTransactions(); 842 843 Pair<Parcelable, FragmentManagerNonConfig> savedState = 844 FragmentTestUtil.destroy(mActivityRule, fc); 845 846 fc = FragmentTestUtil.createController(mActivityRule); 847 FragmentTestUtil.resume(mActivityRule, fc, savedState); 848 savedState = FragmentTestUtil.destroy(mActivityRule, fc); 849 fc = FragmentTestUtil.createController(mActivityRule); 850 FragmentTestUtil.resume(mActivityRule, fc, savedState); 851 fm = fc.getFragmentManager(); 852 fm.popBackStackImmediate(); 853 Fragment retainedChild = fm.findFragmentByTag("1") 854 .getChildFragmentManager().findFragmentByTag("child"); 855 assertEquals(child, retainedChild); 856 }); 857 } 858 859 /** 860 * When a fragment has been optimized out, it state should still be saved during 861 * save and restore instance state. 862 */ 863 @Test saveRemovedFragment()864 public void saveRemovedFragment() throws Throwable { 865 mActivityRule.runOnUiThread(() -> { 866 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 867 FragmentTestUtil.resume(mActivityRule, fc, null); 868 FragmentManager fm = fc.getFragmentManager(); 869 870 SaveStateFragment fragment1 = SaveStateFragment.create(1); 871 fm.beginTransaction() 872 .add(android.R.id.content, fragment1, "1") 873 .addToBackStack(null) 874 .commit(); 875 SaveStateFragment fragment2 = SaveStateFragment.create(2); 876 fm.beginTransaction() 877 .replace(android.R.id.content, fragment2, "2") 878 .addToBackStack(null) 879 .commit(); 880 fm.executePendingTransactions(); 881 882 Pair<Parcelable, FragmentManagerNonConfig> savedState = 883 FragmentTestUtil.destroy(mActivityRule, fc); 884 885 fc = FragmentTestUtil.createController(mActivityRule); 886 FragmentTestUtil.resume(mActivityRule, fc, savedState); 887 fm = fc.getFragmentManager(); 888 fragment2 = (SaveStateFragment) fm.findFragmentByTag("2"); 889 assertNotNull(fragment2); 890 assertEquals(2, fragment2.getValue()); 891 fm.popBackStackImmediate(); 892 fragment1 = (SaveStateFragment) fm.findFragmentByTag("1"); 893 assertNotNull(fragment1); 894 assertEquals(1, fragment1.getValue()); 895 }); 896 } 897 898 /** 899 * When there are no retained instance fragments, the FragmentManagerNonConfig should be 900 * null 901 */ 902 @Test nullNonConfig()903 public void nullNonConfig() throws Throwable { 904 mActivityRule.runOnUiThread(() -> { 905 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 906 FragmentTestUtil.resume(mActivityRule, fc, null); 907 FragmentManager fm = fc.getFragmentManager(); 908 909 Fragment fragment1 = new StrictFragment(); 910 fm.beginTransaction() 911 .add(fragment1, "1") 912 .addToBackStack(null) 913 .commit(); 914 fm.executePendingTransactions(); 915 Pair<Parcelable, FragmentManagerNonConfig> savedState = 916 FragmentTestUtil.destroy(mActivityRule, fc); 917 assertNull(savedState.second); 918 }); 919 } 920 921 /** 922 * When the FragmentManager state changes, the pending transactions should execute. 923 */ 924 @Test runTransactionsOnChange()925 public void runTransactionsOnChange() throws Throwable { 926 mActivityRule.runOnUiThread(() -> { 927 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 928 FragmentTestUtil.resume(mActivityRule, fc, null); 929 FragmentManager fm = fc.getFragmentManager(); 930 931 RemoveHelloInOnResume fragment1 = new RemoveHelloInOnResume(); 932 StrictFragment fragment2 = new StrictFragment(); 933 fm.beginTransaction() 934 .add(fragment1, "1") 935 .setReorderingAllowed(false) 936 .commit(); 937 fm.beginTransaction() 938 .add(fragment2, "Hello") 939 .setReorderingAllowed(false) 940 .commit(); 941 fm.executePendingTransactions(); 942 943 assertEquals(2, fm.getFragments().size()); 944 assertTrue(fm.getFragments().contains(fragment1)); 945 assertTrue(fm.getFragments().contains(fragment2)); 946 947 Pair<Parcelable, FragmentManagerNonConfig> savedState = 948 FragmentTestUtil.destroy(mActivityRule, fc); 949 fc = FragmentTestUtil.createController(mActivityRule); 950 FragmentTestUtil.resume(mActivityRule, fc, savedState); 951 fm = fc.getFragmentManager(); 952 953 assertEquals(1, fm.getFragments().size()); 954 for (Fragment fragment : fm.getFragments()) { 955 assertTrue(fragment instanceof RemoveHelloInOnResume); 956 } 957 }); 958 } 959 960 @Test optionsMenu()961 public void optionsMenu() throws Throwable { 962 mActivityRule.runOnUiThread(() -> { 963 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 964 FragmentTestUtil.resume(mActivityRule, fc, null); 965 FragmentManager fm = fc.getFragmentManager(); 966 967 InvalidateOptionFragment fragment = new InvalidateOptionFragment(); 968 fm.beginTransaction() 969 .add(android.R.id.content, fragment) 970 .commit(); 971 fm.executePendingTransactions(); 972 973 Menu menu = mock(Menu.class); 974 fc.dispatchPrepareOptionsMenu(menu); 975 assertTrue(fragment.onPrepareOptionsMenuCalled); 976 fragment.onPrepareOptionsMenuCalled = false; 977 FragmentTestUtil.destroy(mActivityRule, fc); 978 fc.dispatchPrepareOptionsMenu(menu); 979 assertFalse(fragment.onPrepareOptionsMenuCalled); 980 }); 981 } 982 983 984 /** 985 * When a retained instance fragment is saved while in the back stack, it should go 986 * through onCreate() when it is popped back. 987 */ 988 @Test retainInstanceWithOnCreate()989 public void retainInstanceWithOnCreate() throws Throwable { 990 mActivityRule.runOnUiThread(() -> { 991 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 992 FragmentTestUtil.resume(mActivityRule, fc, null); 993 FragmentManager fm = fc.getFragmentManager(); 994 995 OnCreateFragment fragment1 = new OnCreateFragment(); 996 997 fm.beginTransaction() 998 .add(fragment1, "1") 999 .commit(); 1000 fm.beginTransaction() 1001 .remove(fragment1) 1002 .addToBackStack(null) 1003 .commit(); 1004 1005 Pair<Parcelable, FragmentManagerNonConfig> savedState = 1006 FragmentTestUtil.destroy(mActivityRule, fc); 1007 Pair<Parcelable, FragmentManagerNonConfig> restartState = 1008 Pair.create(savedState.first, null); 1009 1010 fc = FragmentTestUtil.createController(mActivityRule); 1011 FragmentTestUtil.resume(mActivityRule, fc, restartState); 1012 1013 // Save again, but keep the state 1014 savedState = FragmentTestUtil.destroy(mActivityRule, fc); 1015 1016 fc = FragmentTestUtil.createController(mActivityRule); 1017 FragmentTestUtil.resume(mActivityRule, fc, savedState); 1018 1019 fm = fc.getFragmentManager(); 1020 1021 fm.popBackStackImmediate(); 1022 OnCreateFragment fragment2 = (OnCreateFragment) fm.findFragmentByTag("1"); 1023 assertTrue(fragment2.onCreateCalled); 1024 fm.popBackStackImmediate(); 1025 }); 1026 } 1027 1028 /** 1029 * A retained instance fragment should go through onCreate() once, even through save and 1030 * restore. 1031 */ 1032 @Test retainInstanceOneOnCreate()1033 public void retainInstanceOneOnCreate() throws Throwable { 1034 mActivityRule.runOnUiThread(() -> { 1035 FragmentController fc = FragmentTestUtil.createController(mActivityRule); 1036 FragmentTestUtil.resume(mActivityRule, fc, null); 1037 FragmentManager fm = fc.getFragmentManager(); 1038 1039 OnCreateFragment fragment = new OnCreateFragment(); 1040 1041 fm.beginTransaction() 1042 .add(fragment, "fragment") 1043 .commit(); 1044 fm.executePendingTransactions(); 1045 1046 fm.beginTransaction() 1047 .remove(fragment) 1048 .addToBackStack(null) 1049 .commit(); 1050 1051 assertTrue(fragment.onCreateCalled); 1052 fragment.onCreateCalled = false; 1053 1054 Pair<Parcelable, FragmentManagerNonConfig> savedState = 1055 FragmentTestUtil.destroy(mActivityRule, fc); 1056 1057 fc = FragmentTestUtil.createController(mActivityRule); 1058 FragmentTestUtil.resume(mActivityRule, fc, savedState); 1059 fm = fc.getFragmentManager(); 1060 1061 fm.popBackStackImmediate(); 1062 assertFalse(fragment.onCreateCalled); 1063 }); 1064 } 1065 executePendingTransactions(final FragmentManager fm)1066 private void executePendingTransactions(final FragmentManager fm) throws Throwable { 1067 mActivityRule.runOnUiThread(new Runnable() { 1068 @Override 1069 public void run() { 1070 fm.executePendingTransactions(); 1071 } 1072 }); 1073 } 1074 1075 /** 1076 * This tests a deliberately odd use of a child fragment, added in onCreateView instead 1077 * of elsewhere. It simulates creating a UI child fragment added to the view hierarchy 1078 * created by this fragment. 1079 */ 1080 public static class ChildFragmentManagerFragment extends StrictFragment { 1081 private FragmentManager mSavedChildFragmentManager; 1082 private ChildFragmentManagerChildFragment mChildFragment; 1083 1084 @Override onAttach(Context context)1085 public void onAttach(Context context) { 1086 super.onAttach(context); 1087 mSavedChildFragmentManager = getChildFragmentManager(); 1088 } 1089 1090 1091 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)1092 public View onCreateView(LayoutInflater inflater, ViewGroup container, 1093 Bundle savedInstanceState) { 1094 assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager, 1095 getChildFragmentManager()); 1096 ChildFragmentManagerChildFragment child = 1097 (ChildFragmentManagerChildFragment) mSavedChildFragmentManager 1098 .findFragmentByTag("tag"); 1099 if (child == null) { 1100 child = new ChildFragmentManagerChildFragment("foo"); 1101 mSavedChildFragmentManager.beginTransaction() 1102 .add(child, "tag") 1103 .commitNow(); 1104 assertEquals("argument strings don't match", "foo", child.getString()); 1105 } 1106 mChildFragment = child; 1107 return new TextView(container.getContext()); 1108 } 1109 1110 getChildFragment()1111 public Fragment getChildFragment() { 1112 return mChildFragment; 1113 } 1114 } 1115 1116 public static class ChildFragmentManagerChildFragment extends StrictFragment { 1117 private String mString; 1118 ChildFragmentManagerChildFragment()1119 public ChildFragmentManagerChildFragment() { 1120 } 1121 ChildFragmentManagerChildFragment(String arg)1122 public ChildFragmentManagerChildFragment(String arg) { 1123 final Bundle b = new Bundle(); 1124 b.putString("string", arg); 1125 setArguments(b); 1126 } 1127 1128 @Override onAttach(Context context)1129 public void onAttach(Context context) { 1130 super.onAttach(context); 1131 mString = getArguments().getString("string", "NO VALUE"); 1132 } 1133 getString()1134 public String getString() { 1135 return mString; 1136 } 1137 } 1138 1139 public static class TargetFragment extends Fragment { 1140 public boolean calledCreate; 1141 1142 @Override onCreate(Bundle savedInstanceState)1143 public void onCreate(Bundle savedInstanceState) { 1144 super.onCreate(savedInstanceState); 1145 calledCreate = true; 1146 } 1147 } 1148 1149 public static class ReferrerFragment extends Fragment { 1150 @Override onCreate(Bundle savedInstanceState)1151 public void onCreate(Bundle savedInstanceState) { 1152 super.onCreate(savedInstanceState); 1153 1154 Fragment target = getTargetFragment(); 1155 assertNotNull("target fragment was null during referrer onCreate", target); 1156 1157 if (!(target instanceof TargetFragment)) { 1158 throw new IllegalStateException("target fragment was not a TargetFragment"); 1159 } 1160 1161 assertTrue("target fragment has not yet been created", 1162 ((TargetFragment) target).calledCreate); 1163 } 1164 } 1165 1166 static class HostCallbacks extends FragmentHostCallback<Activity> { 1167 private final Activity mActivity; 1168 HostCallbacks(Activity activity)1169 public HostCallbacks(Activity activity) { 1170 super(activity, null, 0); 1171 mActivity = activity; 1172 } 1173 1174 @Override onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1175 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1176 } 1177 1178 @Override onShouldSaveFragmentState(Fragment fragment)1179 public boolean onShouldSaveFragmentState(Fragment fragment) { 1180 return !mActivity.isFinishing(); 1181 } 1182 1183 @Override onGetLayoutInflater()1184 public LayoutInflater onGetLayoutInflater() { 1185 return mActivity.getLayoutInflater().cloneInContext(mActivity); 1186 } 1187 1188 @Override onGetHost()1189 public Activity onGetHost() { 1190 return mActivity; 1191 } 1192 1193 @Override onStartActivityFromFragment( Fragment fragment, Intent intent, int requestCode, Bundle options)1194 public void onStartActivityFromFragment( 1195 Fragment fragment, Intent intent, int requestCode, Bundle options) { 1196 mActivity.startActivityFromFragment(fragment, intent, requestCode, options); 1197 } 1198 1199 @Override onRequestPermissionsFromFragment( Fragment fragment, String[] permissions, int requestCode)1200 public void onRequestPermissionsFromFragment( Fragment fragment, 1201 String[] permissions, int requestCode) { 1202 throw new UnsupportedOperationException(); 1203 } 1204 1205 @Override onHasWindowAnimations()1206 public boolean onHasWindowAnimations() { 1207 return mActivity.getWindow() != null; 1208 } 1209 1210 @Override onGetWindowAnimations()1211 public int onGetWindowAnimations() { 1212 final Window w = mActivity.getWindow(); 1213 return (w == null) ? 0 : w.getAttributes().windowAnimations; 1214 } 1215 1216 @Override onAttachFragment(Fragment fragment)1217 public void onAttachFragment(Fragment fragment) { 1218 mActivity.onAttachFragment(fragment); 1219 } 1220 1221 @Override onFindViewById(int id)1222 public View onFindViewById(int id) { 1223 return mActivity.findViewById(id); 1224 } 1225 1226 @Override onHasView()1227 public boolean onHasView() { 1228 final Window w = mActivity.getWindow(); 1229 return (w != null && w.peekDecorView() != null); 1230 } 1231 } 1232 1233 public static class SaveStateFragment extends Fragment { 1234 private static final String VALUE_KEY = "SaveStateFragment.mValue"; 1235 private int mValue; 1236 create(int value)1237 public static SaveStateFragment create(int value) { 1238 SaveStateFragment saveStateFragment = new SaveStateFragment(); 1239 saveStateFragment.mValue = value; 1240 return saveStateFragment; 1241 } 1242 1243 @Override onSaveInstanceState(Bundle outState)1244 public void onSaveInstanceState(Bundle outState) { 1245 super.onSaveInstanceState(outState); 1246 outState.putInt(VALUE_KEY, mValue); 1247 } 1248 1249 @Override onCreate(Bundle savedInstanceState)1250 public void onCreate(Bundle savedInstanceState) { 1251 super.onCreate(savedInstanceState); 1252 if (savedInstanceState != null) { 1253 mValue = savedInstanceState.getInt(VALUE_KEY, mValue); 1254 } 1255 } 1256 getValue()1257 public int getValue() { 1258 return mValue; 1259 } 1260 } 1261 1262 public static class RemoveHelloInOnResume extends Fragment { 1263 @Override onResume()1264 public void onResume() { 1265 super.onResume(); 1266 Fragment fragment = getFragmentManager().findFragmentByTag("Hello"); 1267 if (fragment != null) { 1268 getFragmentManager().beginTransaction().remove(fragment).commit(); 1269 } 1270 } 1271 } 1272 1273 public static class InvalidateOptionFragment extends Fragment { 1274 public boolean onPrepareOptionsMenuCalled; 1275 InvalidateOptionFragment()1276 public InvalidateOptionFragment() { 1277 setHasOptionsMenu(true); 1278 } 1279 1280 @Override onPrepareOptionsMenu(Menu menu)1281 public void onPrepareOptionsMenu(Menu menu) { 1282 onPrepareOptionsMenuCalled = true; 1283 assertNotNull(getContext()); 1284 super.onPrepareOptionsMenu(menu); 1285 } 1286 } 1287 1288 public static class OnCreateFragment extends Fragment { 1289 public boolean onCreateCalled; 1290 OnCreateFragment()1291 public OnCreateFragment() { 1292 setRetainInstance(true); 1293 } 1294 1295 @Override onCreate(Bundle savedInstanceState)1296 public void onCreate(Bundle savedInstanceState) { 1297 super.onCreate(savedInstanceState); 1298 onCreateCalled = true; 1299 } 1300 } 1301 } 1302