107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor/* 207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Copyright (C) 2012 The Android Open Source Project 307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Licensed under the Apache License, Version 2.0 (the "License"); 507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * you may not use this file except in compliance with the License. 607ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * You may obtain a copy of the License at 707ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 807ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * http://www.apache.org/licenses/LICENSE-2.0 907ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 1007ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Unless required by applicable law or agreed to in writing, software 1107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * distributed under the License is distributed on an "AS IS" BASIS, 1207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * See the License for the specific language governing permissions and 1407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * limitations under the License. 1507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor */ 1651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpackage com.android.mms.util; 1851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.ArrayList; 2051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.HashMap; 2151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.HashSet; 2251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.Set; 2351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.Executor; 2451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.LinkedBlockingQueue; 2551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.ThreadFactory; 2651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.ThreadPoolExecutor; 2751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.TimeUnit; 2851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.atomic.AtomicInteger; 2951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 3051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.content.Context; 3151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.net.Uri; 3251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.os.Handler; 3351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.util.Log; 3451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 3551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor/** 3651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Base class {@link BackgroundLoaderManager} used by {@link MessagingApplication} for loading 3751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * items (images, thumbnails, pdus, etc.) in the background off of the UI thread. 3851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 3951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Public methods should only be used from a single thread (typically the UI 4051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * thread). Callbacks will be invoked on the thread where the ThumbnailManager 4151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * was instantiated. 4251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 4351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may 4451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * request lots of images around the same time, and AsyncTask may reject tasks 4551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * in that case and has no way of bounding the number of threads used by those 4651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * tasks. 4751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * 4851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Based on BooksImageManager by Virgil King. 4951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 5051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorabstract class BackgroundLoaderManager { 5151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final String TAG = "BackgroundLoaderManager"; 5251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 5351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final int MAX_THREADS = 2; 5451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 5551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 5651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * URIs for which tasks are currently enqueued. Don't enqueue new tasks for 5751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * these, just add new callbacks. 5851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 5951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor protected final Set<Uri> mPendingTaskUris; 6051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 6151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor protected final HashMap<Uri, Set<ItemLoadedCallback>> mCallbacks; 6251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 6351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor protected final Executor mExecutor; 6451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 6551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor protected final Handler mCallbackHandler; 6651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 6751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor BackgroundLoaderManager(Context context) { 6851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mPendingTaskUris = new HashSet<Uri>(); 6951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbacks = new HashMap<Uri, Set<ItemLoadedCallback>>(); 7051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); 7151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final int poolSize = MAX_THREADS; 7251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mExecutor = new ThreadPoolExecutor( 7351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor poolSize, poolSize, 5, TimeUnit.SECONDS, queue, 7451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor new BackgroundLoaderThreadFactory(getTag())); 7551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbackHandler = new Handler(); 7651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 7751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 7851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 7951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Release memory if possible. 8051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 8151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void onLowMemory() { 8251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor clear(); 8351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 8451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 8551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void clear() { 8651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 8751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 8851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 8951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Return a tag that will be used to name threads so they'll be visible in the debugger. 9051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 9151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public abstract String getTag(); 9251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 9351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 9451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Attempts to add a callback for a resource. 9551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * 9651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param uri the {@link Uri} of the resource for which a callback is 9751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * desired. 9851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param callback the callback to register. 9951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @return {@code true} if the callback is guaranteed to be invoked with 10051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * a non-null result (as long as there is no error and the 10151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * callback is not canceled), or {@code false} if the callback 10251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * cannot be registered with this task because the result for 10351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * the desired {@link Uri} has already been discarded due to 10451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * low-memory. 10551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @throws NullPointerException if either argument is {@code null} 10651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 10751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public boolean addCallback(Uri uri, ItemLoadedCallback callback) { 10851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 10951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Adding image callback " + callback); 11051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 11151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (uri == null) { 11251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException("uri is null"); 11351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 11451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callback == null) { 11551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException("callback is null"); 11651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 11751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Set<ItemLoadedCallback> callbacks = mCallbacks.get(uri); 11851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbacks == null) { 11951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor callbacks = new HashSet<ItemLoadedCallback>(4); 12051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbacks.put(uri, callbacks); 12151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 12251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor callbacks.add(callback); 12351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return true; 12451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 12551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 12651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void cancelCallback(ItemLoadedCallback callback) { 12751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 12851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Cancelling image callback " + callback); 12951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 13051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor for (final Uri uri : mCallbacks.keySet()) { 13151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final Set<ItemLoadedCallback> callbacks = mCallbacks.get(uri); 13251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor callbacks.remove(callback); 13351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 13451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 13551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 13651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 13751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Copies the elements of a {@link Set} into an {@link ArrayList}. 13851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 13951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor @SuppressWarnings("unchecked") 14051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor protected static <T> ArrayList<T> asList(Set<T> source) { 14151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return new ArrayList<T>(source); 14251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 14451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 14551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * {@link ThreadFactory} which sets a meaningful name for the thread. 14651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 14751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static class BackgroundLoaderThreadFactory implements ThreadFactory { 14851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final AtomicInteger mCount = new AtomicInteger(1); 14951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final String mTag; 15051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 15151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public BackgroundLoaderThreadFactory(String tag) { 15251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mTag = tag; 15351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 15451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 15551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public Thread newThread(final Runnable r) { 156721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Thread t = new Thread(r, mTag + "-" + mCount.getAndIncrement()); 157721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 158721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (t.getPriority() != Thread.MIN_PRIORITY) 159721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor t.setPriority(Thread.MIN_PRIORITY); 160721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 161721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return t; 16251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 16351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 16451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor} 165