Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 1 | /* |
| 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 Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 17 | package android.app.patterns; |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 18 | |
| 19 | |
| 20 | import android.app.Activity; |
| 21 | import android.os.Bundle; |
| 22 | |
| 23 | import 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 | */ |
| 29 | public abstract class LoaderActivity<D> extends Activity implements |
| 30 | Loader.OnLoadCompleteListener<D> { |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 31 | private boolean mStarted = false; |
| 32 | |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 33 | static final class LoaderInfo { |
| 34 | public Bundle args; |
| 35 | public Loader loader; |
| 36 | } |
| 37 | private HashMap<Integer, LoaderInfo> mLoaders; |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 38 | private HashMap<Integer, LoaderInfo> mInactiveLoaders; |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 39 | |
| 40 | /** |
| 41 | * Registers a loader with this activity, registers the callbacks on it, and starts it loading. |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 42 | * 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 Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 45 | */ |
| 46 | protected void startLoading(int id, Bundle args) { |
| 47 | LoaderInfo info = mLoaders.get(id); |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 48 | if (info != null) { |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 49 | // 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 Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 52 | } |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 53 | info = new LoaderInfo(); |
| 54 | info.args = args; |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 55 | mLoaders.put(id, info); |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 56 | Loader loader = onCreateLoader(id, args); |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 57 | info.loader = loader; |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 58 | 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 Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | protected abstract Loader onCreateLoader(int id, Bundle args); |
| 67 | protected abstract void onInitializeLoaders(); |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 68 | protected abstract void onLoadFinished(Loader loader, D data); |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 69 | |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 70 | 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 Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 86 | |
| 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 Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 99 | if (mInactiveLoaders == null) { |
| 100 | mInactiveLoaders = new HashMap<Integer, LoaderInfo>(); |
| 101 | } |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 102 | } |
| 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 Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 117 | } |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 118 | loader.registerListener(id, this); |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 119 | loader.startLoading(); |
| 120 | } |
Daniel Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 121 | |
| 122 | mStarted = true; |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 123 | } |
| 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 Lehmann | c2687c3 | 2010-04-19 18:20:44 -0700 | [diff] [blame^] | 144 | |
| 145 | mStarted = false; |
Daniel Lehmann | cbcc449 | 2010-04-12 18:03:54 -0700 | [diff] [blame] | 146 | } |
| 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 | } |