Android开发中使用mms模块收发单卡和双卡短信的教程

一.信息发送:
com.android.mms.data.WorkingMessage.java 类
send()函数:

public void send() {   

  ......  

  if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {   

    // 彩信   

    slideshow.prepareForSend(); 

    new Thread(new Runnable() {   

      public void run() {   

        sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq);   

      }   

    }).start();   

  } else {   

    // 短信   

    new Thread(new Runnable() {   

      public void run() {   

        preSendSmsWorker(conv, msgText);   

      }   

    }).start();   

  ...... 

}

prapareForSend(). 先确保有slidshow,也就是实质内容。 确保文字已拷贝。确保标题。 根据消息分类,如果是短信直接起一个线程,跑preSendSmsWorker函数,发送短信;如果是彩信,先跑prapareForSave确保文本信息,然后起一个线程,单独跑sendMmsWorker函数。不管是短信还是彩信,起了那个worker函数之一就算发送信息成功了。 最后修改Recipient cache, 重置标志位,过程就结束了。
     短信发送先调用preSendSmsWorker函数,在preSendSmsWorker函数中又起了sendSmsWorker函数。

private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) { 

......  

MessageSender sender = new SmsMessageSender(mContext, dests, msgText, threadId); 

sender.sendMessage(threadId); 

......    

}

SmsMessageSender.java类,在mms/transaction下面,实现了MessageSender接口,这个接口sendMessage并返回boolean的值。若发送的是mms,返回true。若发送的是sms,返回false。

当然,对于单卡手机和双卡双待手机的短信发送流程是有区别的(短信接收的流程是相同的,相对流程也比较简洁),关于具体的流程还是直接用UML图来说明更为直接:
信息发送与接收时序图:
发送短信
   单卡手机短信发送的时序图如图所示:

双卡手机短信发送的时序图则如下图所示:

二.短信的接收
    信息的接收工作是由底层来完成的,当有一个 新的信息时底层完成接收后会以Intent的方式来通知上层应用,信息的相关内容也包含在Intent当中,Android所支持的信息Intent都定义在android.provider.Telephony.Intents里面。
   
     短信接收,对于上层应用程序来讲就是要处理广播事件SMS_RECEIVED_ACTION,它是由Frameworks发出告诉上层有新的SMS已收到。在Mms中,是由PrivilegedSmsReceiver来处理,它收到SMS_RECEIVED_ACTION(android.provider.Telephony.Intents.SMS_RECEIVED_ACTION=”android.provider.Telephony.SMS_RECEIVED”)后会启动SmsReceiverService来做具体的处理。
SmsReceiverService会先检查短信的类型,如果是Class0短信,直接在GUI中显示,不做任何其他的处理,也即不会存储到数据库中,也不会在Notification Bar中做Notification。
     对于其他短信,会进行替换现有的消息,或是当作新消息插入。原则就是如果在数据库中已有的短信中,与新来的短信的原始地址和协议标识都一样,那么就把其替换成新进的短信,否则就当作新短信插入。
具体的替换流程:先用新进的短信生成一个ContentValues,再用短信的地址和协议标识当作条件到数据库中去查询,如果查到了,就替换,否则就存储。
存储的流程,也是先生成一个CotentValues,然后取出短信的Thread Id和地址,地址要与联系人数据库同步一下,以保证是能识别的地址。如果Thread Id不是合法的,那么就用同步过的地址尝试重新生成Thread Id,尝试5次。然后把刷新过的Thread Id放到ContentValues中,把ContentValues插入到数据库中。如果设置为把信息存储到SIM卡,还要调用SmsManager把信息拷贝到SIM卡上。计算短信的大小,并更新至数据库。删除过期的短信,和超过数量限制的短信,然后返回插入后得到的短信Uri。
最后,对于替换或插入的短信,用Uri去StatusBar做Notification。
GUI在刷新列表时也能得到新短信,因为短信已经被存储到数据库中。

短信接收的时序图如图所示:

三、双卡双待手机解析短信异常分析及解决
由于是双SIM卡,而且两个卡槽支持的运营商或者网络制式不一定相同,比如一个卡槽支持WCDMA,另一个却只支持GSM ,导致用正常方法解析短信很容易遇到异常。
这里先看下解决方案,这里需要以反射的方式解析不同类型的短信,并且对于不同机型,需对应地进行调整适配:

获取短信信息,注意:为解决双卡双待手机解析短信异常问题,使用Java反射机制,优先解析GSM类型的短信,假如解析失败才按CDMA类型的短信进行解析)

