Android 系统发送和接收短信和彩信(前 Kit Kat Android 4.4)

我已经知道如何发送和接收短信。为了发送短信息,我必须调用 SmsManager类的 sendTextMessage()sendMultipartTextMessage()方法。要接收短信息,我必须在 AndroidMainfest.xml文件中注册一个接收器。然后我不得不重写 BroadcastReceiveronReceive()方法。我在下面列举了一些例子。

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 ...
}

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>

但是,我想知道你是否可以发送和接收彩信类似的方式。在做了一些研究之后,博客上提供的许多示例只是简单地将 Intent传递给本机 Messaging 应用程序。我试图发送彩信而不留下我的申请。似乎没有一个标准的方式来发送和接收彩信。有人能让这个起作用吗?

此外,我知道 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 中的文本?

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...
}
}

根据 电话服务提供商的文档:

广播行动: 一个新的基于文本的短信息已收到的设备。意图将具有以下额外值:

pdus-byte[]Object[],包含组成消息的 PDU。

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

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

广播行动: 一个新的数据为基础的短信息已收到的设备。意图将具有以下额外值:

pdus-byte[]Object[],包含组成消息的 PDU。

可以使用 getMessagesFromInent (android.content. Inent)提取额外的值。 如果 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 在处理此意图时遇到错误,则应适当地设置结果代码。 ContentTypeParameter 额外值是由名称键控的内容参数的映射。 如果遇到任何未分配的已知参数,映射的关键字将是’未分配/0x...’,其中’...’是未分配参数的十六进制值。如果参数具有 No-Value,那么 map 中的值将为 null。

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

更新 # 2

我已经知道如何通过额外的 PendingIntent被接收到的 BroadcastReceiver: Android 挂起意图附加,BroadcastReceiver 没有接收到

然而,额外的传递给 发送广播接收器而不是 短信接收器。我怎样才能传递一个额外的 短信接收器

更新 # 3

收到彩信

因此,在做了更多的研究之后,我看到了一些注册 ContentObserver的建议。这样,您就可以检测到 content://mms-sms/conversations内容提供程序的任何更改,从而允许您检测传入的 MMS。这里有一个最接近的例子,让这个工作,我已经发现: 收到彩信

但是,有一个类型为 ServiceController的变量 mainActivity。在哪里实现了 ServiceController类?已注册的 ContentObserver是否有其他实现?

发送彩信

至于发送彩信,我遇到了这个例子: 发送彩信

问题是,我试图在我的 Nexus4上运行这段代码,它是在 Android v4.2.2上运行的,我收到了这个错误:

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

APNHelper类的 getMMSApns()方法中查询 Carriers ContentProvider 后引发错误。

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

显然你不能 在 Android 4.2中读取 APN

对于那些使用移动数据执行操作(如发送彩信)而不知道设备中默认 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 错误。我现在正在安卓 KitKat 上测试 Nexus5。在运行示例代码之后,在调用

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

然而,我查了一下我试图发送彩信的人,他们说他们从来没有收到过彩信。

116379 次浏览

我不认为有任何 sdk 支持发送 mms 在机器人。至少我还没找到。但有个人声称有。看看这个帖子。

在 Android 中从我的应用程序发送彩信

没有官方的 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>

我遇到了和你上面描述的完全一样的问题(美国 T-mobile 上的 Galaxy Nexus) ,那是因为移动数据被关闭了。

在《糖豆》里是这样的: 设置 > 资料用量 > 流动数据

请注意,我必须有移动数据打开优先发送彩信或接收之一。如果我收到一个关闭移动数据的彩信,我会得到一个新消息的通知,我会收到一个下载按钮的消息。但是如果我没有手机上的数据之前,收到的彩信附件将不会被接收。即使我在收到信息后打开它。

由于某种原因,当你的电话供应商允许你发送和接收彩信时,你必须启用移动数据,即使你使用 Wifi,如果移动数据被启用,你将能够接收和发送彩信,即使 Wifi 显示为你的设备上的互联网。

这是一个真正的痛苦,因为如果你没有它,消息可以挂起很多,甚至当打开移动数据,可能需要重新启动设备。

如果你想在没有编写 apn 设置权限的情况下为 Android 4.0 api 14或更高版本发送 mms,你可以使用 这个图书馆: 从机器人中检索 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
}
}

要使用它,请将 和 droid prism jar 添加到构建路径,并导入 com.droidprism. * ;

我不理解这种挫折感。为什么不直接做一个广播接收器来过滤这种意图:

android.provider.Telephony.MMS_RECEIVED

我进一步检查了一下,您可能需要系统级访问才能获得这个(根电话)。

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>