Android 接收和收发短信

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

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

接收短信

其实接收短信主要是利用了我们在第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都表示一条短信消息。接着使用SmsMessagecreateFromPdu()方法将每一个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>

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


当有短信到来时,短信的发送方和内容就会显示在界面上。不过话说回来,我们使用的是模拟器,模拟器上怎么可能会收得到短信呢?不用担心,DDMS提供了非常充分的模拟环境,使得我们不需要支付真正的短信费用也可以模拟收发短信的场景。将Eclipse切换到DDMS视图下,然后点击Emulator Control切换卡,在这里就可以向模拟器发送短信了,如图所示。


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


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

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

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

拦截短信

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

在前面学习有序广播的时候我们就已经知道,有序广播的传递是可以截断的,而系统发出的短信广播正是一条有序广播,因此这里我们的答案是肯定的。修改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键将程序关闭后,系统的短信程序又会重新拥有接收短信的功能。

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

 发送短信

下面我们继续对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按钮被点击时,会先调用SmsManagergetDefault()方法获取到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按钮的点击事件里面我们调用了PendingIntentgetBroadcast()方法获取到了一个PendingIntent对象,并将它作为第四个参数传递到sendTextMessage()方法中。然后又注册了一个新的广播接收器SendStatusReceiver,这个广播接收器就是专门用于监听短信发送状态的,当getResultCode()的值等于RESULT_OK就会提示发送成功,否则提示发送失败。

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


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

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





发布了44 篇原创文章 · 获赞 21 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览