public static SmsMessage[] getSmsMessage(Intent intent) {
    SmsMessage[] msgs = null;
    Object messages[] = (Object[]) intent.getSerializableExtra("pdus");
    int len = 0;
    if (null != messages && (len = messages.length) > 0) {
      msgs = new SmsMessage[len];
      try {
        for (int i = 0; i < len; i++) {
          SmsMessage message = null;
          if ("GSM".equals(intent.getStringExtra("from"))) { // 适配MOTO XT800双卡双待手机
            message = createFromPduGsm((byte[]) messages[i]);
          } else if ("CDMA".equals(intent.getStringExtra("from"))) { // 适配MOTO XT800双卡双待手机
            message = createFromPduCdma((byte[]) messages[i]);
          } else {
            message = SmsMessage.createFromPdu((byte[]) messages[i]); // 系统默认的解析短信方式
          }
          if (null == message) { // 解决双卡双待类型手机解析短信异常问题
            message = createFromPduGsm((byte[]) messages[i]);
            if (null == message) {
              message = createFromPduCdma((byte[]) messages[i]);
            }
          }
          if (null != message) {
            msgs[i] = message;
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
        msgs = getSmsMessageByReflect(intent); // 解决双卡双待手机解析短信异常问题
      } catch (Error er) {
        er.printStackTrace();
        msgs = getSmsMessageByReflect(intent); // 解决双卡双待手机解析短信异常问题
      }
    }
    return msgs;
  }

反射方式获取短信

/**
   * 使用Java反射机制获取短信信息(解决双卡双待手机解析短信异常问题,优先解析GSM类型的短信,假如解析失败才按CDMA类型的短信进行解析)
   *
   * @param intent
   * @return
   */
  private static SmsMessage[] getSmsMessageByReflect(Intent intent) {
    SmsMessage[] msgs = null;
    Object messages[] = (Object[]) intent.getSerializableExtra("pdus");
    int len = 0;
    if (null != messages && (len = messages.length) > 0) {
      msgs = new SmsMessage[len];
      try {
        for (int i = 0; i < len; i++) {
          SmsMessage message = createFromPduGsm((byte[]) messages[i]);
          if (null == message) {
            message = createFromPduCdma((byte[]) messages[i]);
          }
          if (null != message) {
            msgs[i] = message;
          }
        }
      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      } catch (InstantiationException e) {
        e.printStackTrace();
      }
    }
    return msgs;
  }

通过Java反射机制解析GSM类型的短信:

private static SmsMessage createFromPduGsm(byte[] pdu) throws SecurityException, IllegalArgumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    return createFromPdu(pdu, "com.android.internal.telephony.gsm.SmsMessage");
  }

解析CDMA类型的短信

private static SmsMessage createFromPduCdma(byte[] pdu) throws SecurityException, IllegalArgumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
  return createFromPdu(pdu, "com.android.internal.telephony.cdma.SmsMessage");
}

解析GSM或者CDMA类型的短信

private static SmsMessage createFromPdu(byte[] pdu, String className) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class<?> clazz = Class.forName(className);
    Object object = clazz.getMethod("createFromPdu", byte[].class).invoke(clazz.newInstance(), pdu);
    if (null != object) {
      Constructor<?> constructor = SmsMessage.class.getDeclaredConstructor(Class.forName("com.android.internal.telephony.SmsMessageBase"));
      constructor.setAccessible(true);
      return (SmsMessage) constructor.newInstance(object);
    } else {
      return null;
    }
  }
(0)

