android: 接收和发送短信

时间:2024-03-04 18:56:19

8.2    接收和发送短信

 

收发短信应该是每个手机最基本的功能之一了,即使是许多年前的老手机也都会具备这 项功能,而 Android 作为出色的智能手机操作系统,自然也少不了在这方面的支持。每个 Android 手机都会内置一个短信应用程序,使用它就可以轻松地完成收发短信的操作,如 图 8.4 所示。

图   8.4

 

不过作为一名开发者,仅仅满足于此显然是不够的。你要知道,Android 还提供了一系 列的 API,使得我们甚至可以在自己的应用程序里接收和发送短信。也就是说,只要你有足 够的信心,完全可以自己实现一个短信应用来替换掉 Android 系统自带的短信应用。那么下 面我们就来看一看,如何才能在自己的应用程序里接收和发送短信。

 

8.2.1    接收短信

 

其实接收短信主要是利用了我们在第 5 章学习过的广播机制。当手机接收到一条短信的 时候,系统会发出一条值为 android.provider.Telephony.SMS_RECEIVED 的广播,这条广播里 携带着与短信相关的所有数据。每个应用程序都可以在广播接收器里对它进行监听,收到广 播时再从中解析出短信的内容即可。

让我们通过一个具体的例子来实践一下吧,新建一个 SMSTest 项目,首先修改 activity_

main.xml 中的代码,如下所示:

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"

android:orientation="vertical" >

 

 

<LinearLayout android:layout_width="match_parent"

 

 

 

android:layout_height="50dp" >

 

 

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:padding="10dp" android:text="From:" />

 

<TextView android:id="@+id/sender" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" />

</LinearLayout>

 

 

<LinearLayout android:layout_width="match_parent" android:layout_height="50dp" >

 

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:padding="10dp" android:text="Content:" />

 

<TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" />

</LinearLayout>

 

 

</LinearLayout>

这个布局文件里,我们在根元素下面放置了两个 LinearLayout,用于显示两行数据。第 一个 LinearLayout 中有两个 TextView,用于显示短信的发送方。第二个 LinearLayout 中也有 两个 TextView,用于显示短信的内容。

接着修改 MainActivity 中的代码,在 onCreate()方法中获取到两个 TextView 的实例,如下所示:

 

public class MainActivity extends Activity {

 

 

private TextView sender;

 

 

private TextView content;

 

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

sender = (TextView) findViewById(R.id.sender);

content = (TextView) findViewById(R.id.content);

}

 

 

}

然后我们需要创建一个广播接收器来接收系统发出的短信广播。在 MainActivity 中新建 MessageReceiver 内部类继承自 BroadcastReceiver,并在 onReceive()方法中编写获取短信数 据的逻辑,代码如下所示:

 

public class MainActivity extends Activity {

 

……

 

 

class MessageReceiver extends BroadcastReceiver {

 

 

@Override

public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras();

Object[] pdus = (Object[]) bundle.get("pdus"); // 提取短信消息

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

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

messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);

 

 

 

 

送方号码


}

String address = messages[0].getOriginatingAddress(); // 获取发

 

 

String fullMessage = "";

for (SmsMessage message : messages) {

fullMessage += message.getMessageBody(); // 获取短信内容

 

} sender.setText(address); content.setText(fullMessage);

}

 

}

}

可以看到,首先我们从 Intent 参数中取出了一个 Bundle 对象,然后使用 pdu 密钥来提取 一个 SMS pdus 数组,其中每一个 pdu 都表示一条短信消息。接着使用 SmsMessage 的 createFromPdu() 方法将每一个 pdu 字节数组转换为 SmsMessage 对象,调用这个对象的 getOriginatingAddress()方法就可以获取到短信的发送方号码,调用 getMessageBody()方法就 可以获取到短信的内容,然后将每一个 SmsMessage 对象中的短信内容拼接起来,就组成了 一条完整的短信。最后将获取到的发送方号码和短信内容显示在 TextView 上。

完成了 MessageReceiver 之后,我们还需要对它进行注册才能让它接收到短信广播,代 码如下所示:

 public class MainActivity extends Activity {

 

private TextView sender;

 

 

private TextView content;

 

 

private IntentFilter receiveFilter;

 

 

private MessageReceiver messageReceiver;

 

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

sender = (TextView) findViewById(R.id.sender); content = (TextView) findViewById(R.id.content); receiveFilter = new IntentFilter();

receiveFilter.addAction("android.provider.Telephony.SMS_RECEIVED");

messageReceiver = new MessageReceiver();

registerReceiver(messageReceiver, receiveFilter);

}

 

 

