QsbApplication.java revision 81a0897ff9685f3313c58294bf7973700c468b2b
1/* 2 * Copyright (C) 2009 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 com.android.quicksearchbox; 18 19import com.android.quicksearchbox.ui.CorpusViewFactory; 20import com.android.quicksearchbox.ui.CorpusViewInflater; 21import com.android.quicksearchbox.ui.DelayingSuggestionsAdapter; 22import com.android.quicksearchbox.ui.EmptySuggestionsFooter; 23import com.android.quicksearchbox.ui.SuggestionViewFactory; 24import com.android.quicksearchbox.ui.SuggestionViewInflater; 25import com.android.quicksearchbox.ui.SuggestionsAdapter; 26import com.android.quicksearchbox.ui.SuggestionsFooter; 27import com.android.quicksearchbox.util.Factory; 28import com.android.quicksearchbox.util.NamedTaskExecutor; 29import com.android.quicksearchbox.util.PerNameExecutor; 30import com.android.quicksearchbox.util.PriorityThreadFactory; 31import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor; 32import com.google.common.util.concurrent.NamingThreadFactory; 33 34import android.app.Application; 35import android.os.Handler; 36import android.os.Looper; 37import android.os.Process; 38 39import java.util.concurrent.Executor; 40import java.util.concurrent.Executors; 41import java.util.concurrent.ThreadFactory; 42 43public class QsbApplication extends Application { 44 45 private Handler mUiThreadHandler; 46 private Config mConfig; 47 private Corpora mCorpora; 48 private CorpusRanker mCorpusRanker; 49 private ShortcutRepository mShortcutRepository; 50 private ShortcutRefresher mShortcutRefresher; 51 private NamedTaskExecutor mSourceTaskExecutor; 52 private ThreadFactory mQueryThreadFactory; 53 private SuggestionsProvider mSuggestionsProvider; 54 private SuggestionViewFactory mSuggestionViewFactory; 55 private CorpusViewFactory mCorpusViewFactory; 56 private Logger mLogger; 57 58 @Override 59 public void onTerminate() { 60 close(); 61 super.onTerminate(); 62 } 63 64 protected void checkThread() { 65 if (Looper.myLooper() != Looper.getMainLooper()) { 66 throw new IllegalStateException("Accessed Application object from thread " 67 + Thread.currentThread().getName()); 68 } 69 } 70 71 protected void close() { 72 checkThread(); 73 if (mConfig != null) { 74 mConfig.close(); 75 mConfig = null; 76 } 77 if (mCorpora != null) { 78 mCorpora.close(); 79 mCorpora = null; 80 } 81 if (mShortcutRepository != null) { 82 mShortcutRepository.close(); 83 mShortcutRepository = null; 84 } 85 if (mSourceTaskExecutor != null) { 86 mSourceTaskExecutor.close(); 87 mSourceTaskExecutor = null; 88 } 89 if (mSuggestionsProvider != null) { 90 mSuggestionsProvider.close(); 91 mSuggestionsProvider = null; 92 } 93 } 94 95 public synchronized Handler getMainThreadHandler() { 96 if (mUiThreadHandler == null) { 97 mUiThreadHandler = new Handler(Looper.getMainLooper()); 98 } 99 return mUiThreadHandler; 100 } 101 102 public void runOnUiThread(Runnable action) { 103 getMainThreadHandler().post(action); 104 } 105 106 /** 107 * Gets the QSB configuration object. 108 * May be called from any thread. 109 */ 110 public synchronized Config getConfig() { 111 if (mConfig == null) { 112 mConfig = createConfig(); 113 } 114 return mConfig; 115 } 116 117 protected Config createConfig() { 118 return new Config(this); 119 } 120 121 /** 122 * Gets the corpora. 123 * May only be called from the main thread. 124 */ 125 public Corpora getCorpora() { 126 checkThread(); 127 if (mCorpora == null) { 128 mCorpora = createCorpora(); 129 } 130 return mCorpora; 131 } 132 133 protected Corpora createCorpora() { 134 SearchableCorpora corpora = new SearchableCorpora(this, getConfig(), createSources(), 135 createCorpusFactory()); 136 corpora.load(); 137 return corpora; 138 } 139 140 protected Sources createSources() { 141 return new SearchableSources(this, getMainThreadHandler()); 142 } 143 144 protected CorpusFactory createCorpusFactory() { 145 int numWebCorpusThreads = getConfig().getNumWebCorpusThreads(); 146 return new SearchableCorpusFactory(this, createExecutorFactory(numWebCorpusThreads)); 147 } 148 149 protected Factory<Executor> createExecutorFactory(final int numThreads) { 150 final ThreadFactory threadFactory = getQueryThreadFactory(); 151 return new Factory<Executor>() { 152 public Executor create() { 153 return Executors.newFixedThreadPool(numThreads, threadFactory); 154 } 155 }; 156 } 157 158 /** 159 * Gets the corpus ranker. 160 * May only be called from the main thread. 161 */ 162 public CorpusRanker getCorpusRanker() { 163 checkThread(); 164 if (mCorpusRanker == null) { 165 mCorpusRanker = createCorpusRanker(); 166 } 167 return mCorpusRanker; 168 } 169 170 protected CorpusRanker createCorpusRanker() { 171 return new DefaultCorpusRanker(getCorpora(), getShortcutRepository()); 172 } 173 174 /** 175 * Gets the shortcut repository. 176 * May only be called from the main thread. 177 */ 178 public ShortcutRepository getShortcutRepository() { 179 checkThread(); 180 if (mShortcutRepository == null) { 181 mShortcutRepository = createShortcutRepository(); 182 } 183 return mShortcutRepository; 184 } 185 186 protected ShortcutRepository createShortcutRepository() { 187 ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d", 188 new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND)); 189 Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory); 190 return ShortcutRepositoryImplLog.create(this, getConfig(), getCorpora(), 191 getShortcutRefresher(), getMainThreadHandler(), logExecutor); 192 } 193 194 /** 195 * Gets the shortcut refresher. 196 * May only be called from the main thread. 197 */ 198 public ShortcutRefresher getShortcutRefresher() { 199 checkThread(); 200 if (mShortcutRefresher == null) { 201 mShortcutRefresher = createShortcutRefresher(); 202 } 203 return mShortcutRefresher; 204 } 205 206 protected ShortcutRefresher createShortcutRefresher() { 207 // For now, ShortcutRefresher gets its own SourceTaskExecutor 208 return new SourceShortcutRefresher(createSourceTaskExecutor()); 209 } 210 211 /** 212 * Gets the source task executor. 213 * May only be called from the main thread. 214 */ 215 public NamedTaskExecutor getSourceTaskExecutor() { 216 checkThread(); 217 if (mSourceTaskExecutor == null) { 218 mSourceTaskExecutor = createSourceTaskExecutor(); 219 } 220 return mSourceTaskExecutor; 221 } 222 223 protected NamedTaskExecutor createSourceTaskExecutor() { 224 Config config = getConfig(); 225 ThreadFactory queryThreadFactory = getQueryThreadFactory(); 226 return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory)); 227 } 228 229 /** 230 * Gets the query thread factory. 231 * May only be called from the main thread. 232 */ 233 protected ThreadFactory getQueryThreadFactory() { 234 checkThread(); 235 if (mQueryThreadFactory == null) { 236 mQueryThreadFactory = createQueryThreadFactory(); 237 } 238 return mQueryThreadFactory; 239 } 240 241 protected ThreadFactory createQueryThreadFactory() { 242 String nameFormat = "QSB #%d"; 243 int priority = getConfig().getQueryThreadPriority(); 244 return new NamingThreadFactory(nameFormat, 245 new PriorityThreadFactory(priority)); 246 } 247 248 /** 249 * Gets the suggestion provider. 250 * May only be called from the main thread. 251 */ 252 protected SuggestionsProvider getSuggestionsProvider() { 253 checkThread(); 254 if (mSuggestionsProvider == null) { 255 mSuggestionsProvider = createSuggestionsProvider(); 256 } 257 return mSuggestionsProvider; 258 } 259 260 protected SuggestionsProvider createSuggestionsProvider() { 261 Promoter promoter = new ShortcutPromoter( 262 new RankAwarePromoter(getConfig(), getCorpora())); 263 SuggestionsProvider provider = new SuggestionsProviderImpl(getConfig(), 264 getSourceTaskExecutor(), 265 getMainThreadHandler(), 266 promoter, 267 getShortcutRepository(), 268 getCorpora(), 269 getCorpusRanker(), 270 getLogger()); 271 return provider; 272 } 273 274 /** 275 * Gets the suggestion view factory. 276 * May only be called from the main thread. 277 */ 278 public SuggestionViewFactory getSuggestionViewFactory() { 279 checkThread(); 280 if (mSuggestionViewFactory == null) { 281 mSuggestionViewFactory = createSuggestionViewFactory(); 282 } 283 return mSuggestionViewFactory; 284 } 285 286 protected SuggestionViewFactory createSuggestionViewFactory() { 287 return new SuggestionViewInflater(this); 288 } 289 290 /** 291 * Gets the corpus view factory. 292 * May only be called from the main thread. 293 */ 294 public CorpusViewFactory getCorpusViewFactory() { 295 checkThread(); 296 if (mCorpusViewFactory == null) { 297 mCorpusViewFactory = createCorpusViewFactory(); 298 } 299 return mCorpusViewFactory; 300 } 301 302 protected CorpusViewFactory createCorpusViewFactory() { 303 return new CorpusViewInflater(this); 304 } 305 306 /** 307 * Creates a suggestions adapter. 308 * May only be called from the main thread. 309 */ 310 public SuggestionsAdapter createSuggestionsAdapter() { 311 Config config = getConfig(); 312 SuggestionViewFactory viewFactory = getSuggestionViewFactory(); 313 DelayingSuggestionsAdapter adapter = new DelayingSuggestionsAdapter(viewFactory); 314 return adapter; 315 } 316 317 /** 318 * Creates a footer view to add at the bottom of the search activity. 319 */ 320 public SuggestionsFooter createSuggestionsFooter() { 321 return new EmptySuggestionsFooter(this); 322 } 323 324 /** 325 * Gets the event logger. 326 * May only be called from the main thread. 327 */ 328 public Logger getLogger() { 329 checkThread(); 330 if (mLogger == null) { 331 mLogger = createLogger(); 332 } 333 return mLogger; 334 } 335 336 protected Logger createLogger() { 337 return new EventLogLogger(this, getConfig()); 338 } 339} 340