使用SMS在自己的程序中:
1. 我们可以通过使用SMSManager来代替本身的SMS应用程序来收发短信或者使用SMS来作为数据传输层。
2. SMS并不能响应实时的需求。
3. 发送SMS短讯:SmsManager smsManager=SmsManager.getDefault();
4. 需要权限android.permission.SEND_SMS
5. String sendTo=”2353432”; String mMessage=”it is a pig”;
6. smsManager.sendTextMessage(sendTo,null,mMessage,null,null();
7. 上述语句的第二个参数用来指定SMS服务中心,null表示使用默认的。倒数第二个参数用来指定Intents来响应发送时的事件和成功发送后的响应事件。
8. 跟踪和确定SMS 信息的投递:为了跟踪传输和发送信息成功,注册和实现Broadcast Receivers用来监听我们指定的actions。在我们创建Pending Intents做为sendTextMessage的后两个参数的时候。
9. 第一个Pending Intent 参数,sentIntent,无论信息是成功传递或者是失败都会被触发。Broadcast Receiver接收到的Intent的 result code 将会是以下的一个:Activity.RESULT_OK :传输成功 SmsManager.RESULT_ERROR_GENERIC_FAILURE :不确定的错误 SmsManager.RESULT_ERROR_RADIO_OFF: 因为radio是关闭的导致错误 SmsManager.RESULT_ERROR_NULL_PDU: PUD错误
10. 第二个 Pending Intent参数在对方接收到你的短信的时候触发。
代码示意:String SENT_SMS_ACTION = “SENT_SMS_ACTION”;
String DELIVERED_SMS_ACTION = “DELIVERED_SMS_ACTION”;
// Create the sentIntent parameter
Intent sentIntent = new Intent(SENT_SMS_ACTION);
PendingIntent sentPI = PendingIntent.getBroadcast(getApplicationContext(),
0,sentIntent,0);
// Create the deliveryIntent parameter
Intent deliveryIntent = new Intent(DELIVERED_SMS_ACTION);
PendingIntent deliverPI = PendingIntent.getBroadcast(getApplicationContext(),
0,deliveryIntent,0);
// Register the Broadcast Receivers
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context _context, Intent _intent) {
switch (getResultCode()) {
case Activity.RESULT_OK:
[… send success actions … ]; break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
[… generic failure actions … ]; break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
[… radio off failure actions … ]; break;
case SmsManager.RESULT_ERROR_NULL_PDU:
[… null PDU failure actions … ]; break;
}
}
},
new IntentFilter(SENT_SMS_ACTION));
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context _context, Intent _intent) {
[… SMS delivered actions … ]
}
},
new IntentFilter(DELIVERED_SMS_ACTION));
// Send the message
smsManager.sendTextMessage(sendTo, null, myMessage, sentPI, deliverPI);
11. 监视发出的SMS短信:android可以在不同的模拟器上发送短信通过指定端口号作为(发送地址),5554之类的。
遵守SMS短信的最大Size:通常SMS的文本信息被限制在160个Characters(字符),一个汉字两个字符,所以,更长的信息就会被分段发送。SMS Manager包含一个divideMessager方法,它接收一个字符串String作为输入参数并且把它分割为一个ArrayList,其中每一项都比最长许可长度小。SendMultipartTextMessage就是用来发送长消息的方法
ArrayList<String> messageArray = smsManager.divideMessage(myMessage);
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
for (int i = 0; i < messageArray.size(); i++) sentIntents.add(sentPI);
smsManager.sendMultipartTextMessage(sendTo,null,messageArray,sentIntents, null); sentIntent和deliveryIntent参数在这个方法中是Arraylist,它可以指定不同的PendingIntent来响应每部分消息的发送。
发送data message。我们可以发送二进制的数据通过使用SMS Manager的sendDataMessage方法。这个方法和sendTextMessage的工作机制很像,但是包括两个个额外的参数---目的端口和一个字节数组(它包含我们想发送的数据)。Intent sentIntent = new Intent(SENT_SMS_ACTION);
PendingIntent sentPI = PendingIntent.getBroadcast(getApplicationContext(),
0,
sentIntent,
0);
short destinationPort = 80;
byte[] data = [ … your data … ];
smsManager.sendDataMessage(sendTo, null, destinationPort, data, sentPI, null);----在sms的源码上测试过,不好使
12. 监听SMS 信息:当设备受到一条新的SMS信息,就会启动一个包含着 android.provider.Telephony.SMS_RECEIVED action的broadcast Intent 。
13. 应用程序监听SMS intent广播,需要增加权限android.permission.RECEIVER_SMS。
这个SMS的广播 intent 包含来信的详细信息。为了转换每个pdu字节数组成为一个SMS信息对象,我们调用SmsMessage.createFromPdu方法。 Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get(“pdus”);
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++)
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
每个SmsMessage对象包含SMS信息的详细内容,包括发送人的地址(电话号码),时间戳和信息主体。以下的例子展示了一个Broadcast Receiver的实现,它的onReceive [email protected],然后返回同样的内同给手机并且发送。 public class IncomingSMSReceiver extends BroadcastReceiver {
private static final String queryString = [email protected] “;
private static final String SMS_RECEIVED = “android.provider.Telephony.SMS_RECEIVED”;
public void onReceive(Context _context, Intent _intent) {
if (_intent.getAction().equals(SMS_RECEIVED)) {
SmsManager sms = SmsManager.getDefault();
Bundle bundle = _intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get(“pdus”);
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++)
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
for (SmsMessage message : messages) {
String msg = message.getMessageBody();
String to = message.getOriginatingAddress();
if (msg.toLowerCase().startsWith(queryString)) {
String out = msg.substring(queryString.length());//截取子串,指定开始的index
sms.sendTextMessage(to, null, out, null, null);
}
}
}
}
}
}
为了监听来信,使用Intent Filter注册Broadcast Receiver来监听android.provider.Telephony.SMS_RECEIVED action。如下: final String SMS_RECEIVED = “android.provider.Telephony.SMS_RECEIVED”;
IntentFilter filter = new IntentFilter(SMS_RECEIVED);
BroadcastReceiver receiver = new IncomingSMSReceiver();
registerReceiver(receiver, filter);
14. 模拟来信:模拟器相互之间发送短信可以通过他们的端口号作为目标地址。
处理 Data SMS 信息:接收Data信息和接收普通的文本信息是一样的,并且按照如下方式提取,使用getUserData和getUserDataHeader方法: byte[] data = msg.getUserData();
SmsHeader header = msg.getUserDataHeader(); getUserData方法返回包含短信数据的字节数组。 getUserDataHeader 返回一组用来描述信息包含的数据的 metadata元素。(什么玩意?)
15. 危急事件响应SMS例子:在这个例子中,我们创建一个SMS应用来让android手机转换成为一个危急事件响应的灯塔。在你遇到危难的时候,手机可以自动的给你的朋友和家人发送短信。 首先需要location-based services来告诉你的救援者你的位置。SMS网络的构造的健壮性对程序的可靠性和可接收能力是决定性的。
首先建立一个名为EmergencyResponder的工程,创建EmergencyResponder Activity。如下:public class EmergencyResponder extends Activity {
@Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
}
}
增加发送和接收短信以及得到自身位置的许可,在程序的manifest中 <?xml version=”1.0” encoding=”utf-8”?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.paad.emergencyresponder”>
<application android:[email protected]/icon”
android:[email protected]/app_name”>
<activity android:name=”.EmergencyResponder”
android:[email protected]/app_name”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
<uses-permission
android:name=”android.permission.ACCESS_GPS”/>
<uses-permission
android:name=”android.permission.ACCESS_LOCATION”/>
<uses-permission
android:name=”android.permission.RECEIVE_SMS”/>
<uses-permission android:name=”android.permission.SEND_SMS”/>
</manifest>
修改main.xml,包括一个List View来显示需要知道你状态更新的人以及一系列的buttons,用户可以点击它们来发送SMS 信息的回复。使用外部资源来填充button的文本信息<?xml version=”1.0” encoding=”utf-8”?>
<RelativeLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<TextView
android:id=”@+id/labelRequestList”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”These people want to know if you’re ok”
android:layout_alignParentTop=”true”/>
<LinearLayout
android:id=”@+id/buttonLayout” xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:padding=”5px”
android:layout_alignParentBottom=”true”>
<CheckBox
android:id=”@+id/checkboxSendLocation”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Include Location in Reply”/>
<Button
android:id=”@+id/okButton”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:[email protected]/respondAllClearButtonText”/>
<Button
android:id=”@+id/notOkButton”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:[email protected]/respondMaydayButtonText”/>
<Button
android:id=”@+id/autoResponder”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Setup Auto Responder”/>
</LinearLayout>
<ListView
android:id=”@+id/myListView”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:[email protected]/labelRequestList”
android:[email protected]/buttonLayout”/>
</RelativeLayout>
更新外部资源strings.xml,包含按钮的文本信息以及默认的回复信息,如“I am safe”和“I am in danger”,还应该定义来信文本在侦测到有请求状态信息的时候使用。<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string name=”app_name”>Emergency Responder</string>
<string name=”respondAllClearButtonText”>I am Safe and Well</string>
<string name=”respondMaydayButtonText”>MAYDAY! MAYDAY! MAYDAY!</string>
<string name=”respondAllClearText”>I am safe and well. Worry not!</string>
<string name=”respondMaydayText”>Tell my mother I love her.</string>
<string name=”querystring”>are you ok?</string>
</resources>
16.
17. 创建一个String 类型的Array List 在EmergencyResponder中保存需要知道你状态的电话号码。把这个Array List绑定到List View中通过在onCreate中使用一个Array Adapter,并且创建一个新的ReentrantLock对象来保证管理Array List的线程安全。
为每个按钮添加响应点击事件的监听器。每个按钮都应该调用响应的respond方法,然而Setup Auto Responder 按钮应该调用 startAutoResponder stub桩。ReentrantLock lock;
CheckBox locationCheckBox;
ArrayList<String> requesters;
ArrayAdapter<String> aa;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
lock = new ReentrantLock();
requesters = new ArrayList<String>();
wireUpControls();
}
private void wireUpControls() {
locationCheckBox = (CheckBox)findViewById(R.id.checkboxSendLocation); ListView myListView = (ListView)findViewById(R.id.myListView);
int layoutID = android.R.layout.simple_list_item_1;
aa = new ArrayAdapter<String>(this, layoutID, requesters);
myListView.setAdapter(aa);
Button okButton = (Button)findViewById(R.id.okButton);
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
respond(true, locationCheckBox.isChecked());
}
});
Button notOkButton = (Button)findViewById(R.id.notOkButton);
notOkButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
respond(false, locationCheckBox.isChecked());
}
});
Button autoResponderButton = (Button)findViewById(R.id.autoResponder);
autoResponderButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
startAutoResponder();
}
});
}
public void respond(boolean _ok, boolean _includeLocation) {}
private void startAutoResponder() {}
接下来实现用来监听来信的Broadcast Receiver。首先创建一个静态字符串变量来存储来短信的 intent action。 public static final String SMS_RECEIVED =“android.provider.Telephony.SMS_RECEIVED”; 接着在EmergencyResponder中创建一个新的Broadcast Receiver,这个接收器应该监听来信并且在它发现短信包含“are you ok”的字符串的时候调用requestRecieved方法。BroadcastReceiver emergencyResponseRequestReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context _context, Intent _intent) {
if (_intent.getAction().equals(SMS_RECEIVED)) {
String queryString = getString(R.string.querystring);
Bundle bundle = _intent.getExtras();
if (bundle != null) { Object[] pdus = (Object[]) bundle.get(“pdus”);
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++)
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
for (SmsMessage message : messages) {
if (message.getMessageBody().toLowerCase().contains(queryString)) {
requestReceived(message.getOriginatingAddress());
}
}
}
}
}
};
public void requestReceived(String _from) {}
更新Emergency Responder的onCreate方法来注册Broadcast Receiver。 @Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
lock = new ReentrantLock();
requesters = new ArrayList<String>();
wireUpControls();
IntentFilter filter = new IntentFilter(SMS_RECEIVED);
registerReceiver(emergencyResponseRequestReceiver, filter);
}
18. 更新requestReceived方法,增加发送状态请求的来信人的号码到 Array List中。
public void requestReceived(String _from) {
if (!requesters.contains(_from)) {
lock.lock();
requesters.add(_from);
aa.notifyDataSetChanged();
lock.unlock();
}
}
19. 这个Emergency Responder activity现在应该可以监听请求状态的SMS 信息并在接收到他们的时候,把他们添加到List View中。
更新 activity让用户响应这些状态的请求。首先完成这个响应方法respond。它应该列举array list中的所有请求者并且给每位都发送一个新的SMS message,这些响应信息就是我们在外部的String.xml资源中定义的字符串。 public void respond(boolean _ok, boolean _includeLocation) {
String okString = getString(R.string.respondAllClearText);
String notOkString = getString(R.string.respondMaydayText);
String outString = _ok ? okString : notOkString;
ArrayList<String> requestersCopy = (ArrayList<String>)requesters.clone();
for (String to : requestersCopy)
respond(to, outString, _includeLocation);
}
private void respond(String _to, String _response, boolean _includeLocation) {}
更新 respond方法来管理发送每条响应SMS。首先在发送SMS之前,从requesters Array List中移除每个可能的接收器。如果我们的回复包括我们当前的位置,则在发送SMS之前使用 location manager来找到它(经纬度)。 public void respond(String _to, String _response, boolean _includeLocation) {
// Remove the target from the list of people we need to respond to.
lock.lock();
requesters.remove(_to);
aa.notifyDataSetChanged(); lock.unlock();
SmsManager sms = SmsManager.getDefault();
// Send the message
sms.sendTextMessage(_to, null, _response, null, null);
StringBuilder sb = new StringBuilder();
// Find the current location and send it as SMS messages if required.
if (_includeLocation) {
String ls = Context.LOCATION_SERVICE;
LocationManager lm = (LocationManager)getSystemService(ls);
Location l = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
sb.append(“I’m @:\n”);
sb.append(l.toString() + “\n”);
List<Address> addresses;
Geocoder g = new Geocoder(getApplicationContext(), Locale.getDefault());
try {
addresses = g.getFromLocation(l.getLatitude(), l.getLongitude(), 1);
if (addresses != null) {
Address currentAddress = addresses.get(0);
if (currentAddress.getMaxAddressLineIndex() > 0) {
for (int i = 0; i < currentAddress.getMaxAddressLineIndex(); i++) {
sb.append(currentAddress.getAddressLine(i));
sb.append(“\n”);
}
}
else {
if (currentAddress.getPostalCode() != null)
sb.append(currentAddress.getPostalCode());
}
}
} catch (IOException e) {}
ArrayList<String> locationMsgs = sms.divideMessage(sb.toString());
for (String locationMsg : locationMsgs)
sms.sendTextMessage(_to, null, locationMsg, null, null);
}
}
SendTextMessage的第四个参数需要一个非空的值。我们在20中进行定义。
在危急的时候,确保信息可以发送时很正确的,为了提高应用的健壮性,我们添加了自动重发功能。监视你的SMS传输是否成功,因此你可以重新广播这个message如果它并没有成功发送。首先创建静态字符串 public static final String SENT_SMS = “com.paad.emergencyresponder.SMS_SENT”;
更新respond方法包含一个新的PendingIntent来广播这个action,当SMS 传送完成的时候。public void respond(String _to, String _response, boolean _includeLocation) {
// Remove the target from the list of people we need to respond to.
lock.lock();
requesters.remove(_to);
aa.notifyDataSetChanged();
lock.unlock();
SmsManager sms = SmsManager.getDefault();
Intent intent = new Intent(SENT_SMS);
intent.putExtra(“recipient”, _to);
PendingIntent sentIntent = PendingIntent.getBroadcast(getApplicationContext(),
0,
intent,
0);
// Send the message
sms.sendTextMessage(_to, null, _response, sentIntent, null);
StringBuilder sb = new StringBuilder();
if (_includeLocation) {
[ … existing respond method that finds the current location … ]
ArrayList<String> locationMsgs = sms.divideMessage(sb.toString());
for (String locationMsg : locationMsgs)
sms.sendTextMessage(_to, null, locationMsg, sentIntent, null);
}
}
然后实现一个新的Broadcast Receiver来监听broadcast intent。Override它的onReceive。
private BroadcastReceiver attemptedDeliveryReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context _context, Intent _intent) {
if (_intent.getAction().equals(SENT_SMS)) {
if (getResultCode() != Activity.RESULT_OK) {
String recipient = _intent.getStringExtra(“recipient”);
requestReceived(recipient);
}
}
}
};
最后,[email protected]
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
lock = new ReentrantLock();
requesters = new ArrayList<String>();
wireUpControls();
IntentFilter filter = new IntentFilter(SMS_RECEIVED);
registerReceiver(emergencyResponseRequestReceiver, filter);
IntentFilter attemptedDeliveryfilter = new IntentFilter(SENT_SMS);
registerReceiver(attemptedDeliveryReceiver, attemptedDeliveryfilter);
}
20. OK!可以运行模拟器测试了。