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

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

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


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hailushijie

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值