android短彩信幻灯片异步加载机制

来源:互联网 发布:淘宝直播怎么上传宝贝 编辑:程序博客网 时间:2024/05/18 21:42

转载请注明出处: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();        }    }

以上就是幻灯片异步加载的一个大体流程。


原创粉丝点击