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