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()方法也基本类似,感 兴趣的话你可以自己研究一下,这里就不再展开讲解了。

android: 接收和发送短信的更多相关文章

  1. android 几种发送短信的方法

    android中发送短信很简单, 首先要在Mainfest.xml中加入所需要的权限: ? 1 2 3 <uses-permission android:name="android.p ...

  2. Android开发之发送短信

    本实例通过SmsManager的sendTextMessage方法实现发送短信关于SmsManager的具体解释大家能够參照:Android开发之SmsManager具体解释 实例执行效果图: 程序代 ...

  3. Android 中发送短信

    import android.net.Uri; //调用Android系统API发送短信 Uri uri = Uri.parse("smsto:" + strSmsPhone_va ...

  4. Android之发送短信和接收验证码

      最近项目需求需要发送短信和接收验证码并将验证码显示在输入框中 以下是我的记录    前提---权限     <uses-permission android:name="andro ...

  5. android 发送短信的两种方式,以及接收报告和发送报告

               android发送短信,以及接收报告和发送报告          android中发送短信其实有两种方式,这个和打电话类似,大家可以了解一下:    一.调起系统发短信功能    ...

  6. android 中调用接口发送短信

    android中可以通过两种方式发送短信 第一:调用系统短信接口直接发送短信:主要代码如下: //直接调用短信接口发短信 SmsManager smsManager = SmsManager.getD ...

  7. 无废话Android之activity的生命周期、activity的启动模式、activity横竖屏切换的生命周期、开启新的activity获取他的返回值、利用广播实现ip拨号、短信接收广播、短信监听器(6)

    1.activity的生命周期 这七个方法定义了Activity的完整生命周期.实现这些方法可以帮助我们监视其中的三个嵌套生命周期循环: (1)Activity的完整生命周期 自第一次调用onCrea ...

  8. Android SmsManager 发送短信

    SmsManager可以在后台发送短信,无需用户操作,开发者就用这个SmsManager功能在后台偷偷给SP发短信,导致用户话费被扣.必须添加android.permission.SEND_SMS权限 ...

  9. Android软件开发之发送短信与系统短信库解析

    今天我和同学们讨论一下Android平台下如何调用系统方法发送短信.接收短信.系统的短信库相关的问题.进入正题,我们先使用Eclipse工具模拟给自己的模拟器发送一条短信.在Eclipse下打开DDM ...

随机推荐

  1. knockout.js $index 做列表索引小技巧

    我们都知道,在foreach binding中,使用$index可以得到基于0的索引序号,但在列表显示中,我们更希望这个索引是从1开始的,怎么处理呢? 这里,有个小技巧:使用$index() + 1, ...

  2. 如何MSHTML命名空间解析HTML文件(MSHTML::IHTMLDocument2Ptr 提示错误)

    1.创建Win32或MFC工程. 2.在预编译或需要使用MSHTML命名空间的头文件中添加以下语句: #include <atlbase.h>    #include <Mshtml ...

  3. POJ3680_Intervals

    给你若干个区间,每个区间有一个权值,你可以选出某些区间,使得在保证没有任何一段的覆盖次数超过k的前提下,总的权值最大. 这个建模真的十分神奇,赞一个. 对于给出的每一个区间,离散化,最终我们可以知道所 ...

  4. 手拼Table 前台显示

    一:前台 <table border=</table> 二:后台 //表头 public void TABTITLE() { TblList.Rows.Clear(); HtmlTa ...

  5. 数位DP (51nod)

    题目:数字1的数量 思路:首先考察不同位数以内的所有整数出现1的次数,例如四位数以内[0,9999],个十百千位均有可能出现1, 出现1的时候,其它三个位均可以是0~9,所以假设固定一个位为1,另外三 ...

  6. mysql.sock

    Mysql有两种连接方式: (1)TCP/IP (2)socket 对mysql.sock来说,其作用是程序与mysqlserver处于同一台机器,发起本地连接时可用. 例如你无须定义连接host的具 ...

  7. 【4_237】Delete Node in a Linked List

    Delete Node in a Linked List Total Accepted: 48121 Total Submissions: 109297 Difficulty: Easy Write ...

  8. mfc/格式转换

    1.int型转为字符串型 int s = 123; CString str; str.Format("%d",s);

  9. hdu 5682 zxa and leaf

    zxa and leaf  Accepts: 25  Submissions: 249  Time Limit: 5000/2500 MS (Java/Others)  Memory Limit: 6 ...

  10. iOS 关于nil和Nil及null与<null>的区别

    问题是这样的. NSDictionary *sample = [NSJSONSerialization JSONObjectWithData:received options:NSJSONReadin ...