在Android中发送和接收SMS和MMS(Kit Kat之前的Android 4.4)


131

我已经弄清楚了如何发送和接收短信。要发送SMS消息,我必须调用类的sendTextMessage()sendMultipartTextMessage()方法SmsManager。要接收SMS消息,我必须在AndroidMainfest.xml文件中注册一个接收器。然后我不得不重写的onReceive()方法BroadcastReceiver。我在下面提供了示例。

MainActivity.java

public class MainActivity extends Activity {
    private static String SENT = "SMS_SENT";
    private static String DELIVERED = "SMS_DELIVERED";
    private static int MAX_SMS_MESSAGE_LENGTH = 160;

    // ---sends an SMS message to another device---
    public static void sendSMS(String phoneNumber, String message) {

        PendingIntent piSent = PendingIntent.getBroadcast(mContext, 0, new Intent(SENT), 0);
        PendingIntent piDelivered = PendingIntent.getBroadcast(mContext, 0,new Intent(DELIVERED), 0);
        SmsManager smsManager = SmsManager.getDefault();

        int length = message.length();          
        if(length > MAX_SMS_MESSAGE_LENGTH) {
            ArrayList<String> messagelist = smsManager.divideMessage(message);          
            smsManager.sendMultipartTextMessage(phoneNumber, null, messagelist, null, null);
        }
        else
            smsManager.sendTextMessage(phoneNumber, null, message, piSent, piDelivered);
        }
    }

    //More methods of MainActivity ...
}

SMSReceiver.java