@Override

 

 

 

protected void onDestroy() {

super.onDestroy();

unregisterReceiver(messageReceiver);

}

……

}

这些代码你应该都已经非常熟悉了,使用的就是动态注册广播的技术。在 onCreate()方 法中对 MessageReceiver 进行注册,在 onDestroy()方法中再对它取消注册。

代码到这里就已经完成得差不多了,不过最后我们还需要给程序声明一个接收短信的权 限才行,修改 AndroidManifest.xml 中的代码,如下所示:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.smstest"

android:versionCode="1" android:versionName="1.0" >

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

……

</manifest>

现在可以来运行一下程序了,界面如图 8.5 所示。

图   8.5

 

当有短信到来时,短信的发送方和内容就会显示在界面上。不过话说回来,我们使用的是模拟器,模拟器上怎么可能会收得到短信呢?不用担心,DDMS 提供了非常充分的模拟环

境,使得我们不需要支付真正的短信费用也可以模拟收发短信的场景。将 Eclipse 切换到 DDMS 视图下,然后点击 Emulator Control 切换卡,在这里就可以向模拟器发送短信了,如 图 8.6 所示。

 

图   8.6

 

可以看到,我们指定发送方的号码是 556677,并填写了一段短信内容,然后点击 Send按钮,这样短信就发送成功了。接着我们立马查看一下 SMSTest 这个程序,结果如图 8.7 所示。

 

图   8.7

 

可以看到,短信的发送方号码和短信内容都显示到界面上了,说明接收短信的功能成功 实现了。

 

8.2.2    拦截短信

 

仔细观察图 8.7,你会发现在系统状态栏出现了一个通知图标,这个通知图标是由 Android 自带的短信程序产生的。也就是说当短信到来时,不仅我们的程序会接收到这条短信,系统 的短信程序同样也会收到。同样一条短信被重复接收两遍就会造成比较差的用户体验,那么 有没有什么办法可以屏蔽系统短信程序的接收功能呢?

在前面 5.3.2 节学习有序广播的时候我们就已经知道,有序广播的传递是可以截断的, 而系统发出的短信广播正是一条有序广播,因此这里我们的答案是肯定的。修改 MainActivity 中的代码,如下所示:

 

public class MainActivity extends Activity {

……

@Override

protected void onCreate(Bundle savedInstanceState) {

……

receiveFilter = new IntentFilter(); receiveFilter.addAction("android.provider.Telephony.SMS_RECEIVED"); receiveFilter.setPriority(100);

messageReceiver = new MessageReceiver();

registerReceiver(messageReceiver, receiveFilter);

}

……

class MessageReceiver extends BroadcastReceiver {

 

 

@Override

public void onReceive(Context context, Intent intent) {

……

abortBroadcast();

}

 

}

}

可以看到,关键性的步骤只有两步。一是提高 MessageReceiver 的优先级,让它能够先 于系统短信程序接收到短信广播。二是在 onReceive()方法中调用 abortBroadcast()方法,中止掉广播的继续传递。

现在重新运行程序,再向模拟器发送一条短信,这时只有我们自己的程序才能收到这条 短信了。按下 Back 键将程序关闭后,系统的短信程序又会重新拥有接收短信的功能。

注意这个功能一定要慎用,随意拦截短信有可能会造成重要数据的丢失,所以你在拦截 之前一定要先想清楚这种功能是不是你想要的。

 

8.2.3    发送短信

 

下面我们继续对 SMSTest 项目进行扩展,给它加上发送短信的功能。那么还是先来编写 一下布局文件吧,修改 activity_main.xml 中的代码,如下所示:

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"

android:orientation="vertical" >

……

<LinearLayout android:layout_width="match_parent" android:layout_height="50dp" >

<TextView

android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:padding="10dp"

android:text="To:" />

 

 

<EditText android:id="@+id/to" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" />

</LinearLayout>

 

 

<LinearLayout android:layout_width="match_parent" android:layout_height="50dp" >

<EditText

android:id="@+id/msg_input" android:layout_width="0dp"

 

 

 

android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" />

 

<Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Send" />

</LinearLayout>

 

 

</LinearLayout>

