android短信底层----彩信发送流程
发布日期:2021-06-30 21:23:09
浏览次数:2
分类:技术文章
本文共 13201 字,大约阅读时间需要 44 分钟。
ComposeMessageActivity.java
sendMessageWorkingMessage.java
sendprivate void prepareForSave(boolean notify) { // Make sure our working set of recipients is resolved // to first-class Contact objects before we save. syncWorkingRecipients(); if (hasMmsContentToSave()) { ensureSlideshow(); syncTextToSlideshow(); } }因为private SlideshowModel mSlideshow;这个是跟数据库中part表数据对应的,所以现在要发送彩信,如果有内容,则要将其都添加到mSlideshow中。
if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
注意,例如你现在添加了一张图片,则这个时候其不会马上存入数据库中,但是会存入到mSlideshow中,当短信发送或者保存为草稿的时候才写入到数据库中的。
final PduPersister persister = PduPersister.getPduPersister(mActivity);彩信是依靠PduPersister来将彩信数据写入到数据库的。
final SlideshowModel slideshow = mSlideshow;final CharSequence subject = mSubject;final boolean textOnly = mAttachmentType == TEXT;new Thread(new Runnable() { @Override public void run() { final SendReq sendReq = makeSendReq(conv, subject); // Make sure the text in slide 0 is no longer holding onto a reference to // the text in the message text box. slideshow.prepareForSend(); sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly); updateSendStats(conv); }}, "WorkingMessage.send MMS").start();同样开启一个子线程来进行发送彩信private static SendReq makeSendReq(Conversation conv, CharSequence subject) { String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */); SendReq req = new SendReq(); EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests); if (encodedNumbers != null) { req.setTo(encodedNumbers);//将收件人写入 } if (!TextUtils.isEmpty(subject)) { req.setSubject(new EncodedStringValue(subject.toString()));//将主题写入 } req.setDate(System.currentTimeMillis() / 1000L);//将当前时间写入 return req;}void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister, SlideshowModel slideshow, SendReq sendReq, boolean textOnly)DraftCache.getInstance().setSavingDraft(true);设置保存草稿,好像是进行互斥,所以彩信是先要保存草稿,然后才进行发送mStatusListener.onPreMessageSent();//彩信发送前更新短信UIif (newMessage) {//此时为true // Write something in the database so the new message will appear as sending ContentValues values = new ContentValues(); values.put(Mms.MESSAGE_BOX, Mms.MESSAGE_BOX_OUTBOX);//此时彩信类型为发件箱 values.put(Mms.THREAD_ID, threadId); values.put(Mms.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);//短信类型为请求发送 if (textOnly) { values.put(Mms.TEXT_ONLY, 1); } if ((TelephonyManager.getDefault().getPhoneCount()) > 1) { values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId); } else { values.put(Mms.SUBSCRIPTION_ID, SubscriptionManager.getDefaultDataSubscriptionId()); } mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI, values);//插入到发件箱中 }同样,彩信,pdu表中也有一个字段是表示当前彩信的各种状态的,就是msg_box字段,这里是发件箱,其类型type是PduHeaders.MESSAGE_TYPE_SEND_REQ,往数据库pdu的发件箱写入数据
mStatusListener.onMessageSent();//短信发送后更新UI,此时显示正在发送中...
然后查询所有处于发件箱的彩信,计算他们的附件总大小,看是否超出了限制(为什么这里有这个限制?)
if (newMessage) { // Create a new MMS message if one hasn't been made yet. mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri, mActivity, null);//将mSlideshow的数据写入到part表中 } else { // Otherwise, sync the MMS message in progress to disk. updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);//更新草稿 }ContentValues values = new ContentValues(1); if ((TelephonyManager.getDefault().getPhoneCount()) > 1) { values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId); } else { values.put(Mms.SUBSCRIPTION_ID, SubscriptionManager.getDefaultDataSubscriptionId()); } SqliteWrapper.update(mActivity, mContentResolver, mmsUri, values, null, null);更新subIdMessageSender sender = new MmsMessageSender(mActivity, mmsUri, slideshow.getCurrentMessageSize(), mCurrentConvSubId);调用MmsMessageSender的sendMessage来发送彩信
MmsMessageSender.java
sendMessage 首先是继续更新数据库的一些数据PduPersister p = PduPersister.getPduPersister(mContext); GenericPdu pdu = p.load(mMessageUri);//从数据库中加载URI为mMessageUri到内存中,所以对于彩信,与数据库交互我
们是直接用google提供的pdu,非常简单,这个另外一个专题讲。
SendReq sendReq = (SendReq) pdu; 发送请求的数据类型if (!mMessageUri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) { // If the message is already in the outbox (most likely because we created a "primed" // message in the outbox when the user hit send), then we have to manually put an // entry in the pending_msgs table which is where TransacationService looks for // messages to send. Normally, the entry in pending_msgs is created by the trigger: // insert_mms_pending_on_update, when a message is moved from drafts to the outbox. ContentValues values = new ContentValues(7); values.put(PendingMessages.PROTO_TYPE, MmsSms.MMS_PROTO); values.put(PendingMessages.MSG_ID, messageId); values.put(PendingMessages.MSG_TYPE, pdu.getMessageType()); values.put(PendingMessages.ERROR_TYPE, 0); values.put(PendingMessages.ERROR_CODE, 0); values.put(PendingMessages.RETRY_INDEX, 0); values.put(PendingMessages.DUE_TIME, 0); SqliteWrapper.insert(mContext, mContext.getContentResolver(), PendingMessages.CONTENT_URI, values); } else { p.move(mMessageUri, Mms.Outbox.CONTENT_URI); }我们已经是处于发件箱中,所以走第一个分支,即在表pending_msgs中插入一条数据,表示是待发送彩信
Intent intent = new Intent(mContext, TransactionService.class); intent.putExtra(Mms.SUBSCRIPTION_ID, mSubId); mContext.startService(intent);启动TransactionService服务来发送彩信。
TransactionService.java
onStartCommand 第一次是EVENT_NEW_INTENT消息private ServiceHandler mServiceHandler;handleMessageonNewIntent((Intent)msg.obj, msg.arg1);mConnMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);发送彩信需要先检测ConnectivityManager服务,这个不是数据连接服务 我们假设发送环境OK,可以发送彩信。
Cursor cursor = PduPersister.getPduPersister(this).getPendingMessages( System.currentTimeMillis());加载所有的pending短信
int columnIndexOfRetryIndex = cursor.getColumnIndexOrThrow( PendingMessages.RETRY_INDEX);这个是重试次数
transactionType是 Transaction.SEND_TRANSACTION;TransactionBundle args = new TransactionBundle(transactionType, uri.toString(), subId);此时uri是pdu的uri
launchTransaction(serviceId, args, false);Message msg = mServiceHandler.obtainMessage(EVENT_TRANSACTION_REQUEST);case Transaction.SEND_TRANSACTION: transaction = new SendTransaction( TransactionService.this, serviceId, transactionSettings, args.getUri()); break;if (!processTransaction(transaction)) { transaction = null; return; }mPending和mProcessing此时是没有的
beginMmsConnectivity(subId);开始mms的数据连接
if (!mIsAvailable[phoneId]) { mPending.add(transaction); LogTag.debugD("processTransaction: connResult=APN_REQUEST_STARTED, " + "defer transaction pending MMS connectivity"); return true; }如果当前sim卡不可用,就会将这条发送记录添加到mPending中if (mProcessing.size() > 0) { LogTag.debugD("Adding transaction to 'mPending' list: " + transaction); mPending.add(transaction); return true; } else { LogTag.debugD("Adding transaction to 'mProcessing' list: " + transaction); mProcessing.add(transaction); }我们现在是走else,所以直接处理。所以这两个的关系是,mPending是将要处理,而mProcessing是正在处理的。transaction.attach(TransactionService.this); transaction.process();process走的是SendTransaction的processpublic void process() { mThread = new Thread(this, "SendTransaction"); mThread.start(); }所以启动子线程发送彩信,然后返回true,我们继续看主线程。哦,直接return。
SendTransactionrunRateController rateCtlr = RateController.getInstance();PduPersister persister = PduPersister.getPduPersister(mContext); SendReq sendReq = (SendReq) persister.load(mSendReqURI);从数据库加载要发送的彩信 重新设置时间,这才是要发送的时间,然后更新数据库
byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey), new PduComposer(mContext, sendReq).make());
Transaction.java
/** * A common method to send a PDU to MMSC. * * @param token The token to identify the sending progress. * @param pdu A byte array which contains the data of the PDU. * @return A byte array which contains the response data. * If an HTTP error code is returned, an IOException will be thrown. * @throws IOException if any error occurred on network interface or * an HTTP error code(>=400) returned from the server. * @throws MmsException if pdu is null. */根据注释,就是利用这个发送彩信的,pdu是发送的彩信内容,token是当前发送的一个唯一标识,而返回值则是发送结果protected byte[] sendPdu(long token, byte[] pdu) throws IOException, MmsException { return sendPdu(token, pdu, mTransactionSettings.getMmscUrl()); }protected byte[] sendPdu(long token, byte[] pdu, String mmscUrl) throws IOException, MmsException { if (pdu == null) { throw new MmsException(); } return HttpUtils.httpConnection( mContext, token, mmscUrl, pdu, HttpUtils.HTTP_POST_METHOD, mTransactionSettings.isProxySet(), mTransactionSettings.getProxyAddress(), mTransactionSettings.getProxyPort()); }AndroidHttpClient往下看是利用AndroidHttpClient来发送。这个我们后面再看。
String respStr = new String(response);我这里是发送失败,看起来是乱码
SendConf conf = (SendConf) new PduParser(response, PduParserUtil.shouldParseContentDisposition()).parse();int respStatus = conf.getResponseStatus();//发送失败这里是130,public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82; values.put(Mms.RESPONSE_STATUS, respStatus);if (respStatus != PduHeaders.RESPONSE_STATUS_OK) {//不是发送成功就更新状态 SqliteWrapper.update(mContext, mContext.getContentResolver(), mSendReqURI, values, null, null); Log.e(TAG, "Server returned an error code: " + respStatus); return; }if (mTransactionState.getState() != TransactionState.SUCCESS) { mTransactionState.setState(TransactionState.FAILED); mTransactionState.setContentUri(mSendReqURI); Log.e(TAG, "Delivery failed."); }String messageId = PduPersister.toIsoString(conf.getMessageId()); values.put(Mms.MESSAGE_ID, messageId); SqliteWrapper.update(mContext, mContext.getContentResolver(), mSendReqURI, values, null, null); // Move M-Send.req from Outbox into Sent. Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI);//发送成功应该会将pending里面的条目删除了,应该在数据库中自动操作的。发送成功会将彩信状态从待发送转为已经发送
notifyObservers();//这里会对监听者进行回调,TransactionService的update会被回调。这里应该是RetryScheduler的update被调用。都有。Transaction extends Observable提供了attach
mProcessing.remove(transaction);else if (mProcessing.isEmpty()) { LogTag.debugD("update: endMmsConnectivity"); endMmsConnectivity(transaction.getSubId()); }关闭mms的数据连接Intent intent = new Intent(TRANSACTION_COMPLETED_ACTION); TransactionState state = transaction.getState(); int result = state.getState(); intent.putExtra(STATE, result);case TransactionState.FAILED:boolean failSetupDataCall = Transaction.FAIL_REASON_CAN_NOT_SETUP_DATA_CALL == transaction.getFailReason();这个是? isLastRetryscheme.getRetryLimit()这里有5次if (type == Transaction.SEND_TRANSACTION) { if (failSetupDataCall) { mToastHandler.sendEmptyMessage( TOAST_SETUP_DATA_CALL_FAILED_FOR_SEND);"无网络,发送失败,稍后自动重新发送。" } else { mToastHandler.sendEmptyMessage(TOAST_SEND_FAILED_RETRY);"发送失败,稍后自动重新发送。" } }sendBroadcast(intent);finally { transaction.detach(this); stopSelfIfIdle(serviceId); }
TRANSACTION_COMPLETED_ACTION 没有发现哪个广播会接收。
所以到这里结束了。UI会根据数据库的改变而刷新界面。
彩信的重发机制
跟DefaultRetryScheme这个有关- 0
- 60000
- 300000
- 600000
- 1800000
转载地址:https://liwangjiang.blog.csdn.net/article/details/86553428 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月20日 07时16分04秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
攻防世界web进阶区easytornado详解
2019-04-30
攻防世界web进阶区web2详解
2019-04-30
xss-labs详解(上)1-10
2019-04-30
xss-labs详解(下)11-20
2019-04-30
攻防世界web进阶区ics-05详解
2019-04-30
攻防世界web进阶区FlatScience详解
2019-04-30
攻防世界web进阶区ics-04详解
2019-04-30
攻防世界web进阶区Cat详解
2019-04-30
攻防世界web进阶区bug详解
2019-04-30
攻防世界web进阶区ics-07详解
2019-04-30
攻防世界web进阶区unfinish详解
2019-04-30
攻防世界web进阶区i-got-id-200超详解
2019-04-30
sql注入总结学习
2019-04-30
leetcode46 全排列
2019-04-30
leetcode121 买卖股票的最佳时机
2019-04-30
leetcode 122 买卖股票的最佳时机II
2019-04-30
leetcode 309 最佳买卖股票含冷冻期
2019-04-30
leetcode 714 买卖股票的最佳时机含手续费
2019-04-30
leetcode3 无重复字符的最长子串
2019-04-30
leetcode 76 最小覆盖子串
2019-04-30