PduLoaderManager.java revision 79bf6f70c54d08dc9c3481b8461a3a46a3cefb83
1/* 2 * Copyright (C) 2012 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.mms.util; 18 19import java.util.Set; 20 21import android.content.Context; 22import android.net.Uri; 23import android.util.Log; 24 25import com.android.mms.LogTag; 26import com.android.mms.model.SlideshowModel; 27import com.google.android.mms.MmsException; 28import com.google.android.mms.pdu.GenericPdu; 29import com.google.android.mms.pdu.MultimediaMessagePdu; 30import com.google.android.mms.pdu.PduPersister; 31import com.google.android.mms.util.PduCache; 32import com.google.android.mms.util.PduCacheEntry; 33 34/** 35 * Primary {@link PduLoaderManager} implementation used by {@link MessagingApplication}. 36 * <p> 37 * Public methods should only be used from a single thread (typically the UI 38 * thread). Callbacks will be invoked on the thread where the PduLoaderManager 39 * was instantiated. 40 * <p> 41 * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may 42 * request lots of pdus around the same time, and AsyncTask may reject tasks 43 * in that case and has no way of bounding the number of threads used by those 44 * tasks. 45 * <p> 46 * PduLoaderManager is used to asynchronously load mms pdu's and then build a slideshow model 47 * from that loaded pdu. Then it will call the passed in callback with the result. This class 48 * uses the PduCache built into the mms framework. It also manages a local cache of slideshow 49 * models. The slideshow cache uses SoftReferences to hang onto the slideshow. 50 * 51 * Based on BooksImageManager by Virgil King. 52 */ 53public class PduLoaderManager extends BackgroundLoaderManager { 54 private static final String TAG = "Mms:PduLoaderManager"; 55 56 private static final boolean DEBUG_DISABLE_PDUS = false; 57 private static final boolean DEBUG_LONG_WAIT = false; 58 59 private static PduCache mPduCache; 60 private final PduPersister mPduPersister; 61 private final SimpleCache<Uri, SlideshowModel> mSlideshowCache; 62 private final Context mContext; 63 64 public PduLoaderManager(final Context context) { 65 super(context); 66 67 mSlideshowCache = new SimpleCache<Uri, SlideshowModel>(8, 48, 0.75f); 68 mPduCache = PduCache.getInstance(); 69 mPduPersister = PduPersister.getPduPersister(context); 70 mContext = context; 71 } 72 73 public ItemLoadedFuture getPdu(Uri uri, boolean requestSlideshow, 74 final ItemLoadedCallback<PduLoaded> callback) { 75 if (uri == null) { 76 throw new NullPointerException(); 77 } 78 79 PduCacheEntry cacheEntry = null; 80 synchronized(mPduCache) { 81 if (!mPduCache.isUpdating(uri)) { 82 cacheEntry = mPduCache.get(uri); 83 } 84 } 85 final SlideshowModel slideshow = requestSlideshow ? mSlideshowCache.get(uri) : null; 86 87 final boolean slideshowExists = (!requestSlideshow || slideshow != null); 88 final boolean pduExists = (cacheEntry != null && cacheEntry.getPdu() != null); 89 final boolean taskExists = mPendingTaskUris.contains(uri); 90 final boolean newTaskRequired = (!pduExists || !slideshowExists) && !taskExists; 91 final boolean callbackRequired = (callback != null); 92 93 if (pduExists && slideshowExists) { 94 if (callbackRequired) { 95 PduLoaded pduLoaded = new PduLoaded(cacheEntry.getPdu(), slideshow); 96 callback.onItemLoaded(pduLoaded, null); 97 } 98 return new NullItemLoadedFuture(); 99 } 100 101 if (callbackRequired) { 102 addCallback(uri, callback); 103 } 104 105 if (newTaskRequired) { 106 mPendingTaskUris.add(uri); 107 Runnable task = new PduTask(uri, requestSlideshow); 108 mExecutor.execute(task); 109 } 110 return new ItemLoadedFuture() { 111 public void cancel() { 112 cancelCallback(callback); 113 } 114 public boolean isDone() { 115 return false; 116 } 117 }; 118 } 119 120 @Override 121 public void clear() { 122 super.clear(); 123 124 PduCache.getInstance().purgeAll(); 125 mSlideshowCache.clear(); 126 } 127 128 public String getTag() { 129 return TAG; 130 } 131 132 public class PduTask implements Runnable { 133 private final Uri mUri; 134 private final boolean mRequestSlideshow; 135 136 public PduTask(Uri uri, boolean requestSlideshow) { 137 if (uri == null) { 138 throw new NullPointerException(); 139 } 140 mUri = uri; 141 mRequestSlideshow = requestSlideshow; 142 } 143 144 /** {@inheritDoc} */ 145 public void run() { 146 if (DEBUG_DISABLE_PDUS) { 147 return; 148 } 149 if (DEBUG_LONG_WAIT) { 150 try { 151 Thread.sleep(10000); 152 } catch (InterruptedException e) { 153 } 154 } 155 GenericPdu pdu = null; 156 SlideshowModel slideshow = null; 157 Throwable exception = null; 158 try { 159 pdu = mPduPersister.load(mUri); 160 if (pdu != null && mRequestSlideshow) { 161 slideshow = SlideshowModel.createFromPduBody(mContext, 162 ((MultimediaMessagePdu)pdu).getBody()); 163 } 164 } catch (final MmsException e) { 165 Log.e(TAG, "MmsException loading uri: " + mUri, e); 166 exception = e; 167 } 168 final GenericPdu resultPdu = pdu; 169 final SlideshowModel resultSlideshow = slideshow; 170 final Throwable resultException = exception; 171 mCallbackHandler.post(new Runnable() { 172 public void run() { 173 final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri); 174 if (callbacks != null) { 175 // Make a copy so that the callback can unregister itself 176 for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) { 177 if (Log.isLoggable(TAG, Log.DEBUG)) { 178 Log.d(TAG, "Invoking pdu callback " + callback); 179 } 180 PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow); 181 callback.onItemLoaded(pduLoaded, resultException); 182 } 183 } 184 // Add the slideshow to the soft cache if the load succeeded 185 if (resultSlideshow != null) { 186 mSlideshowCache.put(mUri, resultSlideshow); 187 } 188 189 mCallbacks.remove(mUri); 190 mPendingTaskUris.remove(mUri); 191 192 if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) { 193 Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size() 194 + " remain"); 195 } 196 } 197 }); 198 } 199 } 200 201 public static class PduLoaded { 202 public final GenericPdu mPdu; 203 public final SlideshowModel mSlideshow; 204 205 public PduLoaded(GenericPdu pdu, SlideshowModel slideshow) { 206 mPdu = pdu; 207 mSlideshow = slideshow; 208 } 209 } 210} 211