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