public class SMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
    private Context mContext;
    private Intent mIntent;

    // Retrieve SMS
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        mIntent = intent;

        String action = intent.getAction();

        if(action.equals(ACTION_SMS_RECEIVED)){

            String address, str = "";
            int contactId = -1;

            SmsMessage[] msgs = getMessagesFromIntent(mIntent);
            if (msgs != null) {
                for (int i = 0; i < msgs.length; i++) {
                    address = msgs[i].getOriginatingAddress();
                    contactId = ContactsUtils.getContactId(mContext, address, "address");
                    str += msgs[i].getMessageBody().toString();
                    str += "\n";
                }
            }   

            if(contactId != -1){
                showNotification(contactId, str);
            }

            // ---send a broadcast intent to update the SMS received in the
            // activity---
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("SMS_RECEIVED_ACTION");
            broadcastIntent.putExtra("sms", str);
            context.sendBroadcast(broadcastIntent);
        }

    }

    public static SmsMessage[] getMessagesFromIntent(Intent intent) {
        Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
        byte[][] pduObjs = new byte[messages.length][];

        for (int i = 0; i < messages.length; i++) {
            pduObjs[i] = (byte[]) messages[i];
        }
        byte[][] pdus = new byte[pduObjs.length][];
        int pduCount = pdus.length;
        SmsMessage[] msgs = new SmsMessage[pduCount];
        for (int i = 0; i < pduCount; i++) {
            pdus[i] = pduObjs[i];
            msgs[i] = SmsMessage.createFromPdu(pdus[i]);
        }
        return msgs;
    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_MMS" />
    <uses-permission android:name="android.permission.WRITE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher_icon"
        android:label="@string/app_name" >

        <activity
            //Main activity...
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            //Activity 2 ...
        </activity>
        //More acitivies ...

        // SMS Receiver
        <receiver android:name="com.myexample.receivers.SMSReceiver" >
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

    </application>
</manifest>

但是,我想知道您是否可以类似的方式发送和接收MMS消息。在进行了一些研究之后,博客上提供的许多示例仅将传递Intent给本地Messaging应用程序。我正在尝试发送彩信而不离开我的申请。似乎没有发送和接收MMS的标准方法。有没有人得到这个工作?

另外,我知道SMS / MMS ContentProvider并不是官方Android SDK的一部分,但我认为有人可能已经能够实现此功能。任何帮助是极大的赞赏。

更新资料

我已BroadcastReceiverAndroidManifest.xml文件中添加,以接收彩信

<receiver android:name="com.sendit.receivers.MMSReceiver" >
    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />

        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>
</receiver>

在MMSReceiver类中,该onReceive()方法只能获取从其发送消息的phoneNumber。您如何从MMS抓取其他重要内容,例如媒体附件的文件路径(图像/音频/视频)或MMS中的文本?

MMSReceiver.java

public class MMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_MMS_RECEIVED = "android.provider.Telephony.WAP_PUSH_RECEIVED";
    private static final String MMS_DATA_TYPE = "application/vnd.wap.mms-message";

     // Retrieve MMS
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        String type = intent.getType();

        if(action.equals(ACTION_MMS_RECEIVED) && type.equals(MMS_DATA_TYPE)){

            Bundle bundle = intent.getExtras();

            Log.d(DEBUG_TAG, "bundle " + bundle);
            SmsMessage[] msgs = null;
            String str = "";
            int contactId = -1;
            String address;

            if (bundle != null) {

                byte[] buffer = bundle.getByteArray("data");
                Log.d(DEBUG_TAG, "buffer " + buffer);
                String incomingNumber = new String(buffer);
                int indx = incomingNumber.indexOf("/TYPE");
                if(indx>0 && (indx-15)>0){
                    int newIndx = indx - 15;
                    incomingNumber = incomingNumber.substring(newIndx, indx);
                    indx = incomingNumber.indexOf("+");
                    if(indx>0){
                        incomingNumber = incomingNumber.substring(indx);
                        Log.d(DEBUG_TAG, "Mobile Number: " + incomingNumber);
                    }
                }

                int transactionId = bundle.getInt("transactionId");
                Log.d(DEBUG_TAG, "transactionId " + transactionId);

                int pduType = bundle.getInt("pduType");
                Log.d(DEBUG_TAG, "pduType " + pduType);

                byte[] buffer2 = bundle.getByteArray("header");      
                String header = new String(buffer2);
                Log.d(DEBUG_TAG, "header " + header);

                if(contactId != -1){
                    showNotification(contactId, str);
                }

                // ---send a broadcast intent to update the MMS received in the
                // activity---
                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction("MMS_RECEIVED_ACTION");
                broadcastIntent.putExtra("mms", str);
                context.sendBroadcast(broadcastIntent);

            }
        }

    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

根据android.provider.Telephony文档

广播操作:设备已接收到新的基于文本的SMS消息。该意图将具有以下额外值:

pdus-的Object[]byte[]其中包含构成消息的PDU。

可以使用以下方法提取额外的值:getMessagesFromIntent(android.content.Intent) 如果BroadcastReceiver在处理此意图时遇到错误,则应适当设置结果代码。

 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
 public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";

广播操作:设备已接收到新的基于数据的SMS消息。该意图将具有以下额外值:

pdus-的Object[]byte[]其中包含构成消息的PDU。

可以使用getMessagesFromIntent(android.content.Intent)提取额外的值。如果BroadcastReceiver在处理此意图时遇到错误,则应适当设置结果代码。

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";

广播操作:设备已收到新的WAP PUSH消息。该意图将具有以下额外值:

transactionId (Integer) -WAP交易ID

pduType (Integer) -WAP PDU类型`

header (byte[]) -邮件标题

data (byte[]) -消息的数据有效载荷

contentTypeParameters (HashMap<String,String>) -与内容类型关联的任何参数(从WSP Content-Type标头解码)

如果BroadcastReceiver在处理此意图时遇到错误,则应适当设置结果代码。contentTypeParameters额外值是由内容参数的名称作为键的内容映射。如果遇到任何未分配的已知参数,则映射的键将为“未分配/ 0x ...”,其中“ ...”是未分配参数的十六进制值。如果参数没有值,则映射中的值将为null。

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";

更新#2

我已经弄清楚了如何通过a PendingIntent接收附加内容BroadcastReceiverAndroid PendingIntent附加内容,而不是BroadcastReceiver接收的附加内容

但是,多余的将传递给SendBroadcastReceiver而不是SMSReceiver。如何将多余的传递给SMSReceiver

更新#3

接收彩信

因此,在进行了更多研究之后,我发现了一些注册域名的建议ContentObserver。这样,您可以检测到content://mms-sms/conversations内容提供程序何时有任何更改,从而使您可以检测传入的MMS。这是我发现的最有效的示例:接收彩信

但是,有一个mainActivitytype 变量ServiceControllerServiceController该类在哪里实现?注册后还有其他实现ContentObserver吗?

发送彩信

关于发送彩信,我遇到了以下示例:发送彩信

问题是我尝试在Android v4.2.2上的Nexus 4上运行以下代码,但收到此错误:

java.lang.SecurityException: No permission to write APN settings: Neither user 10099 nor current process has android.permission.WRITE_APN_SETTINGS.

在类CarriersgetMMSApns()方法中查询ContentProvider 后,将引发错误APNHelper

final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), null, null, null, null);

显然您无法在Android 4.2中阅读APN

对于所有使用移动数据执行操作(例如发送MMS)并且不知道设备中存在默认APN设置的应用程序,有什么选择?

更新#4

发送彩信

我试过以下示例: 发送彩信

正如@Sam在回答中所建议的:

You have to add jsoup to the build path, the jar to the build path and import com.droidprism.*; To do that in android, add the jars to the libs directory first, then configure the project build path to use the jars already in the libs directory, then on the build path config click order and export and check the boxes of the jars and move jsoup and droidprism jar to the top of the build order.

因此,现在我不再遇到SecurityException错误。我现在正在Android KitKat的Nexus 5上进行测试。运行示例代码后,在调用之后,我会收到200响应代码

MMResponse mmResponse = sender.send(out, isProxySet, MMSProxy, MMSPort);

但是,我与尝试发送MMS的人进行了核对。他们说他们从未收到过彩信。



3
是的,我有。我尝试将Maxim的答案拼凑在一起,但无法使其正常工作。那里有很多导入android.provider.telephony的类,这似乎已被弃用。
toobsco42

大概,在阅读@Sahil的答案后,您还尝试过以下操作:stackoverflow.com/questions/2972845/…–
HaemEternal

我不确定如何将答案拼凑在一起,尽管它看起来与@Sahil的答案非常相似。
toobsco42

嗨,@ toobsco42,您能够找到上面提到的所有查询的解决方案。
kamal_tech_view 2013年

Answers:


15

我遇到了与您上述完全相同的问题(美国t-mobile上的Galaxy Nexus),是因为移动数据已关闭。

在Jelly Bean中,它是:设置>数据使用量>移动数据

请注意,在发送彩信或接收彩信之前,必须先打开移动数据。如果我收到关闭了移动数据的彩信,则将收到新消息的通知,并收到带有下载按钮的消息。但是,如果我以前没有移动数据,将不会收到传入的MMS附件。即使我在收到消息后将其打开。

出于某种原因,当您的电话提供商允许您发送和接收MMS时,即使您正在使用Wifi,也必须启用移动数据,即使启用了移动数据,您也可以收发MMS,即使Wifi在您的设备上显示为您的互联网。

这真是一个痛苦,好像您没有打开它,即使打开“移动数据”,该消息也可能挂很多,并且可能需要重新启动设备。


另外,您还必须知道,发送SMS和MMS在后台是2个完全不同的事情。MMS更像是基于Internet的网络服务,因为它需要发送带有文本的其他项目(媒体)。给定的代码可以在我测试过的一些设备上正常工作。ps:您可以忽略诺基亚部分。
Manan Sharma

当我运行此示例时,在LogCat中打印:02-24 13:32:40.872:V / SendMMSActivity(5686):TYPE_MOBILE_MMS未连接,保释02-24 13:32:40.882:V / SendMMSActivity(5686):类型为不是TYPE_MOBILE_MMS,请保释。它还说:java.lang.SecurityException:没有写APN设置的权限:用户10099和当前进程都没有android.permission.WRITE_APN_SETTINGS。似乎无法执行此查询:final游标apnCursor = this.context.getContentResolver()。query(Uri.withAppendedPath(Carriers.CONTENT_URI,“ current”),null,null,null,null);林在Nexus 4测试
toobsco42

这也是@Sahil提供的示例。
toobsco42

7

没有官方的api支持,这意味着它没有公开记录,并且库可能随时更改。我知道您不想离开该应用程序,但是这是您出于其他任何人的意图而这样做的方式。

public void sendData(int num){
    String fileString = "..."; //put the location of the file here
    Intent mmsIntent = new Intent(Intent.ACTION_SEND);
    mmsIntent.putExtra("sms_body", "text");
    mmsIntent.putExtra("address", num);
    mmsIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileString)));
    mmsIntent.setType("image/jpeg");
    startActivity(Intent.createChooser(mmsIntent, "Send"));

}

我还没有完全弄清楚如何处理诸如跟踪消息的传递之类的事情,但这应该可以将其发送出去。

您会收到与彩信相同的方式,收到彩信的提示。接收器上的意图过滤器应如下所示。

<intent-filter>
    <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
    <data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>

这不只是启动本机消息传递应用程序吗?
toobsco42

1
是的,对此感到抱歉。我只是意识到您已经知道该怎么做。我确实添加了如何接收彩信。
user1959417 2013年

谢谢,我最近正在实施MMS的一部分BroadcastReceiver,并使用了Intent Filter您发布的。我将尽快更新此问题。
toobsco42 2013年

4

要发送未经许可编写apn设置的Android 4.0 api 14或更高版本的彩信,您可以使用以下库:从android检索mnc和mcc代码,然后调用

Carrier c = Carrier.getCarrier(mcc, mnc);
if (c != null) {
    APN a = c.getAPN();
    if (a != null) {
        String mmsc = a.mmsc;
        String mmsproxy = a.proxy; //"" if none
        int mmsport = a.port; //0 if none
    }
}

要使用此功能,请将Jsoup和droid棱镜罐添加到构建路径,然后导入com.droidprism。*;。


嘿@Sam,我将.jar文件添加到了我的项目中,但是在实例化Carrier对象的那一行出现此错误:这 java.lang.NoClassDefFoundError: com.droidprism.Carrier 是在发生吗?
toobsco42

没有。您必须将jsoup添加到构建路径,将jar添加到构建路径并导入com.droidprism。*;。我将编辑答案。要在android中做到这一点,首先将jars添加到libs目录,然后配置项目构建路径以使用libs目录中已经存在的jars,然后在构建路径config上单击order并导出并选中jars的框并移动将jsoup和droidprism置于构建顺序的顶部。
山姆·亚当什2013年

添加Jsoup .jar解决了NoClassDefFoundError。我现在可以获取APN设置。下一步是弄清楚如何发送彩信。
toobsco42


-2

我不明白挫败感。为什么不制作一个可以过滤此意图的广播接收器:

android.provider.Telephony.MMS_RECEIVED

我进行了进一步检查,您可能需要系统级别的访问权限才能获得此(rooted phone)。


3
嘿@ j2emanue,问题在于您收到此意图后,您实际上如何获得MMS的内容?如果彩信包含图像和文本,则如何提取这些组件。
toobsco42 2014年

但是我注意到,如果按照.i提到的方式进行操作,则可以获得额外的字节数组。林不知道如何解析,虽然抱歉。
j2emanue 2014年

我能够解析它。但是我所能获得的只是主题,彩信的来源以及存储彩信内容的contentlocation。但是无法访问该URL。
toobsco42 2014年

-2

SmsListenerClass

public class SmsListener extends BroadcastReceiver {

static final String ACTION =
        "android.provider.Telephony.SMS_RECEIVED";

@Override
public void onReceive(Context context, Intent intent) {

    Log.e("RECEIVED", ":-:-" + "SMS_ARRIVED");

    // TODO Auto-generated method stub
    if (intent.getAction().equals(ACTION)) {

        Log.e("RECEIVED", ":-" + "SMS_ARRIVED");

        StringBuilder buf = new StringBuilder();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {

            Object[] pdus = (Object[]) bundle.get("pdus");

            SmsMessage[] messages = new SmsMessage[pdus.length];
            SmsMessage message = null;

            for (int i = 0; i < messages.length; i++) {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    String format = bundle.getString("format");
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
                } else {
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                }

                message = messages[i];
                buf.append("Received SMS from  ");
                buf.append(message.getDisplayOriginatingAddress());
                buf.append(" - ");
                buf.append(message.getDisplayMessageBody());
            }

            MainActivity inst = MainActivity.instance();
            inst.updateList(message.getDisplayOriginatingAddress(),message.getDisplayMessageBody());

        }

        Log.e("RECEIVED:", ":" + buf.toString());

        Toast.makeText(context, "RECEIVED SMS FROM :" + buf.toString(), Toast.LENGTH_LONG).show();

    }
}

活动

@Override
public void onStart() {
    super.onStart();
    inst = this;
}

public static MainActivity instance() {
    return inst;
}

public void updateList(final String msg_from, String msg_body) {

    tvMessage.setText(msg_from + " :- " + msg_body);

    sendSMSMessage(msg_from, msg_body);

}

protected void sendSMSMessage(String phoneNo, String message) {

    try {
        SmsManager smsManager = SmsManager.getDefault();
        smsManager.sendTextMessage(phoneNo, null, message, null, null);
        Toast.makeText(getApplicationContext(), "SMS sent.", Toast.LENGTH_LONG).show();
    } catch (Exception e) {
        Toast.makeText(getApplicationContext(), "SMS faild, please try again.", Toast.LENGTH_LONG).show();
        e.printStackTrace();
    }
}

表现

<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS"/>

<receiver android:name=".SmsListener">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.