Android实战技巧之三十九:短信收发

时间:2022-12-22 16:36:29

7月4日从广州出差回来就定下写作计划,但迟迟没有动笔。耽搁的原因还是老样子,工作上又有新任务,全部精力都投入过去了,每天精疲力竭的回来也打不起精神做其他事了。这就是精力管理不当所致,就像我把很多要做的事无情的放到“等有时间”再做一样。今晚,我一定要给自己一个交待。不论文章写的如何,但不动笔就永远是零。

正文在下面

一直以来,Android的手机功能(通话与短信)都放在android.telephony包中,到了4.4时(也就是API19)android.provider.Telephony及相关类横空出世辅助电话功能以及制定安卓世界手机功能的新秩序。
我们的短信功能用到了SmsManager和SmsMessage两个主要类。

发送短信via Intent

万能的intent能够帮我们做很多事,只要你有“意图”它就会满足你。

    private void sendMessageViaSystem() {
Uri uri = Uri.parse("smsto:"+etNumber.getText());
Intent intent = new Intent(Intent.ACTION_VIEW,uri);
intent.putExtra("sms_body",etMessage.getText().toString());
// intent.setType("rnd");
startActivity(intent);
}

发送短信via SmsManager

    private void sendMessage() {        
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(etNumber.getText().toString(), null,
etMessage.getText().toString(), null, null);
}

最简单的发送短信条件就是有电话号码和短信内容,调用SmsManager的sendTextMessage方法即可。

监听短信的发送状态

sendTextMessage方法的后两个参数是PendingIntent,函数原型如下:

     * @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK</code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
* the extra "errorCode" containing a radio technology specific value,
* generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent)

而我们要监听发送状态就要用到这两个参数。与这个两个PendingIntent联系的是两个Broadcast Receiver,其会接收到发送过程中状态广播。就像上面参数解释的一样。
代码示例如下:

    private String SMS_SEND_ACTIOIN = "SMS_SEND";
private String SMS_DELIVERED_ACTION = "SMS_DELIVERED";

private SmsStatusReceiver mSmsStatusReceiver;
private SmsDeliveryStatusReceiver mSmsDeliveryStatusReceiver;

@Override
protected void onResume() {
super.onResume();
mSmsStatusReceiver = new SmsStatusReceiver();
registerReceiver(mSmsStatusReceiver,new IntentFilter(SMS_SEND_ACTIOIN));

mSmsDeliveryStatusReceiver = new SmsDeliveryStatusReceiver();
registerReceiver(mSmsDeliveryStatusReceiver,new IntentFilter(SMS_DELIVERED_ACTION));
}

@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mSmsStatusReceiver);
unregisterReceiver(mSmsDeliveryStatusReceiver);
}

private void sendMessage() {
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SEND_ACTIOIN), 0);
PendingIntent deliveryIntent = PendingIntent.getBroadcast(this, 0,
new Intent(SMS_DELIVERED_ACTION), 0);
smsManager.sendTextMessage(etNumber.getText().toString(), null,
etMessage.getText().toString(), sentIntent, deliveryIntent);
Log.d(TAG,"sent message.");
}

public class SmsStatusReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"SmsStatusReceiver onReceive.");
switch(getResultCode()) {
case Activity.RESULT_OK:
Log.d(TAG, "Activity.RESULT_OK");
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Log.d(TAG, "RESULT_ERROR_GENERIC_FAILURE");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Log.d(TAG, "RESULT_ERROR_NO_SERVICE");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Log.d(TAG, "RESULT_ERROR_NULL_PDU");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Log.d(TAG, "RESULT_ERROR_RADIO_OFF");
break;
}
}
}

public class SmsDeliveryStatusReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"SmsDeliveryStatusReceiver onReceive.");
switch(getResultCode()) {
case Activity.RESULT_OK:
Log.i(TAG, "RESULT_OK");
break;
case Activity.RESULT_CANCELED:
Log.i(TAG, "RESULT_CANCELED");
break;
}
}
}

短信接收

与发送状态监听类似,短信的接收也是用广播接收器。为了一直对短信广播的接收,我采用了在Manifest中注册广播的方法。

        <receiver android:name="com.linc.intercept.SmsReceiver" >
<intent-filter android:priority="1000" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>

并且将用一个单独类作为短信接收类(这样的方式是不能将其放到内部类中的)。

package com.linc.intercept;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;

/**
* Created by linc on 15-7-12.
*/

public class SmsReceiver extends BroadcastReceiver {
private static final String TAG = "SmsReceiver";
public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";

@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG,"action: "+action);
if (SMS_RECEIVED_ACTION.equals(action)) {
Bundle bundle = intent.getExtras();
StringBuffer messageContent = new StringBuffer();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
for (Object pdu : pdus) {
SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
String sender = message.getOriginatingAddress();
Log.d(TAG,"sender: "+sender);
if ("10086".equals(sender) || "10010".equals(sender) ||
"10001".equals(sender)) {
messageContent.append(message.getMessageBody());
}
}
if(!messageContent.toString().isEmpty()) {
Log.d(TAG,"send message broadcast.");
Intent intentBroadcast = new Intent();
intentBroadcast.putExtra("message", messageContent.toString());
intentBroadcast.setAction("sms_received");
context.sendBroadcast(intentBroadcast);
Log.d(TAG, "send broadcast and abort");
// abortBroadcast();
}
}
}
}
}

但是这样做的弊端就是,接收到的短信如何显示到界面?路有多条,我最后还是选择了广播。

    private SmsReceiver mSmsReceiver;
@Override
protected void onResume() {
super.onResume();
mSmsReceiver = new SmsReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("sms_received");
registerReceiver(mSmsReceiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mSmsReceiver);
}
public class SmsReceiver extends BroadcastReceiver {
public static final String SMS_RECEIVED_ACTION = "sms_received";

@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG,"action: "+action);
if (SMS_RECEIVED_ACTION.equals(action)) {
Bundle bundle = intent.getExtras();

String messageContent = bundle.getString("message");
tvMessage.setText(messageContent);
}
}
}

调试tip

我倾向于用真机调试,我们可以给运营商发送短信,这样就不用担心花费的问题啦。
移动
号码:10086
内容:119等一系列查询消费的码
联通
号码:10010
内容:101 当月花费;102 可用余额;103上月账单…
电信
号码:10001
内容:101实时花费;102账户余额…