blob: a52fb1a07b84dbfc45058f42d3b57141f08d64d5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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
17package android.app;
18
Mathew Inwood61e8ae62018-08-14 14:17:44 +010019import android.annotation.UnsupportedAppUsage;
Bryce Lee886e7fe2018-01-23 16:18:32 -080020import android.app.ActivityThread.ActivityClientRecord;
21import android.app.servertransaction.PendingTransactionActions;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Intent;
23import android.content.pm.ActivityInfo;
24import android.os.Binder;
25import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.util.Log;
27import android.view.Window;
Andrii Kulian88e05cb2017-12-05 17:21:10 -080028
Dianne Hackborn85d558c2014-11-04 10:31:54 -080029import com.android.internal.content.ReferrerIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
31import java.util.ArrayList;
32import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import java.util.Map;
34
35/**
Dianne Hackborn2f048832011-06-16 13:31:57 -070036 * <p>Helper class for managing multiple running embedded activities in the same
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 * process. This class is not normally used directly, but rather created for
38 * you as part of the {@link android.app.ActivityGroup} implementation.
39 *
40 * @see ActivityGroup
Dianne Hackborn271c2fe2011-08-09 19:35:13 -070041 *
42 * @deprecated Use the new {@link Fragment} and {@link FragmentManager} APIs
43 * instead; these are also
44 * available on older platforms through the Android compatibility package.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 */
Dianne Hackborn2f048832011-06-16 13:31:57 -070046@Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047public class LocalActivityManager {
48 private static final String TAG = "LocalActivityManager";
Romain Guyd315ee92010-06-03 11:03:18 -070049 private static final boolean localLOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
51 // Internal token for an Activity being managed by LocalActivityManager.
52 private static class LocalActivityRecord extends Binder {
53 LocalActivityRecord(String _id, Intent _intent) {
54 id = _id;
55 intent = _intent;
56 }
57
58 final String id; // Unique name of this record.
59 Intent intent; // Which activity to run here.
60 ActivityInfo activityInfo; // Package manager info about activity.
61 Activity activity; // Currently instantiated activity.
62 Window window; // Activity's top-level window.
63 Bundle instanceState; // Last retrieved freeze state.
64 int curState = RESTORED; // Current state the activity is in.
65 }
66
67 static final int RESTORED = 0; // State restored, but no startActivity().
68 static final int INITIALIZING = 1; // Ready to launch (after startActivity()).
69 static final int CREATED = 2; // Created, not started or resumed.
70 static final int STARTED = 3; // Created and started, not resumed.
71 static final int RESUMED = 4; // Created started and resumed.
72 static final int DESTROYED = 5; // No longer with us.
73
74 /** Thread our activities are running in. */
75 private final ActivityThread mActivityThread;
76 /** The containing activity that owns the activities we create. */
Mathew Inwood61e8ae62018-08-14 14:17:44 +010077 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private final Activity mParent;
79
80 /** The activity that is currently resumed. */
Mathew Inwood61e8ae62018-08-14 14:17:44 +010081 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private LocalActivityRecord mResumed;
83 /** id -> record of all known activities. */
Mathew Inwood61e8ae62018-08-14 14:17:44 +010084 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private final Map<String, LocalActivityRecord> mActivities
86 = new HashMap<String, LocalActivityRecord>();
87 /** array of all known activities for easy iterating. */
Mathew Inwood61e8ae62018-08-14 14:17:44 +010088 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private final ArrayList<LocalActivityRecord> mActivityArray
90 = new ArrayList<LocalActivityRecord>();
91
92 /** True if only one activity can be resumed at a time */
Mathew Inwood61e8ae62018-08-14 14:17:44 +010093 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private boolean mSingleMode;
95
96 /** Set to true once we find out the container is finishing. */
97 private boolean mFinishing;
98
99 /** Current state the owner (ActivityGroup) is in */
100 private int mCurState = INITIALIZING;
101
102 /** String ids of running activities starting with least recently used. */
103 // TODO: put back in stopping of activities.
104 //private List<LocalActivityRecord> mLRU = new ArrayList();
105
106 /**
107 * Create a new LocalActivityManager for holding activities running within
108 * the given <var>parent</var>.
109 *
110 * @param parent the host of the embedded activities
111 * @param singleMode True if the LocalActivityManger should keep a maximum
112 * of one activity resumed
113 */
114 public LocalActivityManager(Activity parent, boolean singleMode) {
115 mActivityThread = ActivityThread.currentActivityThread();
116 mParent = parent;
117 mSingleMode = singleMode;
118 }
119
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100120 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private void moveToState(LocalActivityRecord r, int desiredState) {
122 if (r.curState == RESTORED || r.curState == DESTROYED) {
123 // startActivity() has not yet been called, so nothing to do.
124 return;
125 }
126
127 if (r.curState == INITIALIZING) {
128 // Get the lastNonConfigurationInstance for the activity
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -0700129 HashMap<String, Object> lastNonConfigurationInstances =
130 mParent.getLastNonConfigurationChildInstances();
131 Object instanceObj = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 if (lastNonConfigurationInstances != null) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -0700133 instanceObj = lastNonConfigurationInstances.get(r.id);
134 }
135 Activity.NonConfigurationInstances instance = null;
136 if (instanceObj != null) {
137 instance = new Activity.NonConfigurationInstances();
138 instance.activity = instanceObj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 }
140
141 // We need to have always created the activity.
142 if (localLOGV) Log.v(TAG, r.id + ": starting " + r.intent);
143 if (r.activityInfo == null) {
144 r.activityInfo = mActivityThread.resolveActivityInfo(r.intent);
145 }
146 r.activity = mActivityThread.startActivityNow(
147 mParent, r.id, r.intent, r.activityInfo, r, r.instanceState, instance);
148 if (r.activity == null) {
149 return;
150 }
151 r.window = r.activity.getWindow();
152 r.instanceState = null;
Bryce Lee886e7fe2018-01-23 16:18:32 -0800153
154 final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
155 final PendingTransactionActions pendingActions;
156
157 if (!r.activity.mFinished) {
158 // This matches pending actions set in ActivityThread#handleLaunchActivity
159 pendingActions = new PendingTransactionActions();
160 pendingActions.setOldState(clientRecord.state);
161 pendingActions.setRestoreInstanceState(true);
162 pendingActions.setCallOnPostCreate(true);
163 } else {
164 pendingActions = null;
165 }
166
167 mActivityThread.handleStartActivity(clientRecord, pendingActions);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 r.curState = STARTED;
169
170 if (desiredState == RESUMED) {
171 if (localLOGV) Log.v(TAG, r.id + ": resuming");
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800172 mActivityThread.performResumeActivity(r, true, "moveToState-INITIALIZING");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 r.curState = RESUMED;
174 }
175
176 // Don't do anything more here. There is an important case:
177 // if this is being done as part of onCreate() of the group, then
178 // the launching of the activity gets its state a little ahead
179 // of our own (it is now STARTED, while we are only CREATED).
180 // If we just leave things as-is, we'll deal with it as the
181 // group's state catches up.
182 return;
183 }
184
185 switch (r.curState) {
186 case CREATED:
187 if (desiredState == STARTED) {
188 if (localLOGV) Log.v(TAG, r.id + ": restarting");
Andrii Kulian88e05cb2017-12-05 17:21:10 -0800189 mActivityThread.performRestartActivity(r, true /* start */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 r.curState = STARTED;
191 }
192 if (desiredState == RESUMED) {
193 if (localLOGV) Log.v(TAG, r.id + ": restarting and resuming");
Andrii Kulian88e05cb2017-12-05 17:21:10 -0800194 mActivityThread.performRestartActivity(r, true /* start */);
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800195 mActivityThread.performResumeActivity(r, true, "moveToState-CREATED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 r.curState = RESUMED;
197 }
198 return;
199
200 case STARTED:
201 if (desiredState == RESUMED) {
202 // Need to resume it...
203 if (localLOGV) Log.v(TAG, r.id + ": resuming");
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800204 mActivityThread.performResumeActivity(r, true, "moveToState-STARTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 r.instanceState = null;
206 r.curState = RESUMED;
207 }
208 if (desiredState == CREATED) {
209 if (localLOGV) Log.v(TAG, r.id + ": stopping");
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800210 mActivityThread.performStopActivity(r, false, "moveToState-STARTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 r.curState = CREATED;
212 }
213 return;
214
215 case RESUMED:
216 if (desiredState == STARTED) {
217 if (localLOGV) Log.v(TAG, r.id + ": pausing");
218 performPause(r, mFinishing);
219 r.curState = STARTED;
220 }
221 if (desiredState == CREATED) {
222 if (localLOGV) Log.v(TAG, r.id + ": pausing");
223 performPause(r, mFinishing);
224 if (localLOGV) Log.v(TAG, r.id + ": stopping");
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800225 mActivityThread.performStopActivity(r, false, "moveToState-RESUMED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 r.curState = CREATED;
227 }
228 return;
229 }
230 }
231
232 private void performPause(LocalActivityRecord r, boolean finishing) {
Wale Ogunwalecd7043e2016-02-27 17:37:46 -0800233 final boolean needState = r.instanceState == null;
Andrii Kulian8ae79572018-01-26 15:36:06 -0800234 final Bundle instanceState = mActivityThread.performPauseActivity(r, finishing,
235 "performPause", null /* pendingActions */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 if (needState) {
237 r.instanceState = instanceState;
238 }
239 }
240
241 /**
242 * Start a new activity running in the group. Every activity you start
243 * must have a unique string ID associated with it -- this is used to keep
244 * track of the activity, so that if you later call startActivity() again
245 * on it the same activity object will be retained.
246 *
247 * <p>When there had previously been an activity started under this id,
248 * it may either be destroyed and a new one started, or the current
249 * one re-used, based on these conditions, in order:</p>
250 *
251 * <ul>
252 * <li> If the Intent maps to a different activity component than is
253 * currently running, the current activity is finished and a new one
254 * started.
255 * <li> If the current activity uses a non-multiple launch mode (such
256 * as singleTop), or the Intent has the
257 * {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag set, then the current
258 * activity will remain running and its
259 * {@link Activity#onNewIntent(Intent) Activity.onNewIntent()} method
260 * called.
261 * <li> If the new Intent is the same (excluding extras) as the previous
262 * one, and the new Intent does not have the
263 * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set, then the current activity
264 * will remain running as-is.
265 * <li> Otherwise, the current activity will be finished and a new
266 * one started.
267 * </ul>
268 *
269 * <p>If the given Intent can not be resolved to an available Activity,
270 * this method throws {@link android.content.ActivityNotFoundException}.
271 *
272 * <p>Warning: There is an issue where, if the Intent does not
273 * include an explicit component, we can restore the state for a different
274 * activity class than was previously running when the state was saved (if
275 * the set of available activities changes between those points).
276 *
277 * @param id Unique identifier of the activity to be started
278 * @param intent The Intent describing the activity to be started
279 *
280 * @return Returns the window of the activity. The caller needs to take
281 * care of adding this window to a view hierarchy, and likewise dealing
282 * with removing the old window if the activity has changed.
283 *
284 * @throws android.content.ActivityNotFoundException
285 */
286 public Window startActivity(String id, Intent intent) {
287 if (mCurState == INITIALIZING) {
288 throw new IllegalStateException(
289 "Activities can't be added until the containing group has been created.");
290 }
291
292 boolean adding = false;
293 boolean sameIntent = false;
294
295 ActivityInfo aInfo = null;
296
297 // Already have information about the new activity id?
298 LocalActivityRecord r = mActivities.get(id);
299 if (r == null) {
300 // Need to create it...
301 r = new LocalActivityRecord(id, intent);
302 adding = true;
303 } else if (r.intent != null) {
304 sameIntent = r.intent.filterEquals(intent);
305 if (sameIntent) {
306 // We are starting the same activity.
307 aInfo = r.activityInfo;
308 }
309 }
310 if (aInfo == null) {
311 aInfo = mActivityThread.resolveActivityInfo(intent);
312 }
313
314 // Pause the currently running activity if there is one and only a single
315 // activity is allowed to be running at a time.
316 if (mSingleMode) {
317 LocalActivityRecord old = mResumed;
318
319 // If there was a previous activity, and it is not the current
320 // activity, we need to stop it.
321 if (old != null && old != r && mCurState == RESUMED) {
322 moveToState(old, STARTED);
323 }
324 }
325
326 if (adding) {
327 // It's a brand new world.
328 mActivities.put(id, r);
329 mActivityArray.add(r);
330 } else if (r.activityInfo != null) {
331 // If the new activity is the same as the current one, then
332 // we may be able to reuse it.
333 if (aInfo == r.activityInfo ||
334 (aInfo.name.equals(r.activityInfo.name) &&
335 aInfo.packageName.equals(r.activityInfo.packageName))) {
336 if (aInfo.launchMode != ActivityInfo.LAUNCH_MULTIPLE ||
337 (intent.getFlags()&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) {
338 // The activity wants onNewIntent() called.
Dianne Hackborn85d558c2014-11-04 10:31:54 -0800339 ArrayList<ReferrerIntent> intents = new ArrayList<>(1);
340 intents.add(new ReferrerIntent(intent, mParent.getPackageName()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 if (localLOGV) Log.v(TAG, r.id + ": new intent");
Wale Ogunwale826c7062016-09-13 08:25:54 -0700342 mActivityThread.performNewIntents(r, intents, false /* andPause */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 r.intent = intent;
344 moveToState(r, mCurState);
345 if (mSingleMode) {
346 mResumed = r;
347 }
348 return r.window;
349 }
350 if (sameIntent &&
351 (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_TOP) == 0) {
352 // We are showing the same thing, so this activity is
353 // just resumed and stays as-is.
354 r.intent = intent;
355 moveToState(r, mCurState);
356 if (mSingleMode) {
357 mResumed = r;
358 }
359 return r.window;
360 }
361 }
362
363 // The new activity is different than the current one, or it
364 // is a multiple launch activity, so we need to destroy what
365 // is currently there.
366 performDestroy(r, true);
367 }
368
369 r.intent = intent;
370 r.curState = INITIALIZING;
371 r.activityInfo = aInfo;
372
373 moveToState(r, mCurState);
374
375 // When in single mode keep track of the current activity
376 if (mSingleMode) {
377 mResumed = r;
378 }
379 return r.window;
380 }
381
382 private Window performDestroy(LocalActivityRecord r, boolean finish) {
Romain Guyd315ee92010-06-03 11:03:18 -0700383 Window win;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 win = r.window;
385 if (r.curState == RESUMED && !finish) {
386 performPause(r, finish);
387 }
388 if (localLOGV) Log.v(TAG, r.id + ": destroying");
Andrii Kulian88e05cb2017-12-05 17:21:10 -0800389 mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
Bryce Leea33c13d2018-02-08 14:37:06 -0800390 false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 r.activity = null;
392 r.window = null;
393 if (finish) {
394 r.instanceState = null;
395 }
396 r.curState = DESTROYED;
397 return win;
398 }
399
400 /**
401 * Destroy the activity associated with a particular id. This activity
402 * will go through the normal lifecycle events and fine onDestroy(), and
403 * then the id removed from the group.
404 *
405 * @param id Unique identifier of the activity to be destroyed
406 * @param finish If true, this activity will be finished, so its id and
407 * all state are removed from the group.
408 *
409 * @return Returns the window that was used to display the activity, or
410 * null if there was none.
411 */
412 public Window destroyActivity(String id, boolean finish) {
413 LocalActivityRecord r = mActivities.get(id);
414 Window win = null;
415 if (r != null) {
416 win = performDestroy(r, finish);
417 if (finish) {
Romain Guyd315ee92010-06-03 11:03:18 -0700418 mActivities.remove(id);
419 mActivityArray.remove(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 }
421 }
422 return win;
423 }
424
425 /**
426 * Retrieve the Activity that is currently running.
427 *
428 * @return the currently running (resumed) Activity, or null if there is
429 * not one
430 *
431 * @see #startActivity
432 * @see #getCurrentId
433 */
434 public Activity getCurrentActivity() {
435 return mResumed != null ? mResumed.activity : null;
436 }
437
438 /**
439 * Retrieve the ID of the activity that is currently running.
440 *
441 * @return the ID of the currently running (resumed) Activity, or null if
442 * there is not one
443 *
444 * @see #startActivity
445 * @see #getCurrentActivity
446 */
447 public String getCurrentId() {
448 return mResumed != null ? mResumed.id : null;
449 }
450
451 /**
452 * Return the Activity object associated with a string ID.
453 *
454 * @see #startActivity
455 *
456 * @return the associated Activity object, or null if the id is unknown or
457 * its activity is not currently instantiated
458 */
459 public Activity getActivity(String id) {
460 LocalActivityRecord r = mActivities.get(id);
461 return r != null ? r.activity : null;
462 }
463
464 /**
465 * Restore a state that was previously returned by {@link #saveInstanceState}. This
466 * adds to the activity group information about all activity IDs that had
467 * previously been saved, even if they have not been started yet, so if the
468 * user later navigates to them the correct state will be restored.
469 *
470 * <p>Note: This does <b>not</b> change the current running activity, or
471 * start whatever activity was previously running when the state was saved.
472 * That is up to the client to do, in whatever way it thinks is best.
473 *
474 * @param state a previously saved state; does nothing if this is null
475 *
476 * @see #saveInstanceState
477 */
478 public void dispatchCreate(Bundle state) {
479 if (state != null) {
Romain Guyd315ee92010-06-03 11:03:18 -0700480 for (String id : state.keySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 final Bundle astate = state.getBundle(id);
483 LocalActivityRecord r = mActivities.get(id);
484 if (r != null) {
485 r.instanceState = astate;
486 } else {
487 r = new LocalActivityRecord(id, null);
488 r.instanceState = astate;
489 mActivities.put(id, r);
490 mActivityArray.add(r);
491 }
492 } catch (Exception e) {
493 // Recover from -all- app errors.
Romain Guyd315ee92010-06-03 11:03:18 -0700494 Log.e(TAG, "Exception thrown when restoring LocalActivityManager state", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 }
496 }
497 }
498
499 mCurState = CREATED;
500 }
501
502 /**
503 * Retrieve the state of all activities known by the group. For
504 * activities that have previously run and are now stopped or finished, the
505 * last saved state is used. For the current running activity, its
506 * {@link Activity#onSaveInstanceState} is called to retrieve its current state.
507 *
508 * @return a Bundle holding the newly created state of all known activities
509 *
510 * @see #dispatchCreate
511 */
512 public Bundle saveInstanceState() {
513 Bundle state = null;
514
515 // FIXME: child activities will freeze as part of onPaused. Do we
516 // need to do this here?
517 final int N = mActivityArray.size();
518 for (int i=0; i<N; i++) {
519 final LocalActivityRecord r = mActivityArray.get(i);
520 if (state == null) {
521 state = new Bundle();
522 }
523 if ((r.instanceState != null || r.curState == RESUMED)
524 && r.activity != null) {
525 // We need to save the state now, if we don't currently
526 // already have it or the activity is currently resumed.
527 final Bundle childState = new Bundle();
Martin Hibdon6bcda632010-12-17 16:56:07 -0800528 r.activity.performSaveInstanceState(childState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 r.instanceState = childState;
530 }
531 if (r.instanceState != null) {
532 state.putBundle(r.id, r.instanceState);
533 }
534 }
535
536 return state;
537 }
538
539 /**
540 * Called by the container activity in its {@link Activity#onResume} so
541 * that LocalActivityManager can perform the corresponding action on the
542 * activities it holds.
543 *
544 * @see Activity#onResume
545 */
546 public void dispatchResume() {
547 mCurState = RESUMED;
548 if (mSingleMode) {
549 if (mResumed != null) {
550 moveToState(mResumed, RESUMED);
551 }
552 } else {
553 final int N = mActivityArray.size();
554 for (int i=0; i<N; i++) {
555 moveToState(mActivityArray.get(i), RESUMED);
556 }
557 }
558 }
559
560 /**
561 * Called by the container activity in its {@link Activity#onPause} so
562 * that LocalActivityManager can perform the corresponding action on the
563 * activities it holds.
564 *
565 * @param finishing set to true if the parent activity has been finished;
566 * this can be determined by calling
567 * Activity.isFinishing()
568 *
569 * @see Activity#onPause
570 * @see Activity#isFinishing
571 */
572 public void dispatchPause(boolean finishing) {
573 if (finishing) {
574 mFinishing = true;
575 }
576 mCurState = STARTED;
577 if (mSingleMode) {
578 if (mResumed != null) {
579 moveToState(mResumed, STARTED);
580 }
581 } else {
582 final int N = mActivityArray.size();
583 for (int i=0; i<N; i++) {
584 LocalActivityRecord r = mActivityArray.get(i);
585 if (r.curState == RESUMED) {
586 moveToState(r, STARTED);
587 }
588 }
589 }
590 }
591
592 /**
593 * Called by the container activity in its {@link Activity#onStop} so
594 * that LocalActivityManager can perform the corresponding action on the
595 * activities it holds.
596 *
597 * @see Activity#onStop
598 */
599 public void dispatchStop() {
600 mCurState = CREATED;
601 final int N = mActivityArray.size();
602 for (int i=0; i<N; i++) {
603 LocalActivityRecord r = mActivityArray.get(i);
604 moveToState(r, CREATED);
605 }
606 }
607
608 /**
609 * Call onRetainNonConfigurationInstance on each child activity and store the
610 * results in a HashMap by id. Only construct the HashMap if there is a non-null
611 * object to store. Note that this does not support nested ActivityGroups.
612 *
613 * {@hide}
614 */
615 public HashMap<String,Object> dispatchRetainNonConfigurationInstance() {
616 HashMap<String,Object> instanceMap = null;
617
618 final int N = mActivityArray.size();
619 for (int i=0; i<N; i++) {
620 LocalActivityRecord r = mActivityArray.get(i);
621 if ((r != null) && (r.activity != null)) {
622 Object instance = r.activity.onRetainNonConfigurationInstance();
623 if (instance != null) {
624 if (instanceMap == null) {
625 instanceMap = new HashMap<String,Object>();
626 }
627 instanceMap.put(r.id, instance);
628 }
629 }
630 }
631 return instanceMap;
632 }
633
634 /**
635 * Remove all activities from this LocalActivityManager, performing an
636 * {@link Activity#onDestroy} on any that are currently instantiated.
637 */
638 public void removeAllActivities() {
639 dispatchDestroy(true);
640 }
641
642 /**
643 * Called by the container activity in its {@link Activity#onDestroy} so
644 * that LocalActivityManager can perform the corresponding action on the
645 * activities it holds.
646 *
647 * @see Activity#onDestroy
648 */
649 public void dispatchDestroy(boolean finishing) {
650 final int N = mActivityArray.size();
651 for (int i=0; i<N; i++) {
652 LocalActivityRecord r = mActivityArray.get(i);
653 if (localLOGV) Log.v(TAG, r.id + ": destroying");
Andrii Kulian88e05cb2017-12-05 17:21:10 -0800654 mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
Bryce Leea33c13d2018-02-08 14:37:06 -0800655 false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 }
657 mActivities.clear();
658 mActivityArray.clear();
659 }
660}