本文共 9183 字,大约阅读时间需要 30 分钟。
对于彩信与数据库的交互操作,google并没有将这部分代码放在Mms中,而是放在framework中的pdu部分。
具体代码路径是在:opt\telephony\src\java\com\google\android\mms\pdu 现在我们来先简单介绍下。从发送彩信过程中需要使用到的类分析。我们从前面博客知道,发送彩信涉及到的Pdu有:
SendReq sendReq = makeSendReq(conv, subject);PduPersister p = PduPersister.getPduPersister(mContext);GenericPdu pdu = p.load(mMessageUri);SendReq sendReq = (SendReq) pdu;其中间接涉及到的还有EncodedStringValue,CharacterSets,MutimediaMessagePdu,PduBody,PduHeaders等。 SendReq.java 是否还记得前面,我们在介绍彩信发送流程,就有使用到这个类,这个就是存放这发送请求信息的一个类。 其类结构如下: SendReq MultimediaMessagePdu GenericPdu 我们会将发送的信息都存放到里面去,其方法 addBcc 应该是仿造邮件,暗抄送 addCc 抄送 setContentType 设置内容类型,这里是"application/vnd.wap.multipart.related".getBytes() setDeliveryReport设置是否需要发送报告,发送了就会有报告 setExpiry设置短信下载超时时间 setMessageSize设置彩信大小 setMessageClass 设置的是PduHeaders.MESSAGE_CLASS_PERSONAL_STR = "personal",我们注意到pdu表的m_cls字段就是这个值,表示这是一条个人彩信,还有MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement"广告。 setReadReport设置是否需要送达报告,发送成功会有报告 setTo设置收件人 setTransactionId 默认是通过下面方法生成的,跟当前时间有关的一个唯一的Transaction传输的Id。
private byte[] generateTransactionId() { String transactionId = "T" + Long.toHexString(System.currentTimeMillis()); return transactionId.getBytes(); }
MultimediaMessagePdu.java 多媒体信息Pdu
其有一个PduBody类型的成员变量,它存放着彩信附件的内容 setBody 设置彩信内容 setSubject 设置主题 addTo 设置收件人 setPriority 设置优先级(什么用?),彩信发送设置的是PduHeaders.PRIORITY_NORMAL普通级别 setDate 设置日期GenericPdu.java 彩信的不同操作,例如发送,接收,下载等,不同的操作的信息类都是继承自它。
有一个PduHeaders类型的成员变量,它存放这彩信的头部信息,也就是一些除了彩信内容之外的信息,例如收件人之类 setMessageType 设置彩信类型,发送的类型是PduHeaders.MESSAGE_TYPE_SEND_REQ,其他的可以看PduHeaders定义的其他类型 setMmsVersion 设置彩信版本public static final int MMS_VERSION_1_3 = ((1 << 4) | 3); public static final int MMS_VERSION_1_2 = ((1 << 4) | 2); public static final int MMS_VERSION_1_1 = ((1 << 4) | 1); public static final int MMS_VERSION_1_0 = ((1 << 4) | 0);
// Current version is 1.2.
public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2;默认彩信版本是1_2,不同版本的区别是? setFrom 设置发件人上面的方法,除了setBody之外,其他的都是将信息放入到PduHeaders中,并且每一项都有对应的索引声明在PduHeaders中,例如PduHeaders.FROM。
所以PduHeaders就是有关彩信的头部信息的一个集合类。
mHeaderMap = new HashMap<Integer, Object>();我们可以看到,上面设置的信息都是放在这里面的一个hashMap的。 setOctet(int value, int field) setTextString(byte[] value, int field) setEncodedStringValue(EncodedStringValue value, int field) setEncodedStringValues(EncodedStringValue[] value, int field) setLongInteger(long value, int field 上面的都是很容易从参数就可以了解到,例如setOctet就是存放int,int入mHeaderMap中的。这里需要了解的是EncodedStringValue 。EncodedStringValue.java 所以这个是对一个字符串,进行一定的编码后重新获取到的数据
我们举个例子来说明下。 public void setTo(EncodedStringValue[] value) { mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); } 在Mms中是这样调用的 EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests); if (encodedNumbers != null) { req.setTo(encodedNumbers); }private int mCharacterSet;编码类型,这里默认是CharacterSets.DEFAULT_CHARSET = MIMENAME_UTF_8 = "utf-8";也就是utf-8。
private byte[] mData;经过编码后的数据,String的getBytes(CharacterSets.DEFAULT_CHARSET_NAME) CharacterSets.java 基本都是常量,跟字符串的信息相关,例如编码类型。 PduBody.java 这个跟数据库part表对应 在Mms中,是调用PduBody pb = slideshow.toPduBody(); sendReq.setBody(pb);,所以他是跟Mms的SlideshowModel对应的。private VectormParts = null;private Map mPartMapByContentId = null;private Map mPartMapByContentLocation = null;private Map mPartMapByName = null;private Map mPartMapByFileName = null;
PduPart.java 每一条part记录对应一个PduPart对象
PduPersister.java 这个类就是数据库和应用的一个交互的接口,利用它可以将彩信的数据pdu保存到数据库中,也可以将彩信数据从数据库中读取后转换为pdu,供Mms等使用。 public static PduPersister getPduPersister(Context context) { if ((sPersister == null)) { sPersister = new PduPersister(context); } else if (!context.equals(sPersister.mContext)) { sPersister.release(); sPersister = new PduPersister(context); } return sPersister; }单例模式,但是不是普通的单例模式,每个场景下,不同context下的是不一样的。 updateHeaders(Uri uri, SendReq sendReq) 根据sendReq更新pdu表的字段 move 更新pdu的Mms.MESSAGE_BOX字段,例如发件箱,草稿箱等类型 persist persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled, HashMap<Uri, InputStream> preOpenedFiles) 其实也是更新pdu的字段,但是这里处理的事情更多。具体更新的字段读者自行看代码。 我们前面分析彩信发送流程的时候说过,在createDraftMmsMessage方法执行完后,彩信附件内容就会插入到part表中。 PduBody pb = slideshow.toPduBody(); sendReq.setBody(pb); 然后调用persist 我们针对这部分来分析下代码if (pdu instanceof MultimediaMessagePdu) { body = ((MultimediaMessagePdu) pdu).getBody(); // Start saving parts if necessary. if (body != null) { int partsNum = body.getPartsNum(); if (partsNum > 2) { // For a text-only message there will be two parts: 1-the SMIL, 2-the text. // Down a few lines below we're checking to make sure we've only got SMIL or // text. We also have to check then we don't have more than two parts. // Otherwise, a slideshow with two text slides would be marked as textOnly. textOnly = false;//长短信会转化为彩信,当只有文字,则有两条记录,smile和text,所以大于2肯定是不止文字了。 } for (int i = 0; i < partsNum; i++) { PduPart part = body.getPart(i); messageSize += part.getDataLength(); persistPart(part, dummyId, preOpenedFiles); // If we've got anything besides text/plain or SMIL part, then we've got // an mms message with some other type of attachment. String contentType = getPartContentType(part);//part表ct字段,例如图片有image/jpeg类型,文字为text/plain类型 if (contentType != null && !ContentType.APP_SMIL.equals(contentType) && !ContentType.TEXT_PLAIN.equals(contentType)) { textOnly = false; } } } }updatePart看了之后,就是解析PduPart,更新part表 persistPart看了之后,就是解析PduPart,更新part表
GenericPdu load(Uri uri)
PduPart[] loadParts(long msgId)其他的PduComposer和PduParse后面博客讲,这个都是从数据库加载数据后生成pdu的。
PduBody pb = slideshow.toPduBody(); 这个Mms跟Pdu的转换也在后面的博客进行分析,并且会详细介绍SlideshowModel类型
ComposeMessageActivity.sendMessage
从下图可以看出,在这个方法中,主要做的事是确认手机状态的有效性。最终调用WorkingMessage的send方法进行信息的发送。WorkingMessage.send
从下图可以看出,在本方法中,对于不同类型的消息,分别调用不同的方法对其进行处理。并对彩信进行一些简要的预处理。 短信部分: WorkingMessage.preSendSmsWorker 如下图所示,这是短信部分的发送流程。本方法中所涉及到的东西就比较多了。由于已经转到了新线程中利用WorkingMessage进行消息的发送,那么 原来ComposeMessage中的那个WorkingMessage就可以更新了。然后确认一下所属的会话是否存在,如果不存在就新建一个。 接着,将接收者序列化,调用sendSmsWorker。创建一个SmsMessageSender,将消息存入发送队列中(type字段设为6)。通知SmsReceiver发送。 SmsReceiver则调用SmsReceiverService发送队列中的第一条短信。 在发送之前,还要根据手机卡的不同,将短信内容分成若干块,保证每块的大小不超过该卡的限制。在分段后的最后一条短信的sentIntent中,设置 EXTRA_MESSAGE_SENT_SEND_NEXT为true。这样当短信发送成功后,会调用SmsReceiverService的 handleSmsSent方法,发送队列中的第一条。 然后,对每块都指定一个sentIntent,当发送出去之后,修改其状态。若需要报告,还会指定一个deliveryIntent,用于消息报告的处 理。这两个Intent都会被封装到SmsTracker中,当发送成功后,在SMSDispatcher的handleSendComplete中被取 出。sentIntent会被立即执行,将消息状态转为已发送。而deliveryIntent则会被加入deliveryPendingList,等收 到消息报告后才被执行(具体代码在不同的dispatcher中)。 发送完毕后,还要对短信上限、消息显示列表、草稿进行相应的处理。SmsReceiverService.handleSmsSent 消息发送后的处理
当sentIntent被执行后,会根据不同的结果更新消息的所处信箱。MessageStatusReceiver 消息报告
当需要接收报告时,会在报告收到之后,在SMSDispatcher中根据不同类型手机从deliveryPendingList中取出并执行相应的deliveryIntent。SmsReceiverService.handleSmsReceived 接收短信
当RILReceiver有消息收到时,会从RIL向上传递,经由SMSDispatcher的dispatchPdus方法生成Intent调用PrivilegedSmsReceiver。彩信部分:
WorkingMessage.sendMmsWorker 从下图可以看出,彩信发送的过程和短信过程有些类似。都需要重置WorkingMessage,获取实际ThreadId。发送完都要删除多余的信息,调 用ComposeMessageActivity的onMessageSent。只是彩信没另外创建一个类似preSendSmsWorker的方法,而 是把所有内容都放在sendMmsWorker中处理。同时,删除草稿的位置也有所不同。TransactionService.onStartCommand
彩信的发送与短信不同,是以网络的方式发送的。 每次调用的时候,先取出所有due_time在当前时间之前的待发送的彩信。然后将它的Uri和transactionType封装到 TransactionBundle中,传给ServiceHandler。类型设为EVENT_TRANSACTION_REQUEST。在 ServiceHandler中创建一个SendTransaction对象。然后调用processTransaction方法,根据当前 Transaction是否已在队列中,以及当前的连接状态确定该把这个SendTransaction对象放到哪个队列中(mPending为待发 送,mProcessing为发送中)。同时使用sendMessageDelayed方法发送一个标记为 EVENT_CONTINUE_MMS_CONNECTIVITY的message来保持连接。 接着,将TransactionService放入该Transaction对象的观察者列表,以便于在后面成功发送后,继续发送待发送的彩信。 接下来,使用SendTransaction的Run方法从数据库中获取指定彩信,并构造SendReq,经由HttpUtils发送编码后的彩信。根据发送结果,选择是将错误状态存入数据库,还是将该彩信转到已发送箱并通知TransactionService处理待发送的彩信。TransactionService.update
该方法执行后,先将Transaction从mProcessing列表中移除。若mPending不空,说明有彩信处于已基本处理但未发送状态,故调用 mServiceHandler,设置EVENT_HANDLE_NEXT_PENDING_TRANSACTION进行处理。从mPending队列中 取出第一个,交由processTransaction处理。由于在之前说过,调用processTransaction的Transaction都会被 加入mProcessing队列,而这个Transaction发送成功后,由会再次通知其观察者,进而调用TransactionService的 update方法继续发送mPending队列中的信息。故mPending队列中的彩信会自动按顺序发完。 然后对于成功发送的消息,使用Notification通知用户(包括消息未读,消息报告等)。并发送 android.intent.action.TRANSACTION_COMPLETED_ACTION的广播(目前该广播无人接收,应该是为了支持应 用开发人员而提供的一种广播)。PushReceiver
android的彩信接收应用层部分从PushReceiver开始。当onReceive被调用后,让屏幕亮5秒,然后创建一个 ReceivePushTask并使用它的execute方法。ReceivePushTask是一个AsyncTask,实现了 doInBackground方法。当传入intent后,会在doInBackground中将其中的数据转成GenericPdu,并根据其消息类型 做出不同的操作。 如果是发送报告或已读报告,将其存入数据库。 如果是彩信通知,若已存在,则不处理。否则将其存入数据库。启动TransactionService进行处理。 TransactionService中的处理主要是调用mServiceHandler,大体过程与发送彩信时相同,只是此处创建的是NotificationTransaction。 如果不支持自动下载或数据传输没打开,仅通知mmsc。否则,下载相应彩信,删除彩信通知,通知mmsc,删除超过容量限制的彩信,通知TransactionService处理其余待发送的彩信。转载地址:https://liwangjiang.blog.csdn.net/article/details/88911891 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!