blob: bcb36927d3f639ec04572c4972eab3b7bf64f6e0 [file] [log] [blame]
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001/*
2 * Copyright (C) 2010 Google Inc.
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
Daniel Lehmannc2687c32010-04-19 18:20:44 -070017package android.app.patterns;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070018
19
20import android.app.Activity;
21import android.os.Bundle;
22
23import java.util.HashMap;
24
25/**
26 * The idea here was to abstract the generic life cycle junk needed to properly keep loaders going.
27 * It didn't work out as-is because registering the callbacks post config change didn't work.
28 */
29public abstract class LoaderActivity<D> extends Activity implements
30 Loader.OnLoadCompleteListener<D> {
Daniel Lehmannc2687c32010-04-19 18:20:44 -070031 private boolean mStarted = false;
32
Dmitri Plotnikov86053952010-04-29 10:22:01 -070033 static final class LoaderInfo<D> {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070034 public Bundle args;
Dmitri Plotnikov86053952010-04-29 10:22:01 -070035 public Loader<D> loader;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070036 }
Dmitri Plotnikov86053952010-04-29 10:22:01 -070037 private HashMap<Integer, LoaderInfo<D>> mLoaders;
38 private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070039
40 /**
41 * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
Daniel Lehmannc2687c32010-04-19 18:20:44 -070042 * If a loader with the same id has previously been started it will automatically be destroyed
43 * when the new loader completes it's work. The callback will be delivered before the old loader
44 * is destroyed.
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070045 */
46 protected void startLoading(int id, Bundle args) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -070047 LoaderInfo<D> info = mLoaders.get(id);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070048 if (info != null) {
Daniel Lehmannc2687c32010-04-19 18:20:44 -070049 // Keep track of the previous instance of this loader so we can destroy
50 // it when the new one completes.
51 mInactiveLoaders.put(id, info);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070052 }
Dmitri Plotnikov86053952010-04-29 10:22:01 -070053 info = new LoaderInfo<D>();
Daniel Lehmannc2687c32010-04-19 18:20:44 -070054 info.args = args;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070055 mLoaders.put(id, info);
Dmitri Plotnikov86053952010-04-29 10:22:01 -070056 Loader<D> loader = onCreateLoader(id, args);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070057 info.loader = loader;
Daniel Lehmannc2687c32010-04-19 18:20:44 -070058 if (mStarted) {
59 // The activity will start all existing loaders in it's onStart(), so only start them
60 // here if we're past that point of the activitiy's life cycle
61 loader.registerListener(id, this);
62 loader.startLoading();
63 }
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070064 }
65
Dmitri Plotnikov86053952010-04-29 10:22:01 -070066 protected abstract Loader<D> onCreateLoader(int id, Bundle args);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070067 protected abstract void onInitializeLoaders();
Dmitri Plotnikov86053952010-04-29 10:22:01 -070068 protected abstract void onLoadFinished(Loader<D> loader, D data);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070069
Dmitri Plotnikov86053952010-04-29 10:22:01 -070070 public final void onLoadComplete(Loader<D> loader, D data) {
Daniel Lehmannc2687c32010-04-19 18:20:44 -070071 // Notify of the new data so the app can switch out the old data before
72 // we try to destroy it.
73 onLoadFinished(loader, data);
74
75 // Look for an inactive loader and destroy it if found
76 int id = loader.getId();
Dmitri Plotnikov86053952010-04-29 10:22:01 -070077 LoaderInfo<D> info = mInactiveLoaders.get(id);
Daniel Lehmannc2687c32010-04-19 18:20:44 -070078 if (info != null) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -070079 Loader<D> oldLoader = info.loader;
Daniel Lehmannc2687c32010-04-19 18:20:44 -070080 if (oldLoader != null) {
81 oldLoader.destroy();
82 }
83 mInactiveLoaders.remove(id);
84 }
85 }
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070086
87 @Override
88 public void onCreate(Bundle savedState) {
89 super.onCreate(savedState);
90
91 if (mLoaders == null) {
92 // Look for a passed along loader and create a new one if it's not there
Dmitri Plotnikov86053952010-04-29 10:22:01 -070093 mLoaders = (HashMap<Integer, LoaderInfo<D>>) getLastNonConfigurationInstance();
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070094 if (mLoaders == null) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -070095 mLoaders = new HashMap<Integer, LoaderInfo<D>>();
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070096 onInitializeLoaders();
97 }
98 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -070099 if (mInactiveLoaders == null) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700100 mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
Daniel Lehmannc2687c32010-04-19 18:20:44 -0700101 }
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700102 }
103
104 @Override
105 public void onStart() {
106 super.onStart();
107
108 // Call out to sub classes so they can start their loaders
109 // Let the existing loaders know that we want to be notified when a load is complete
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700110 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
111 LoaderInfo<D> info = entry.getValue();
112 Loader<D> loader = info.loader;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700113 int id = entry.getKey();
114 if (loader == null) {
115 loader = onCreateLoader(id, info.args);
116 info.loader = loader;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700117 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -0700118 loader.registerListener(id, this);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700119 loader.startLoading();
120 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -0700121
122 mStarted = true;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700123 }
124
125 @Override
126 public void onStop() {
127 super.onStop();
128
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700129 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
130 LoaderInfo<D> info = entry.getValue();
131 Loader<D> loader = info.loader;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700132 if (loader == null) {
133 continue;
134 }
135
136 // Let the loader know we're done with it
137 loader.unregisterListener(this);
138
139 // The loader isn't getting passed along to the next instance so ask it to stop loading
140// if (!isChangingConfigurations()) {
141// loader.stopLoading();
142// }
143 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -0700144
145 mStarted = false;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700146 }
147
148 @Override
149 public Object onRetainNonConfigurationInstance() {
150 // Pass the loader along to the next guy
151 Object result = mLoaders;
152 mLoaders = null;
153 return result;
154 }
155
156 @Override
157 public void onDestroy() {
158 super.onDestroy();
159
160 if (mLoaders != null) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700161 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
162 LoaderInfo<D> info = entry.getValue();
163 Loader<D> loader = info.loader;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700164 if (loader == null) {
165 continue;
166 }
167 loader.destroy();
168 }
169 }
170 }
171}