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