blob: 664a8bc9fcc0a459d1c26ebfad7317b596db9be2 [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
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070033 static final class LoaderInfo {
34 public Bundle args;
35 public Loader loader;
36 }
37 private HashMap<Integer, LoaderInfo> mLoaders;
Daniel Lehmannc2687c32010-04-19 18:20:44 -070038 private HashMap<Integer, LoaderInfo> 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) {
47 LoaderInfo 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 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -070053 info = new LoaderInfo();
54 info.args = args;
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070055 mLoaders.put(id, info);
Daniel Lehmannc2687c32010-04-19 18:20:44 -070056 Loader 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
66 protected abstract Loader onCreateLoader(int id, Bundle args);
67 protected abstract void onInitializeLoaders();
Daniel Lehmannc2687c32010-04-19 18:20:44 -070068 protected abstract void onLoadFinished(Loader loader, D data);
Daniel Lehmanncbcc4492010-04-12 18:03:54 -070069
Daniel Lehmannc2687c32010-04-19 18:20:44 -070070 public final void onLoadComplete(Loader loader, D data) {
71 // 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();
77 LoaderInfo info = mInactiveLoaders.get(id);
78 if (info != null) {
79 Loader oldLoader = info.loader;
80 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
93 mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
94 if (mLoaders == null) {
95 mLoaders = new HashMap<Integer, LoaderInfo>();
96 onInitializeLoaders();
97 }
98 }
Daniel Lehmannc2687c32010-04-19 18:20:44 -070099 if (mInactiveLoaders == null) {
100 mInactiveLoaders = new HashMap<Integer, LoaderInfo>();
101 }
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
110 for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
111 LoaderInfo info = entry.getValue();
112 Loader loader = info.loader;
113 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
129 for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
130 LoaderInfo info = entry.getValue();
131 Loader loader = info.loader;
132 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) {
161 for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
162 LoaderInfo info = entry.getValue();
163 Loader loader = info.loader;
164 if (loader == null) {
165 continue;
166 }
167 loader.destroy();
168 }
169 }
170 }
171}