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 18package android.app; 19 20import android.content.Context; 21import android.os.Handler; 22import android.os.Parcelable; 23import android.support.test.filters.MediumTest; 24import android.support.test.rule.ActivityTestRule; 25import android.support.test.runner.AndroidJUnit4; 26import android.util.ArrayMap; 27import org.junit.Rule; 28import org.junit.Test; 29import org.junit.runner.RunWith; 30 31import static junit.framework.TestCase.assertNotNull; 32import static junit.framework.TestCase.assertNotSame; 33import static junit.framework.TestCase.assertSame; 34 35@RunWith(AndroidJUnit4.class) 36public class LoaderLifecycleTest { 37 @Rule 38 public ActivityTestRule<EmptyActivity> mActivityRule = 39 new ActivityTestRule<>(EmptyActivity.class); 40 @Test 41 @MediumTest 42 public void loaderIdentityTest() throws Throwable{ 43 mActivityRule.runOnUiThread(() -> { 44 final Handler h = new Handler(); 45 final FragmentController fc1 = FragmentController.createController( 46 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0)); 47 48 fc1.attachHost(null); 49 fc1.dispatchCreate(); 50 51 final FragmentManager fm1 = fc1.getFragmentManager(); 52 53 final Fragment f1 = new Fragment(); 54 fm1.beginTransaction().add(f1, "one").commitNow(); 55 56 // Removing and re-adding a fragment completely will destroy its LoaderManager. 57 // Keep the first one here to confirm this later. 58 final LoaderManager lm1 = f1.getLoaderManager(); 59 60 // Remove the fragment, add a second one, and re-add the first to 61 // force its internal index to change. The tests below should still remain consistent. 62 final Fragment f2 = new Fragment(); 63 fm1.beginTransaction().remove(f1).commitNow(); 64 fm1.beginTransaction().add(f2, "two").commitNow(); 65 fm1.beginTransaction().add(f1, "one").commitNow(); 66 67 // We'll check this to see if we get the same instance back later 68 // as passed through NonConfigurationInstance. If the keys stay consistent 69 // across fragment remove/re-add, this will be consistent. 70 final LoaderManager lm12 = f1.getLoaderManager(); 71 72 assertNotSame("fully removed and re-added fragment got same LoaderManager", lm1, lm12); 73 74 fc1.dispatchActivityCreated(); 75 fc1.noteStateNotSaved(); 76 fc1.execPendingActions(); 77 fc1.doLoaderStart(); 78 fc1.dispatchStart(); 79 fc1.reportLoaderStart(); 80 fc1.dispatchResume(); 81 fc1.execPendingActions(); 82 83 // Bring the state back down to destroyed, simulating an activity restart 84 fc1.dispatchPause(); 85 final Parcelable savedState = fc1.saveAllState(); 86 fc1.doLoaderStop(true); 87 fc1.dispatchStop(); 88 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig(); 89 90 final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig(); 91 assertNotNull("loaderNonConfig was null", loaderNonConfig); 92 93 fc1.dispatchDestroy(); 94 95 // Create the new controller and restore state 96 final FragmentController fc2 = FragmentController.createController( 97 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0)); 98 99 final FragmentManager fm2 = fc2.getFragmentManager(); 100 101 fc2.attachHost(null); 102 // Make sure nothing blows up on a null here 103 fc2.restoreLoaderNonConfig(null); 104 // for real this time 105 fc2.restoreLoaderNonConfig(loaderNonConfig); 106 fc2.restoreAllState(savedState, nonconf); 107 fc2.dispatchCreate(); 108 109 110 fc2.dispatchActivityCreated(); 111 fc2.noteStateNotSaved(); 112 fc2.execPendingActions(); 113 fc2.doLoaderStart(); 114 fc2.dispatchStart(); 115 fc2.reportLoaderStart(); 116 fc2.dispatchResume(); 117 fc2.execPendingActions(); 118 119 // Test that the fragments are in the configuration we expect 120 final Fragment restoredOne = fm2.findFragmentByTag("one"); 121 final LoaderManager lm2 = restoredOne.getLoaderManager(); 122 123 assertSame("didn't get same LoaderManager instance back", lm2, lm12); 124 125 // Bring the state back down to destroyed before we finish the test 126 fc2.dispatchPause(); 127 fc2.saveAllState(); 128 fc2.dispatchStop(); 129 fc2.dispatchDestroy(); 130 }); 131 } 132 133 @Test 134 @MediumTest 135 public void backStackLoaderIdentityTest() throws Throwable{ 136 mActivityRule.runOnUiThread(() -> { 137 final Handler h = new Handler(); 138 final FragmentHostCallback host1 = 139 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0); 140 final FragmentController fc1 = FragmentController.createController(host1); 141 142 fc1.attachHost(null); 143 fc1.dispatchCreate(); 144 145 final FragmentManager fm1 = fc1.getFragmentManager(); 146 147 final Fragment f1 = new Fragment(); 148 fm1.beginTransaction().add(f1, "one").commitNow(); 149 150 final LoaderManager lm1 = f1.getLoaderManager(); 151 152 // Put the fragment on the back stack. 153 fm1.beginTransaction().remove(f1).addToBackStack("backentry").commit(); 154 fm1.executePendingTransactions(); 155 156 fc1.dispatchActivityCreated(); 157 fc1.noteStateNotSaved(); 158 fc1.execPendingActions(); 159 fc1.doLoaderStart(); 160 fc1.dispatchStart(); 161 fc1.reportLoaderStart(); 162 fc1.dispatchResume(); 163 fc1.execPendingActions(); 164 165 // Bring the state back down to destroyed, simulating an activity restart 166 fc1.dispatchPause(); 167 final Parcelable savedState = fc1.saveAllState(); 168 fc1.doLoaderStop(true); 169 fc1.dispatchStop(); 170 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig(); 171 172 final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig(); 173 assertNotNull("loaderNonConfig was null", loaderNonConfig); 174 175 fc1.dispatchDestroy(); 176 177 // Create the new controller and restore state 178 final FragmentHostCallback host2 = 179 new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0); 180 final FragmentController fc2 = FragmentController.createController(host2); 181 182 final FragmentManager fm2 = fc2.getFragmentManager(); 183 184 fc2.attachHost(null); 185 fc2.restoreLoaderNonConfig(loaderNonConfig); 186 fc2.restoreAllState(savedState, nonconf); 187 fc2.dispatchCreate(); 188 189 190 fc2.dispatchActivityCreated(); 191 fc2.noteStateNotSaved(); 192 fc2.execPendingActions(); 193 fc2.doLoaderStart(); 194 fc2.dispatchStart(); 195 fc2.reportLoaderStart(); 196 fc2.dispatchResume(); 197 fc2.execPendingActions(); 198 199 assertNotSame("LoaderManager kept reference to old FragmentHostCallback", 200 host1, lm1.getFragmentHostCallback()); 201 assertSame("LoaderManager did not refrence new FragmentHostCallback", 202 host2, lm1.getFragmentHostCallback()); 203 204 // Test that the fragments are in the configuration we expect 205 final Fragment restoredOne = fm2.findFragmentByTag("one"); 206 final LoaderManager lm2 = restoredOne.getLoaderManager(); 207 208 assertSame("didn't get same LoaderManager instance back", lm2, lm1); 209 210 // Bring the state back down to destroyed before we finish the test 211 fc2.dispatchPause(); 212 fc2.saveAllState(); 213 fc2.dispatchStop(); 214 fc2.dispatchDestroy(); 215 }); 216 } 217 218 public class TestFragmentHostCallback extends FragmentHostCallback<LoaderLifecycleTest> { 219 public TestFragmentHostCallback(Context context, Handler handler, int windowAnimations) { 220 super(context, handler, windowAnimations); 221 } 222 223 @Override 224 public LoaderLifecycleTest onGetHost() { 225 return LoaderLifecycleTest.this; 226 } 227 } 228} 229