转载请注明出处:http://blog.csdn.net/droyon/article/details/8703779
记不清是android 4.0之后还是4.1之后,浏览信息时,彩信幻灯片不再随着信息内容一并显示,而是在信息内容显示后,开启后台线程,异步加载彩信幻灯片,加载完毕之后再显示附件。为什么要这么设计那?主要是为了解决彩信显示缓慢的问题。在原先的设计中,彩信想要显示,首先要做准备工作,准备工作包括从数据库中加载信息的内容,收件人,发送时间,主题,类型,状态报告等基础内容,其中还包括了一项费时的操作,那就是加载彩信幻灯片附件。只有上述工作全部完成之后彩信才会显示在界面上,用户才可以进行浏览。这种设计非常容易卡机,有时还会引起ANR(应用程序无相应),尤其当我们的运营商要求彩信幻灯片支持20页(默认10页),这个问题就更加严重,长时间不显示信息,严重影响性能。其实从功能设计上来说,用户首先希望看到的是彩信收件人,彩信主题,发送时间,发送状态报告等基础信息,然后才会选择查看幻灯片等多媒体附件。我们完全可以将幻灯片的加载滞后。androd目前给出了这样的一个设计。
首先根据当前回话,查询数据库,加载信息条目。每一个信息条目,每一条信息条目,用视图MessageListItem来显示,信息内容数据包装在MessageItem中。
MessageListAdapter.java
public View newView(Context context, Cursor cursor, ViewGroup parent) {
int boxType = getItemViewType(cursor);
View view = mInflater.inflate((boxType == INCOMING_ITEM_TYPE_SMS ||
boxType == INCOMING_ITEM_TYPE_MMS) ?
R.layout.message_list_item_recv : R.layout.message_list_item_send,
parent, false);
if (boxType == INCOMING_ITEM_TYPE_MMS || boxType == OUTGOING_ITEM_TYPE_MMS) {
// We've got an mms item, pre-inflate the mms portion of the view
view.findViewById(R.id.mms_layout_view_stub).setVisibility(View.VISIBLE);
}
return view;
}
这个view就是MessageLIstItem。然后MessagelistAdapter会调用bindView方法,完成视图的加载,在加载中,主要是调用MessageItem的bind方法。我们现在主要关心的是MessageItem对彩信内容数据的加载,以及着重注意异步加载幻灯片附件的逻辑处理。
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof MessageListItem) {
String type = cursor.getString(mColumnsMap.mColumnMsgType);
long msgId = cursor.getLong(mColumnsMap.mColumnMsgId);
MessageItem msgItem = getCachedMessageItem(type, msgId, cursor);
if (msgItem != null) {
MessageListItem mli = (MessageListItem) view;
int position = cursor.getPosition();
mli.bind(msgItem, position == cursor.getCount() - 1, position);
mli.setMsgListItemHandler(mMsgListItemHandler);
}
}
}
根据信息的type以及msgId,通过getCachedMessageItem方法,得到一个MessageItem。
public MessageItem getCachedMessageItem(String type, long msgId, Cursor c) {
MessageItem item = mMessageItemCache.get(getKey(type, msgId));
if (item == null && c != null && isCursorValid(c)) {
try {
item = new MessageItem(mContext, type, c, mColumnsMap, mHighlight, mFullTimestamp, mSentTimestamp);
mMessageItemCache.put(getKey(item.mType, item.mMsgId), item);
} catch (MmsException e) {
Log.e(TAG, "getCachedMessageItem: ", e);
}
}
return item;
}
在MessageItem中会对cursor中的数据进行加载。
MessgeItem.java
MessageItem(Context context, String type, final Cursor cursor,
final ColumnsMap columnsMap, Pattern highlight, boolean fullTimestamp, boolean sentTimestamp) throws MmsException {
mContext = context;
mMsgId = cursor.getLong(columnsMap.mColumnMsgId);
mHighlight = highlight;
mType = type;
mCursor = cursor;
mColumnsMap = columnsMap;
mFullTimestamp = fullTimestamp;
mSentTimestamp = sentTimestamp;
if ("sms".equals(type)) {
mReadReport = false; // No read reports in sms
long status = cursor.getLong(columnsMap.mColumnSmsStatus);
if (status == Sms.STATUS_NONE) {
// No delivery report requested
mDeliveryStatus = DeliveryStatus.NONE;
} else if (status >= Sms.STATUS_FAILED) {
// Failure
mDeliveryStatus = DeliveryStatus.FAILED;
} else if (status >= Sms.STATUS_PENDING) {
// Pending
mDeliveryStatus = DeliveryStatus.PENDING;
} else {
// Success
mDeliveryStatus = DeliveryStatus.RECEIVED;
}
mMessageUri = ContentUris.withAppendedId(Sms.CONTENT_URI, mMsgId);
// Set contact and message body
mBoxId = cursor.getInt(columnsMap.mColumnSmsType);
mAddress = cursor.getString(columnsMap.mColumnSmsAddress);
if (Sms.isOutgoingFolder(mBoxId)) {
String meString = context.getString(
R.string.messagelist_sender_self);
mContact = meString;
} else {
// For incoming messages, the ADDRESS field contains the sender.
mContact = Contact.get(mAddress, false).getName();
}
mBody = cursor.getString(columnsMap.mColumnSmsBody);
// Unless the message is currently in the progress of being sent, it gets a time stamp.
if (!isOutgoingMessage()) {
// Set "received" or "sent" time stamp
long date = cursor.getLong(columnsMap.mColumnSmsDate);
if (mSentTimestamp && (mBoxId == Sms.MESSAGE_TYPE_INBOX)) {
date = cursor.getLong(columnsMap.mColumnSmsDateSent);
}
mTimestamp = MessageUtils.formatTimeStampString(context, date, mFullTimestamp);
}
mLocked = cursor.getInt(columnsMap.mColumnSmsLocked) != 0;
mErrorCode = cursor.getInt(columnsMap.mColumnSmsErrorCode);
} else if ("mms".equals(type)) {
mMessageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, mMsgId);
mBoxId = cursor.getInt(columnsMap.mColumnMmsMessageBox);
mMessageType = cursor.getInt(columnsMap.mColumnMmsMessageType);
mErrorType = cursor.getInt(columnsMap.mColumnMmsErrorType);
String subject = cursor.getString(columnsMap.mColumnMmsSubject);
if (!TextUtils.isEmpty(subject)) {
EncodedStringValue v = new EncodedStringValue(
cursor.getInt(columnsMap.mColumnMmsSubjectCharset),
PduPersister.getBytes(subject));
mSubject = v.getString();
}
mLocked = cursor.getInt(columnsMap.mColumnMmsLocked) != 0;
mSlideshow = null;
mAttachmentType = ATTACHMENT_TYPE_NOT_LOADED;
mDeliveryStatus = DeliveryStatus.NONE;
mReadReport = false;
mBody = null;
mMessageSize = 0;
mTextContentType = null;
mTimestamp = null;
mMmsStatus = cursor.getInt(columnsMap.mColumnMmsStatus);
// Start an async load of the pdu. If the pdu is already loaded, the callback
// will get called immediately
boolean loadSlideshow = mMessageType != PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
mItemLoadedFuture = MmsApp.getApplication().getPduLoaderManager()//异步加载幻灯片的逻辑
.getPdu(mMessageUri, loadSlideshow,
new PduLoadedMessageItemCallback());
} else {
throw new MmsException("Unknown type of the message: " + type);
}
}
在构造中,会根据信息的类别,加载短信和彩信的数据。数据包括信息的MessageUri,信息的 messageType,信息的主题,信息的boxId,信息的状态等。这些属于基础信息,这些信息是要首先展示给用户的。我们的目的是最后几行加黑的代码,他们就是我们彩信幻灯片后台加载的逻辑代码。
MmsApp.java
public PduLoaderManager getPduLoaderManager() {
return mPduLoaderManager;
}
PduLoadedManager.java
public class PduLoaderManager extends BackgroundLoaderManager {.....}
public PduLoaderManager(final Context context) {
super(context);
mSlideshowCache = new SimpleCache<Uri, SlideshowModel>(8, 16, 0.75f, true);
mPduCache = PduCache.getInstance();
mPduPersister = PduPersister.getPduPersister(context);
mContext = context;
}
方法继承子BackgroundLoadManager。
BackgroundLoadManager.java
BackgroundLoaderManager(Context context) {
mPendingTaskUris = new HashSet<Uri>();//等待加载幻灯片任务集合,内容为彩信uri的集合
mCallbacks = new HashMap<Uri, Set<ItemLoadedCallback>>();//彩信uri为key,value是callback的集合,callback是在幻灯片附件加载任务完成后回调的方法对象。
final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
final int poolSize = MAX_THREADS;
mExecutor = new ThreadPoolExecutor(//开启一个线程池,在新线程中处理幻灯片的加载等工作。
poolSize, poolSize, 5, TimeUnit.SECONDS, queue,
new BackgroundLoaderThreadFactory(getTag()));
mCallbackHandler = new Handler();
}
在上面的MessageItem类中,会调用PduLoadedManager类的getPdu方法。
public ItemLoadedFuture getPdu(Uri uri, boolean requestSlideshow,
final ItemLoadedCallback<PduLoaded> callback) {
if (uri == null) {
throw new NullPointerException();
}
PduCacheEntry cacheEntry = null;
synchronized(mPduCache) {
if (!mPduCache.isUpdating(uri)) {
cacheEntry = mPduCache.get(uri);//第一次会走到这里,cacheEntry为null。
}
}
final SlideshowModel slideshow = (requestSlideshow && !DEBUG_DISABLE_CACHE) ?//requestSlideshow为true,但mSlideshowCache.get(uri)为null。
mSlideshowCache.get(uri) : null;
final boolean slideshowExists = (!requestSlideshow || slideshow != null);//判断slideshowModel是否存在,slideshowModel是幻灯片的实体。
final boolean pduExists = (cacheEntry != null && cacheEntry.getPdu() != null);//pdu是否存在。pdu是幻灯片信息的封装,基类是GenericPdu。
final boolean taskExists = mPendingTaskUris.contains(uri);//判断是否存在线程
final boolean newTaskRequired = (!pduExists || !slideshowExists) && !taskExists;//是否需要新开启线程
final boolean callbackRequired = (callback != null);//是否存在回调对象
if (pduExists && slideshowExists) {//如果pdu存在,并且slideshowModel也存在,那么不用开启线程加载了,直接返回。
if (callbackRequired) {
PduLoaded pduLoaded = new PduLoaded(cacheEntry.getPdu(), slideshow);
callback.onItemLoaded(pduLoaded, null);
}
return new NullItemLoadedFuture();
}
if (callbackRequired) {//如果存在回调对象,那么将uri为key,value为callbacks的集合,加入到mCallbacks中。
addCallback(uri, callback);
}
if (newTaskRequired) {//是否需要开启新的线程,如果需要,那么构建一个PduTask,并且放到线程池中,执行它。
mPendingTaskUris.add(uri);
Runnable task = new PduTask(uri, requestSlideshow);
mExecutor.execute(task);
}
return new ItemLoadedFuture() {
public void cancel() {
cancelCallback(callback);
}
public boolean isDone() {
return false;
}
};
}
PduTask.java
public class PduTask implements Runnable {
private final Uri mUri;
private final boolean mRequestSlideshow;
public PduTask(Uri uri, boolean requestSlideshow) {
if (uri == null) {
throw new NullPointerException();
}
mUri = uri;
mRequestSlideshow = requestSlideshow;
}
/** {@inheritDoc} */
public void run() {
if (DEBUG_DISABLE_PDUS) {
return;
}
if (DEBUG_LONG_WAIT) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
}
GenericPdu pdu = null;//所有彩信pdu的基类,子类中有我们熟悉的SendReq(发送的彩信所包装的pdu),RetrieveConf等
SlideshowModel slideshow = null;
Throwable exception = null;
try {
pdu = mPduPersister.load(mUri);//特别关键的一个函数,在介绍完流程后,会介绍一下这个类的,处理的数据很多,但框架很清晰
if (pdu != null && mRequestSlideshow) {
slideshow = SlideshowModel.createFromPduBody(mContext,//从pdu中解析出SlideshowModel。从SlideshowModel能够得到pdu,那么反过来也一样能够得到。
((MultimediaMessagePdu)pdu).getBody());
}
} catch (final MmsException e) {
Log.e(TAG, "MmsException loading uri: " + mUri, e);
exception = e;
}
final GenericPdu resultPdu = pdu;
final SlideshowModel resultSlideshow = slideshow;
final Throwable resultException = exception;
mCallbackHandler.post(new Runnable() {
public void run() {
final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri);//我们在上面将回调对象的集合加入到了mCallbacks中,现在我们根据uri将回调对象集合取出来
if (callbacks != null) {
// Make a copy so that the callback can unregister itself
for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Invoking pdu callback " + callback);
}
PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow);//遍历集合,然后执行回调函数的onItemLoaded。
callback.onItemLoaded(pduLoaded, resultException);
}
}
// Add the slideshow to the soft cache if the load succeeded
if (resultSlideshow != null) {
mSlideshowCache.put(mUri, resultSlideshow);//将得到的slideshowModel作为value,uri作为key加入到mSlideshowCache中,下次就不需要开启线程加载就可以得到SlideshowModel
}
mCallbacks.remove(mUri);//回调函数执行完毕,那么从mCallbacks中移除
mPendingTaskUris.remove(mUri);//同样移除操作
if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) {
Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size()
+ " remain");
}
}
});
}
}
这个执行完毕,那么我们回到MessageItem看看回调函数。
public class PduLoadedMessageItemCallback implements ItemLoadedCallback {
public void onItemLoaded(Object result, Throwable exception) {
if (exception != null) {
Log.e(TAG, "PduLoadedMessageItemCallback PDU couldn't be loaded: ", exception);
return;
}
PduLoaderManager.PduLoaded pduLoaded = (PduLoaderManager.PduLoaded)result;//回调的结果
long timestamp = 0L;
if (PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND == mMessageType) {//根据当前信息的类型,将pdu将强转成相应的pdu封装类。这里我有一点疑惑(MessageItem中的mMessageType是会改变的,而我们的加载是异步加载,也就说加载前mMessageType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND,如果我们加载完,mMessageType会不会变成另外一个值,比如PduHeaders.MESSAGE_TYPE_SEND_REQ等,那么我们强转会不会产生异常?就目前所知,在接收彩信时,MessageItem类型会发生改变,这种情况应该是会出现的)
mDeliveryStatus = DeliveryStatus.NONE;
NotificationInd notifInd = (NotificationInd)pduLoaded.mPdu;//强转类型为NotificationInd,接收彩信时,为下载彩信内容
interpretFrom(notifInd.getFrom(), mMessageUri);
// Borrow the mBody to hold the URL of the message.
mBody = new String(notifInd.getContentLocation());
mMessageSize = (int) notifInd.getMessageSize();
timestamp = notifInd.getExpiry() * 1000L;
} else {
if (mCursor.isClosed()) {
return;
}
MultimediaMessagePdu msg = (MultimediaMessagePdu)pduLoaded.mPdu;//MultimediaMessagePdu是发送和接收彩信的pdu类型的基类型。
mSlideshow = pduLoaded.mSlideshow;
mAttachmentType = MessageUtils.getAttachmentType(mSlideshow);
if (mMessageType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {//接收彩信的类型,再往下就是继续初始化一些彩信数据
if (msg == null) {
interpretFrom(null, mMessageUri);
} else {
RetrieveConf retrieveConf = (RetrieveConf) msg;
interpretFrom(retrieveConf.getFrom(), mMessageUri);
timestamp = retrieveConf.getDate() * 1000L;
}
} else {
// Use constant string for outgoing messages
mContact = mAddress =
mContext.getString(R.string.messagelist_sender_self);
timestamp = msg == null ? 0 : ((SendReq) msg).getDate() * 1000L;
}
SlideModel slide = mSlideshow == null ? null : mSlideshow.get(0);
if ((slide != null) && slide.hasText()) {
TextModel tm = slide.getText();
mBody = tm.getText();
mTextContentType = tm.getContentType();
}
mMessageSize = mSlideshow == null ? 0 : mSlideshow.getTotalMessageSize();
String report = mCursor.getString(mColumnsMap.mColumnMmsDeliveryReport);
if ((report == null) || !mAddress.equals(mContext.getString(
R.string.messagelist_sender_self))) {
mDeliveryStatus = DeliveryStatus.NONE;
} else {
int reportInt;
try {
reportInt = Integer.parseInt(report);
if (reportInt == PduHeaders.VALUE_YES) {
mDeliveryStatus = DeliveryStatus.RECEIVED;
} else {
mDeliveryStatus = DeliveryStatus.NONE;
}
} catch (NumberFormatException nfe) {
Log.e(TAG, "Value for delivery report was invalid.");
mDeliveryStatus = DeliveryStatus.NONE;
}
}
report = mCursor.getString(mColumnsMap.mColumnMmsReadReport);
if ((report == null) || !mAddress.equals(mContext.getString(
R.string.messagelist_sender_self))) {
mReadReport = false;
} else {
int reportInt;
try {
reportInt = Integer.parseInt(report);
mReadReport = (reportInt == PduHeaders.VALUE_YES);
} catch (NumberFormatException nfe) {
Log.e(TAG, "Value for read report was invalid.");
mReadReport = false;
}
}
}
if (!isOutgoingMessage()) {
if (PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND == mMessageType) {
mTimestamp = mContext.getString(R.string.expire_on,
MessageUtils.formatTimeStampString(mContext, timestamp, mFullTimestamp));
} else {
mTimestamp = MessageUtils.formatTimeStampString(mContext, timestamp, mFullTimestamp);
}
}
if (mPduLoadedCallback != null) {
mPduLoadedCallback.onPduLoaded(MessageItem.this);
}
}
}
异步加载完幻灯片,那么就可以在彩信中显示了。我们主要看幻灯片附件的异步加载,剩下的逻辑以后在分析。
我们最后来看一下
pdu = mPduPersister.load(mUri);
PduPersister.java
这个类在framework层
public GenericPdu load(Uri uri) throws MmsException {
GenericPdu pdu = null;
PduCacheEntry cacheEntry = null;
int msgBox = 0;
long threadId = -1;
try {
synchronized(PDU_CACHE_INSTANCE) {
if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
if (LOCAL_LOGV) {
Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
}
try {
PDU_CACHE_INSTANCE.wait();
} catch (InterruptedException e) {
Log.e(TAG, "load: ", e);
}
cacheEntry = PDU_CACHE_INSTANCE.get(uri);
if (cacheEntry != null) {
return cacheEntry.getPdu();
}
}
// Tell the cache to indicate to other callers that this item
// is currently being updated.
PDU_CACHE_INSTANCE.setUpdating(uri, true);
}
Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,//加载彩信数据库中的内容
PDU_PROJECTION, null, null, null);
PduHeaders headers = new PduHeaders();
Set<Entry<Integer, Integer>> set;
long msgId = ContentUris.parseId(uri);
try {
if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
throw new MmsException("Bad uri: " + uri);
}
msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
threadId = c.getLong(PDU_COLUMN_THREAD_ID);
set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();//解析数据库中的内容到PduHeaders中。
for (Entry<Integer, Integer> e : set) {
setEncodedStringValueToHeaders(
c, e.getValue(), headers, e.getKey());
}
set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
for (Entry<Integer, Integer> e : set) {
setTextStringToHeaders(
c, e.getValue(), headers, e.getKey());
}
set = OCTET_COLUMN_INDEX_MAP.entrySet();
for (Entry<Integer, Integer> e : set) {
setOctetToHeaders(
c, e.getValue(), headers, e.getKey());
}
set = LONG_COLUMN_INDEX_MAP.entrySet();
for (Entry<Integer, Integer> e : set) {
setLongToHeaders(
c, e.getValue(), headers, e.getKey());
}
} finally {
if (c != null) {
c.close();
}
}
// Check whether 'msgId' has been assigned a valid value.
if (msgId == -1L) {
throw new MmsException("Error! ID of the message: -1.");
}
// Load address information of the MM.
loadAddress(msgId, headers);
int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
PduBody body = new PduBody();
// For PDU which type is M_retrieve.conf or Send.req, we should
// load multiparts and put them into the body of the PDU.
if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
|| (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {//构建pduBody
PduPart[] parts = loadParts(msgId);//这部分相信大家可以看懂,同样是加载数据库
if (parts != null) {
int partsNum = parts.length;
for (int i = 0; i < partsNum; i++) {
body.addPart(parts[i]);
}
}
}
switch (msgType) {//根据msgType,构建GeniricPdu的实例子类型
case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
pdu = new NotificationInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
pdu = new DeliveryInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
pdu = new ReadOrigInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
pdu = new RetrieveConf(headers, body);
break;
case PduHeaders.MESSAGE_TYPE_SEND_REQ:
pdu = new SendReq(headers, body);
break;
case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
pdu = new AcknowledgeInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
pdu = new NotifyRespInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
pdu = new ReadRecInd(headers);
break;
case PduHeaders.MESSAGE_TYPE_SEND_CONF:
case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
throw new MmsException(
"Unsupported PDU type: " + Integer.toHexString(msgType));
default:
throw new MmsException(
"Unrecognized PDU type: " + Integer.toHexString(msgType));
}
} finally {
synchronized(PDU_CACHE_INSTANCE) {
if (pdu != null) {
assert(PDU_CACHE_INSTANCE.get(uri) == null);
// Update the cache entry with the real info
cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
PDU_CACHE_INSTANCE.put(uri, cacheEntry);
}
PDU_CACHE_INSTANCE.setUpdating(uri, false);
PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
}
}
return pdu;
}
在我们每次删除会话时,会清空所有缓存。
public static void startDelete(ConversationQueryHandler handler, int token, boolean deleteAll,
long threadId) {
synchronized(sDeletingThreadsLock) {
if (sDeletingThreads) {
Log.e(TAG, "startDeleteAll already in the middle of a delete", new Exception());
}
sDeletingThreads = true;
Uri uri = ContentUris.withAppendedId(Threads.CONTENT_URI, threadId);
String selection = deleteAll ? null : "locked=0";
MmsApp.getApplication().getPduLoaderManager().clear();//这里会将缓存的信息全部清空,下次进入短彩信,重新更新缓存。
// HACK: the keys to the thumbnail cache are the part uris, such as mms/part/3
// Because the part table doesn't have auto-increment ids, the part ids are reused
// when a message or thread is deleted. For now, we're clearing the whole thumbnail
// cache so we don't retrieve stale images when part ids are reused. This will be
// fixed in the next release in the mms provider.
MmsApp.getApplication().getThumbnailManager().clear();
handler.setDeleteToken(token);
handler.startDelete(token, new Long(threadId), uri, selection, null);
}
}
当删除单条信息时,会先清空这条信息对应的缓存,然后在删除。这个顺序很重要,如果先删除信息,在清空缓存信息,那么短彩信可能会引发同步错误。
private class DeleteMessageListener implements OnClickListener {
private final MessageItem mMessageItem;
public DeleteMessageListener(MessageItem messageItem) {
mMessageItem = messageItem;
}
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
new AsyncTask<Void, Void, Void>() {
protected Void doInBackground(Void... none) {
if (mMessageItem.isMms()) {
WorkingMessage.removeThumbnailsFromCache(mMessageItem.getSlideshow());
MmsApp.getApplication().getPduLoaderManager()
.removePdu(mMessageItem.mMessageUri);
// Delete the message *after* we've removed the thumbnails because we
// need the pdu and slideshow for removeThumbnailsFromCache to work.
}
mBackgroundQueryHandler.startDelete(DELETE_MESSAGE_TOKEN,
null, mMessageItem.mMessageUri,
mMessageItem.mLocked ? null : "locked=0", null);
return null;
}
}.execute();
}
}
以上就是幻灯片异步加载的一个大体流程。