这里我们又新增了两个 LinearLayout,分别处于第三和第四行的位置。第三行中放置了 一个 EditText,用于输入接收方的手机号码。第四行中放置了一个 EditText 和一个 Button, 分别用于输入短信内容和发送短信。

然后修改 MainActivity 中的代码,在里面加入发送短信的处理逻辑,代码如下所示:

 

public class MainActivity extends Activity {

……

private EditText to; private EditText msgInput; private Button send;

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

……

to = (EditText) findViewById(R.id.to);

msgInput = (EditText) findViewById(R.id.msg_input); send = (Button) findViewById(R.id.send); send.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

SmsManager smsManager = SmsManager.getDefault();

smsManager.sendTextMessage(to.getText().toString(), null,

 

 

}

});

}

……

}


msgInput.getText().toString(), null, null);

 

可以看到,首先我们获取到了布局文件中新增控件的实例,然后在 Send 按钮的点击事 件里面处理了发送短信的具体逻辑。当 Send 按钮被点击时,会先调用 SmsManager 的 getDefault()方法获取到 SmsManager 的实例,然后再调用它的 sendTextMessage()方法就可以 去发送短信了。sendTextMessage()方法接收五个参数,其中第一个参数用于指定接收人的手 机号码,第三个参数用于指定短信的内容,其他的几个参数我们暂时用不到,直接传入 null 就可以了。

接下来也许你已经猜到了,发送短信也是需要声明权限的,因此修改 AndroidManifest.xml中的代码,如下所示:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.smstest"

android:versionCode="1"

android:versionName="1.0" >

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

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

……

</manifest>

现在重新运行程序之后,SMSTest 就拥有了发送短信的能力。不过点击 Send 按钮虽然 可以将短信发送出去,但是我们并不知道到底发送成功了没有,这个时候就可以利用 sendTextMessage()方法的第四个参数来对短信的发送状态进行监控。修改 MainActivity 中的 代码,如下所示:

 

public class MainActivity extends Activity {

……

private IntentFilter sendFilter;

 

 

private SendStatusReceiver sendStatusReceiver;

 

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

 

 

 

……

sendFilter = new IntentFilter(); sendFilter.addAction("SENT_SMS_ACTION"); sendStatusReceiver = new SendStatusReceiver(); registerReceiver(sendStatusReceiver, sendFilter); send.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

SmsManager smsManager = SmsManager.getDefault(); Intent sentIntent = new Intent("SENT_SMS_ACTION"); PendingIntent pi = PendingIntent.getBroadcast

(MainActivity.this, 0, sentIntent, 0);

smsManager.sendTextMessage(to.getText().toString(), null, msgInput.getText().toString(), pi, null);

}

});

}

 

@Override

protected void onDestroy() { super.onDestroy(); unregisterReceiver(messageReceiver);

unregisterReceiver(sendStatusReceiver);

}

……

class SendStatusReceiver extends BroadcastReceiver {

 

 

@Override

public void onReceive(Context context, Intent intent) {

if (getResultCode() == RESULT_OK) {

// 短信发送成功

Toast.makeText(context, "Send succeeded", Toast.LENGTH_LONG).show();

} else {

// 短信发送失败

Toast.makeText(context, "Send failed", Toast.LENGTH_LONG).show();

}

}

 

 

 

}

}

可以看到,在 Send 按钮的点击事件里面我们调用了 PendingIntent 的 getBroadcast()方法 获取到了一个 PendingIntent 对象,并将它作为第四个参数传递到 sendTextMessage()方法中。 然后又注册了一个新的广播接收器 SendStatusReceiver,这个广播接收器就是专门用于监听 短信发送状态的,当 getResultCode()的值等于 RESULT_OK 就会提示发送成功,否则提示发 送失败。

现在重新运行一下程序,在文本输入框里输入接收方的手机号码以及短信内容,然后点 击 Send 按钮,结果如图 8.8 所示。

 

图   8.8

 

注意,这里虽然提示发送成功了,但实际上使用模拟器来发送短信对方是不可能收得到 的,只有把这个项目运行在手机上,才能真正地实现发送短信的功能。

另外,根据国际标准,每条短信的长度不得超过 160 个字符,如果想要发送超出这个长 度的短信,则需要将这条短信分割成多条短信来发送,使用 SmsManager 的 sendMultipart- TextMessage()方法就可以实现上述功能。它的用法和 sendTextMessage()方法也基本类似,感 兴趣的话你可以自己研究一下,这里就不再展开讲解了。