RecentsTaskLoader.java revision ffa2ec664479bff6b4b61d4c349d9db2cb37ca16
1/* 2 * Copyright (C) 2014 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.systemui.recents.model; 18 19import android.app.ActivityManager; 20import android.content.ComponentCallbacks2; 21import android.content.Context; 22import android.content.pm.ActivityInfo; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.drawable.BitmapDrawable; 26import android.graphics.drawable.Drawable; 27import android.os.Handler; 28import android.os.HandlerThread; 29import android.os.UserHandle; 30import android.util.Pair; 31import com.android.systemui.recents.Constants; 32import com.android.systemui.recents.RecentsConfiguration; 33import com.android.systemui.recents.misc.Console; 34import com.android.systemui.recents.misc.SystemServicesProxy; 35 36import java.util.ArrayList; 37import java.util.Collections; 38import java.util.List; 39import java.util.concurrent.ConcurrentHashMap; 40import java.util.concurrent.ConcurrentLinkedQueue; 41 42 43/** A bitmap load queue */ 44class TaskResourceLoadQueue { 45 ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>(); 46 ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet = 47 new ConcurrentHashMap<Task.TaskKey, Boolean>(); 48 49 static final Boolean sFalse = new Boolean(false); 50 51 /** Adds a new task to the load queue */ 52 void addTask(Task t, boolean forceLoad) { 53 if (Console.Enabled) { 54 Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|addTask]"); 55 } 56 if (!mQueue.contains(t)) { 57 mQueue.add(t); 58 } 59 if (forceLoad) { 60 mForceLoadSet.put(t.key, new Boolean(true)); 61 } 62 synchronized(this) { 63 notifyAll(); 64 } 65 } 66 67 /** 68 * Retrieves the next task from the load queue, as well as whether we want that task to be 69 * force reloaded. 70 */ 71 Pair<Task, Boolean> nextTask() { 72 if (Console.Enabled) { 73 Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]"); 74 } 75 Task task = mQueue.poll(); 76 Boolean forceLoadTask = null; 77 if (task != null) { 78 forceLoadTask = mForceLoadSet.remove(task.key); 79 } 80 if (forceLoadTask == null) { 81 forceLoadTask = sFalse; 82 } 83 return new Pair<Task, Boolean>(task, forceLoadTask); 84 } 85 86 /** Removes a task from the load queue */ 87 void removeTask(Task t) { 88 if (Console.Enabled) { 89 Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|removeTask]"); 90 } 91 mQueue.remove(t); 92 mForceLoadSet.remove(t.key); 93 } 94 95 /** Clears all the tasks from the load queue */ 96 void clearTasks() { 97 if (Console.Enabled) { 98 Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|clearTasks]"); 99 } 100 mQueue.clear(); 101 mForceLoadSet.clear(); 102 } 103 104 /** Returns whether the load queue is empty */ 105 boolean isEmpty() { 106 return mQueue.isEmpty(); 107 } 108} 109 110/* Task resource loader */ 111class TaskResourceLoader implements Runnable { 112 Context mContext; 113 HandlerThread mLoadThread; 114 Handler mLoadThreadHandler; 115 Handler mMainThreadHandler; 116 117 SystemServicesProxy mSystemServicesProxy; 118 TaskResourceLoadQueue mLoadQueue; 119 DrawableLruCache mApplicationIconCache; 120 BitmapLruCache mThumbnailCache; 121 Bitmap mDefaultThumbnail; 122 123 boolean mCancelled; 124 boolean mWaitingOnLoadQueue; 125 126 /** Constructor, creates a new loading thread that loads task resources in the background */ 127 public TaskResourceLoader(TaskResourceLoadQueue loadQueue, 128 DrawableLruCache applicationIconCache, 129 BitmapLruCache thumbnailCache, 130 Bitmap defaultThumbnail) { 131 mLoadQueue = loadQueue; 132 mApplicationIconCache = applicationIconCache; 133 mThumbnailCache = thumbnailCache; 134 mDefaultThumbnail = defaultThumbnail; 135 mMainThreadHandler = new Handler(); 136 mLoadThread = new HandlerThread("Recents-TaskResourceLoader"); 137 mLoadThread.setPriority(Thread.NORM_PRIORITY - 1); 138 mLoadThread.start(); 139 mLoadThreadHandler = new Handler(mLoadThread.getLooper()); 140 mLoadThreadHandler.post(this); 141 } 142 143 /** Restarts the loader thread */ 144 void start(Context context) { 145 if (Console.Enabled) { 146 Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|start]"); 147 } 148 mContext = context; 149 mCancelled = false; 150 mSystemServicesProxy = new SystemServicesProxy(context); 151 // Notify the load thread to start loading 152 synchronized(mLoadThread) { 153 mLoadThread.notifyAll(); 154 } 155 } 156 157 /** Requests the loader thread to stop after the current iteration */ 158 void stop() { 159 if (Console.Enabled) { 160 Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|stop]"); 161 } 162 // Mark as cancelled for the thread to pick up 163 mCancelled = true; 164 mSystemServicesProxy = null; 165 // If we are waiting for the load queue for more tasks, then we can just reset the 166 // Context now, since nothing is using it 167 if (mWaitingOnLoadQueue) { 168 mContext = null; 169 } 170 } 171 172 @Override 173 public void run() { 174 while (true) { 175 if (Console.Enabled) { 176 Console.log(Constants.Log.App.TaskDataLoader, 177 "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]"); 178 } 179 if (mCancelled) { 180 if (Console.Enabled) { 181 Console.log(Constants.Log.App.TaskDataLoader, 182 "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]"); 183 } 184 // We have to unset the context here, since the background thread may be using it 185 // when we call stop() 186 mContext = null; 187 // If we are cancelled, then wait until we are started again 188 synchronized(mLoadThread) { 189 try { 190 if (Console.Enabled) { 191 Console.log(Constants.Log.App.TaskDataLoader, 192 "[TaskResourceLoader|waitOnLoadThreadCancelled]"); 193 } 194 mLoadThread.wait(); 195 } catch (InterruptedException ie) { 196 ie.printStackTrace(); 197 } 198 } 199 } else { 200 SystemServicesProxy ssp = mSystemServicesProxy; 201 202 // Load the next item from the queue 203 Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask(); 204 final Task t = nextTaskData.first; 205 final boolean forceLoadTask = nextTaskData.second; 206 if (t != null) { 207 Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key); 208 Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key); 209 if (Console.Enabled) { 210 Console.log(Constants.Log.App.TaskDataLoader, 211 " [TaskResourceLoader|load]", 212 t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail + 213 " forceLoad: " + forceLoadTask); 214 } 215 // Load the application icon 216 if (loadIcon == null || forceLoadTask) { 217 ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(), 218 t.userId); 219 Drawable icon = ssp.getActivityIcon(info, t.userId); 220 if (!mCancelled) { 221 if (icon != null) { 222 if (Console.Enabled) { 223 Console.log(Constants.Log.App.TaskDataLoader, 224 " [TaskResourceLoader|loadIcon]", icon); 225 } 226 loadIcon = icon; 227 mApplicationIconCache.put(t.key, icon); 228 } 229 } 230 } 231 // Load the thumbnail 232 if (loadThumbnail == null || forceLoadTask) { 233 Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id); 234 if (!mCancelled) { 235 if (thumbnail != null) { 236 if (Console.Enabled) { 237 Console.log(Constants.Log.App.TaskDataLoader, 238 " [TaskResourceLoader|loadThumbnail]", thumbnail); 239 } 240 thumbnail.setHasAlpha(false); 241 loadThumbnail = thumbnail; 242 } else { 243 loadThumbnail = mDefaultThumbnail; 244 Console.logError(mContext, 245 "Failed to load task top thumbnail for: " + 246 t.key.baseIntent.getComponent().getPackageName()); 247 } 248 // We put the default thumbnail in the cache anyways 249 mThumbnailCache.put(t.key, loadThumbnail); 250 } 251 } 252 if (!mCancelled) { 253 // Notify that the task data has changed 254 final Drawable newIcon = loadIcon; 255 final Bitmap newThumbnail = loadThumbnail; 256 mMainThreadHandler.post(new Runnable() { 257 @Override 258 public void run() { 259 t.notifyTaskDataLoaded(newThumbnail, newIcon); 260 } 261 }); 262 } 263 } 264 265 // If there are no other items in the list, then just wait until something is added 266 if (!mCancelled && mLoadQueue.isEmpty()) { 267 synchronized(mLoadQueue) { 268 try { 269 if (Console.Enabled) { 270 Console.log(Constants.Log.App.TaskDataLoader, 271 "[TaskResourceLoader|waitOnLoadQueue]"); 272 } 273 mWaitingOnLoadQueue = true; 274 mLoadQueue.wait(); 275 mWaitingOnLoadQueue = false; 276 } catch (InterruptedException ie) { 277 ie.printStackTrace(); 278 } 279 } 280 } 281 } 282 } 283 } 284} 285 286/* Recents task loader 287 * NOTE: We should not hold any references to a Context from a static instance */ 288public class RecentsTaskLoader { 289 static RecentsTaskLoader sInstance; 290 291 SystemServicesProxy mSystemServicesProxy; 292 DrawableLruCache mApplicationIconCache; 293 BitmapLruCache mThumbnailCache; 294 TaskResourceLoadQueue mLoadQueue; 295 TaskResourceLoader mLoader; 296 297 RecentsPackageMonitor mPackageMonitor; 298 299 int mMaxThumbnailCacheSize; 300 int mMaxIconCacheSize; 301 302 BitmapDrawable mDefaultApplicationIcon; 303 Bitmap mDefaultThumbnail; 304 Bitmap mLoadingThumbnail; 305 306 /** Private Constructor */ 307 private RecentsTaskLoader(Context context) { 308 // Calculate the cache sizes, we just use a reasonable number here similar to those 309 // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory 310 // for icons. 311 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 312 mMaxThumbnailCacheSize = maxMemory / 8; 313 mMaxIconCacheSize = mMaxThumbnailCacheSize / 4; 314 int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 : 315 mMaxIconCacheSize; 316 int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 : 317 mMaxThumbnailCacheSize; 318 319 if (Console.Enabled) { 320 Console.log(Constants.Log.App.TaskDataLoader, 321 "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize + 322 " iconCache: " + iconCacheSize); 323 } 324 325 // Create the default assets 326 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 327 icon.eraseColor(0x00000000); 328 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 329 mDefaultThumbnail.setHasAlpha(false); 330 mDefaultThumbnail.eraseColor(0xFFffffff); 331 mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 332 mLoadingThumbnail.setHasAlpha(false); 333 mLoadingThumbnail.eraseColor(0xFFffffff); 334 mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon); 335 336 // Initialize the proxy, cache and loaders 337 mSystemServicesProxy = new SystemServicesProxy(context); 338 mPackageMonitor = new RecentsPackageMonitor(); 339 mLoadQueue = new TaskResourceLoadQueue(); 340 mApplicationIconCache = new DrawableLruCache(iconCacheSize); 341 mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); 342 mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, 343 mDefaultThumbnail); 344 345 if (Console.Enabled) { 346 Console.log(Constants.Log.App.TaskDataLoader, 347 "[RecentsTaskLoader|defaultBitmaps]", 348 "icon: " + mDefaultApplicationIcon + 349 " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed); 350 } 351 } 352 353 /** Initializes the recents task loader */ 354 public static RecentsTaskLoader initialize(Context context) { 355 if (sInstance == null) { 356 sInstance = new RecentsTaskLoader(context); 357 } 358 return sInstance; 359 } 360 361 /** Returns the current recents task loader */ 362 public static RecentsTaskLoader getInstance() { 363 return sInstance; 364 } 365 366 /** Returns the system services proxy */ 367 public SystemServicesProxy getSystemServicesProxy() { 368 return mSystemServicesProxy; 369 } 370 371 private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) { 372 long t1 = System.currentTimeMillis(); 373 374 List<ActivityManager.RecentTaskInfo> tasks = 375 ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier()); 376 Collections.reverse(tasks); 377 if (Console.Enabled) { 378 Console.log(Constants.Log.App.TimeSystemCalls, 379 "[RecentsTaskLoader|getRecentTasks]", 380 "" + (System.currentTimeMillis() - t1) + "ms"); 381 Console.log(Constants.Log.App.TaskDataLoader, 382 "[RecentsTaskLoader|tasks]", "" + tasks.size()); 383 } 384 385 return tasks; 386 } 387 388 /** Reload the set of recent tasks */ 389 public SpaceNode reload(Context context, int preloadCount) { 390 long t1 = System.currentTimeMillis(); 391 392 if (Console.Enabled) { 393 Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]"); 394 } 395 RecentsConfiguration config = RecentsConfiguration.getInstance(); 396 Resources res = context.getResources(); 397 ArrayList<Task> tasksToForceLoad = new ArrayList<Task>(); 398 TaskStack stack = new TaskStack(); 399 SpaceNode root = new SpaceNode(context); 400 root.setStack(stack); 401 402 // Get the recent tasks 403 SystemServicesProxy ssp = mSystemServicesProxy; 404 List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp); 405 406 // Add each task to the task stack 407 t1 = System.currentTimeMillis(); 408 int taskCount = tasks.size(); 409 for (int i = 0; i < taskCount; i++) { 410 ActivityManager.RecentTaskInfo t = tasks.get(i); 411 ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId); 412 if (info == null) continue; 413 414 ActivityManager.TaskDescription av = t.taskDescription; 415 String activityLabel = null; 416 Drawable activityIcon = null; 417 int activityColor = config.taskBarViewDefaultBackgroundColor; 418 if (av != null) { 419 activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info)); 420 activityIcon = (av.getIcon() != null) ? 421 ssp.getBadgedIcon(new BitmapDrawable(res, av.getIcon()), t.userId) : null; 422 if (av.getPrimaryColor() != 0) { 423 activityColor = av.getPrimaryColor(); 424 } 425 } else { 426 activityLabel = ssp.getActivityLabel(info); 427 } 428 boolean isForemostTask = (i == (taskCount - 1)); 429 430 // Create a new task 431 Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel, 432 activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime); 433 434 // Preload the specified number of apps 435 if (i >= (taskCount - preloadCount)) { 436 if (Console.Enabled) { 437 Console.log(Constants.Log.App.TaskDataLoader, 438 "[RecentsTaskLoader|preloadTask]", 439 "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName()); 440 } 441 442 // Load the icon from the cache if possible 443 task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key); 444 if (task.applicationIcon == null) { 445 if (isForemostTask) { 446 // We force loading the application icon for the foremost task 447 task.applicationIcon = ssp.getActivityIcon(info, task.userId); 448 if (task.applicationIcon != null) { 449 mApplicationIconCache.put(task.key, task.applicationIcon); 450 } else { 451 task.applicationIcon = mDefaultApplicationIcon; 452 } 453 } else { 454 // Either the task has updated, or we haven't cached any information for the 455 // task, so reload it 456 tasksToForceLoad.add(task); 457 } 458 } 459 460 // Load the thumbnail (if possible and not the foremost task, from the cache) 461 task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key); 462 if (task.thumbnail == null) { 463 if (Console.Enabled) { 464 Console.log(Constants.Log.App.TaskDataLoader, 465 "[RecentsTaskLoader|loadingTaskThumbnail]"); 466 } 467 if (isForemostTask) { 468 // We force loading the thumbnail icon for the foremost task 469 task.thumbnail = ssp.getTaskThumbnail(task.key.id); 470 if (task.thumbnail != null) { 471 task.thumbnail.setHasAlpha(false); 472 } else { 473 task.thumbnail = mDefaultThumbnail; 474 } 475 mThumbnailCache.put(task.key, task.thumbnail); 476 } else { 477 // Either the task has updated, or we haven't cached any information for the 478 // task, so reload it 479 tasksToForceLoad.add(task); 480 } 481 } 482 } 483 484 // Add the task to the stack 485 if (Console.Enabled) { 486 Console.log(Constants.Log.App.TaskDataLoader, 487 " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName()); 488 } 489 stack.addTask(task); 490 } 491 if (Console.Enabled) { 492 Console.log(Constants.Log.App.TimeSystemCalls, 493 "[RecentsTaskLoader|getAllTaskTopThumbnail]", 494 "" + (System.currentTimeMillis() - t1) + "ms"); 495 } 496 497 // Simulate the groupings that we describe 498 stack.createSimulatedAffiliatedGroupings(); 499 500 // Start the task loader 501 mLoader.start(context); 502 503 // Add all the tasks that we are force/re-loading 504 for (Task t : tasksToForceLoad) { 505 mLoadQueue.addTask(t, true); 506 } 507 508 // Update the package monitor with the list of packages to listen for 509 mPackageMonitor.setTasks(tasks); 510 511 return root; 512 } 513 514 /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */ 515 public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) { 516 List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp); 517 TaskStack stack = new TaskStack(); 518 519 int taskCount = tasks.size(); 520 for (int i = 0; i < taskCount; i++) { 521 ActivityManager.RecentTaskInfo t = tasks.get(i); 522 ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId); 523 if (info == null) continue; 524 525 stack.addTask(new Task(t.persistentId, true, t.baseIntent, null, null, 0, 0, 526 t.firstActiveTime, t.lastActiveTime)); 527 } 528 stack.createSimulatedAffiliatedGroupings(); 529 return stack; 530 } 531 532 /** Acquires the task resource data directly from the pool. */ 533 public void loadTaskData(Task t) { 534 Drawable applicationIcon = mApplicationIconCache.get(t.key); 535 Bitmap thumbnail = mThumbnailCache.get(t.key); 536 537 if (Console.Enabled) { 538 Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]", 539 t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail + 540 " thumbnailCacheSize: " + mThumbnailCache.size()); 541 } 542 543 boolean requiresLoad = false; 544 if (applicationIcon == null) { 545 applicationIcon = mDefaultApplicationIcon; 546 requiresLoad = true; 547 } 548 if (thumbnail == null) { 549 thumbnail = mLoadingThumbnail; 550 requiresLoad = true; 551 } 552 if (requiresLoad) { 553 mLoadQueue.addTask(t, false); 554 } 555 t.notifyTaskDataLoaded(thumbnail, applicationIcon); 556 } 557 558 /** Releases the task resource data back into the pool. */ 559 public void unloadTaskData(Task t) { 560 if (Console.Enabled) { 561 Console.log(Constants.Log.App.TaskDataLoader, 562 "[RecentsTaskLoader|unloadTask]", t + 563 " thumbnailCacheSize: " + mThumbnailCache.size()); 564 } 565 566 mLoadQueue.removeTask(t); 567 t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); 568 } 569 570 /** Completely removes the resource data from the pool. */ 571 public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) { 572 if (Console.Enabled) { 573 Console.log(Constants.Log.App.TaskDataLoader, 574 "[RecentsTaskLoader|deleteTask]", t); 575 } 576 577 mLoadQueue.removeTask(t); 578 mThumbnailCache.remove(t.key); 579 mApplicationIconCache.remove(t.key); 580 if (notifyTaskDataUnloaded) { 581 t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); 582 } 583 } 584 585 /** Stops the task loader and clears all pending tasks */ 586 void stopLoader() { 587 if (Console.Enabled) { 588 Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]"); 589 } 590 mLoader.stop(); 591 mLoadQueue.clearTasks(); 592 } 593 594 /** Registers any broadcast receivers. */ 595 public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) { 596 // Register the broadcast receiver to handle messages related to packages being added/removed 597 mPackageMonitor.register(context, cb); 598 } 599 600 /** Unregisters any broadcast receivers. */ 601 public void unregisterReceivers() { 602 mPackageMonitor.unregister(); 603 } 604 605 /** 606 * Handles signals from the system, trimming memory when requested to prevent us from running 607 * out of memory. 608 */ 609 public void onTrimMemory(int level) { 610 if (Console.Enabled) { 611 Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]", 612 Console.trimMemoryLevelToString(level)); 613 } 614 615 switch (level) { 616 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: 617 // Stop the loader immediately when the UI is no longer visible 618 stopLoader(); 619 break; 620 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE: 621 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: 622 // We are leaving recents, so trim the data a bit 623 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2); 624 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2); 625 break; 626 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: 627 case ComponentCallbacks2.TRIM_MEMORY_MODERATE: 628 // We are going to be low on memory 629 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4); 630 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4); 631 break; 632 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: 633 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: 634 // We are low on memory, so release everything 635 mThumbnailCache.evictAll(); 636 mApplicationIconCache.evictAll(); 637 break; 638 default: 639 break; 640 } 641 } 642} 643