相关推荐

  • Android Mms之:PDU的使用详解

    Android当中的Mms对MMS(Multimedia Messaging Service)的操作关乎MMS协议部分都是通过Frameworks中提供的API来完成的:com.google.android.mms这个包在SDK中是不开放的,只能供内部程序使用,它封装了所有MMS所需要的API.这个包就是Android中对MMS协议的实现,包括一些数据结构:GenericPdu,MultimediaMessagePdu,SendReq,SendConf,NotificationInd,Retri

  • Android Mms之:联系人管理的应用分析

    Contact联系人对Mms来说是十分重要的,因为每一个对话的收信人都是一个联系人,新建信息时可以输入联系人的任何信息,比如号码或名字,Mms都可以把信息发给相应的人.Mms中的类Contact就是用来代表一个联系人,它含有联系人的信息,如名字,号码,联系人Id,是否存在于联系人数据库等等,同时Contact也提供了一些方法来获取Contact对象,Contact.get()方法来获取Contact对象.Contact会保持数据与联系人数据库的同步,有一个接口updateContact()用于通

  • Android Mms之:草稿管理的应用

    当编辑完一条信息后,如果在没有发送的情况下退出编辑页面,那么信息会自动保存为草稿.也就是在ComposeMessageActivity的onStop()时,如果还没有发送,那么就会调用WorkingMessage.saveDraft()来把信息保存为草稿.期间也会检查一些条件,比如消息是否已被标识为放弃,或是是否为空(isWorthSaving),如果一切正常会saveDraft()并会用Toast来告知信息已保存为草稿.草稿的保存也是针对不同的信息而不同,短信和彩信的流程有所不同.保存短信为草

  • Android编程之非调用系统界面实现发送彩信的方法(MMS)

    本文实例讲述了Android非调用系统界面实现发送彩信的方法.分享给大家供大家参考,具体如下: 一.问题: 最近有个需求,不去调用系统界面发送彩信功能.做过发送短信功能的同学可能第一反应是这样: 不使用 StartActivity,像发短信那样,调用一个类似于发短信的方法 SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(phoneCode, null, text, null, null); 二

  • Android Mms之:深入MMS支持

    Composing and editingMMS在Android Mms应用里面的具体实现形式,或数据结构是SlideshowModel,它是一个每个节点为SlideModel的ArrayList,SlideModel是一个Model的List,也就是它可以接收任何Model的子类,Audio,Video,Image和Text都可以放到SlideModel上面.SlideModel主要用于管理其上面的各个媒体,比如它们的布局,它们的播放控制,而SlideshowModel主要用于管理所有的附件,

  • Android Mms之:对话与联系人关联的总结详解

    在Mms中每个Thread都有其相应的联系人,但是threads表中并没有直接保存联系人的信息(号码或名字),而是保存一个叫做recipient_id的东西,也还有一个类叫做data/RecipientIdCache.java专门管理它. 在数据库中专门有一个表来保存它canonical_addresses.它的目的就是为了能够快速的找到某一对话的联系人的信息.对话的表threads里面并没有保存其联系人的直接信息,而是有一列叫做recipient_ids的整数来代表收信人.而在数据库还有另外一

  • Android Mms之:短信发送流程(图文详解)

    信息的发送,对于Mms应用程序来讲主要就是在信息数据库中创建并维护一条信息记录,真正的发送过程交由底层(Frameworks层)函数来处理. 总体的来讲,当信息创建完成后,对于信息通常有三个去处,一个是放弃这个信息,也就是用户不想要此信息,一旦选择,信息将不会被保存:第二个去处就是保存为草稿:最后一个去处就是发送此信息. 当点击了发送后,UI层暂不会有变化,UI层要监听负责发送的各个类的回调信息和数据库的变化信息来更新UI.信息发送的第一站是WorkingMessage,它会先处理一下信息的相关

  • Android Mms之:深入理解对话列表管理

    Conversation这个类主要表示对话的数据结构,其内持有一个Thread所有的相关信息,如Recipients,ThreadId等等,也用于管理Thread,比如查询Thread,删除Thread,更新Thread,管理ThreadId,管理Thread的联系人等等.因为Thread表与其他表关联的信息比较多,如联系人,消息个数,这些信息有些直接会写在数据库.因为Thread上面的信息比较多,所以加载时会较费时,因此就有了一个在Conversation内部的Cache来提高加载Thread

  • Android Mms之:深入理解Compose

    Mms中的ComposeMessageActivity(以下简称Composer)是整个Mms中最重要的一个组件,它负责编辑信息,发送信息,管理信息,接收信息,与外部应用接口.在Mms内部与Composer关联的类和组件特别多,几乎所有的类和组件都与Composer有关联,关于信息的所有操作流程都起始于Composer:在外部Composer也是公开的接口,能够处理Intent.ACTION_SEND和Intent.ACTION_SENDTO以及文件类型为audio/*,image/*,vide

  • Android Mms之:接收信息流程(图文详解)

    信息的接收工作是由底层来完成的,当有一个 新的信息时底层完成接收后会以Intent的方式来通知上层应用,信息的相关内容也包含在Intent当中,Android所支持的信息Intent都定义在android.provider.Telephony.Intents里面.短信的接收 短信接收,对于上层应用程序来讲就是要处理广播事件SMS_RECEIVED_ACTION,它是由Frameworks发出告诉上层有新的SMS已收到.在Mms中,是由PrivilegedSmsReceiver来处理,它收到SMS

  • 简单掌握Android开发中彩信的发送接收及其附件的处理

    一. 彩信发送:   彩信比短信麻烦很多.从sendMmsWorker函数的参数就可以看出来:(conv, mmsUri, persister, slideshow, sendReq) 上下文,uri,PduPersister(彩信是用pdu的),slideshow 包含了所有的彩信信息,sendreq包含了mime封装mms时的headers,也包括了from,to等信息 . (1)函数 sendMmsWorker: private void sendMmsWorker(Conversatio

随机推荐