SMS Messaging in Android(1)
转自:http://jakielong.javaeye.com/blog/732328
可以安全的说,在过去的近 20 年里卖的每一款移动电话都拥有 SMS 消息功能。事实上, SMS 消息是移动手机中一个杀手级的应用程序,它为移动运营商创造了稳定的收入源。理解如何在你的应用程序中使用 SMS 消息能帮助你产生灵感来创建下一个杀手级程序。
在这篇文章里,我将一览如何在你的 Android 应用程序中发送和接收 SMS 消息。对 Android 开发者来说,有个好消息是你不需要一款真实的设备来测试 SMS 消息——免费的 Android 模拟器提供这一功能。
发送SMS消息
开始,先启动 Eclipse 并创建一个新的 Android 工程。命名工程如图 1 所示。
图 1 使用 Eclipse 创建一个新的 Android 工程
Android 使用一种基于权限的策略,应用程序所需的所有权限需要在 AndroidManifest.xml 文件中指定。通过这样做,当应用程序安装后,用户就能清楚地知晓应用程序需要哪些特定的访问权限。举个例子,发送 SMS 消息会引起用户的额外费用,在 AndroidManifest.xml 文件中指明 SMS 权限能让用户决定应用程序是否安装。
在 AndroidManifest.xml 文件中,添加两个权限—— SEND_SMS 和 RECEIVE_SMS :
<?xml version = "1.0" encoding = "utf-8" ?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
package = "net.learn2develop.SMSMessaging"
android:versionCode = "1"
android:versionName = "1.0.0" >
<application android:icon = "@drawable/icon" android:label = "@string/app_name" >
<activity android:name = ".SMS"
android:label = "@string/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.SEND_SMS" >
</uses-permission>
<uses-permission android:name = "android.permission.RECEIVE_SMS" >
</uses-permission>
</manifest>
在 res/layout 文件夹下的 main.xml 文件中添加以下代码,这样,用户可以输入电话号码和要发送的消息:
<?xml version = "1.0" encoding = "utf-8" ?>
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
>
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "Enter the phone number of recipient"
/>
<EditText
android:id = "@+id/txtPhoneNo"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
/>
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "Message"
/>
<EditText
android:id = "@+id/txtMessage"
android:layout_width = "fill_parent"
android:layout_height = "150px"
android:gravity = "top"
/>
<Button
android:id = "@+id/btnSendSMS"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "Send SMS"
/>
</LinearLayout>
上面的代码创建的 UI 如图 2 所示。
图 2 创建发送 SMS 消息的 UI
接下来,在 SMS Activity 中,我们连接 Button View ,这样当用户点击它时,我们可以检查接收者的电话号码和输入的消息,然后使用 sendSMS 函数来发送消息。
package net.learn2develop .SMSMessaging ;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.gsm.SmsManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class SMS extends Activity
{
Button btnSendSMS;
EditText txtPhoneNo ;
EditText txtMessage ;
/** Called when the activity is first created. */
@Override
public void onCreate( Bundle savedInstanceState)
{
super .onCreate ( savedInstanceState);
setContentView ( R.layout .main );
btnSendSMS = ( Button ) findViewById( R.id .btnSendSMS );
txtPhoneNo = ( EditText) findViewById( R.id .txtPhoneNo );
txtMessage = ( EditText) findViewById( R.id .txtMessage );
btnSendSMS. setOnClickListener ( new View .OnClickListener ()
{
public void onClick( View v)
{
String phoneNo = txtPhoneNo.getText () .toString ();
String message = txtMessage.getText () .toString ();
if ( phoneNo.length ()> 0 && message.length ()> 0 )
sendSMS ( phoneNo, message);
else
Toast. makeText ( getBaseContext() ,
"Please enter both phone number and message." ,
Toast. LENGTH_SHORT ) .show ();
}
});
}
}
sendSMS 函数定义如下:
public class SMS extends Activity
{
//...
/** Called when the activity is first created. */
@Override
public void onCreate( Bundle savedInstanceState)
{
//...
}
//---sends an SMS message to another device---
private void sendSMS( String phoneNumber, String message)
{
PendingIntent pi = PendingIntent. getActivity ( this , 0 ,
new Intent( this , SMS.class ) , 0 );
SmsManager sms = SmsManager. getDefault ();
sms. sendTextMessage ( phoneNumber, null , message, pi, null );
}
}
为了发送一个 SMS 消息,你需要使用 SmsManager 类。不同于其它的类,你不需要直接实例化这个类;取而代之的是调用 getDefault() 静态方法来获取一个 SmsManager 对象。sendTextMessage () 方法发送 SMS 消息和一个 PendingIntent 。 PendingIntent 对象用于在以后的时间识别触发的目标。例如,发送消息后,你可以使用 PendingIntent 对象来显示其它的 Activity 。在这里, PendingIntent 对象( pi )简单指向相同的 Activity ( SMS.java ),所以,当 SMS 发送后,什么事情也不会发生。
如果你需要监视 SMS 消息的发送过程状况,你可以使用两个 PendingIntent 对象以及两个 BroadcastReceiver 对象,像这样:
//---sends an SMS message to another device---
private void sendSMS( String phoneNumber, String message)
{
String SENT = "SMS_SENT" ;
String DELIVERED = "SMS_DELIVERED" ;
PendingIntent sentPI = PendingIntent. getBroadcast ( this , 0 ,
new Intent( SENT) , 0 );
PendingIntent deliveredPI = PendingIntent. getBroadcast ( this , 0 ,
new Intent( DELIVERED) , 0 );
//---when the SMS has been sent---
registerReceiver ( new BroadcastReceiver(){
@Override
public void onReceive( Context arg0, Intent arg1) {
switch ( getResultCode())
{
case Activity.RESULT_OK :
Toast. makeText ( getBaseContext() , "SMS sent" ,
Toast. LENGTH_SHORT ) .show ();
break ;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE :
Toast. makeText ( getBaseContext() , "Generic failure" ,
Toast. LENGTH_SHORT ) .show ();
break ;
case SmsManager.RESULT_ERROR_NO_SERVICE :
Toast. makeText ( getBaseContext() , "No service" ,
Toast. LENGTH_SHORT ) .show ();
break ;
case SmsManager.RESULT_ERROR_NULL_PDU :
Toast. makeText ( getBaseContext() , "Null PDU" ,
Toast. LENGTH_SHORT ) .show ();
break ;
case SmsManager.RESULT_ERROR_RADIO_OFF :
Toast. makeText ( getBaseContext() , "Radio off" ,
Toast. LENGTH_SHORT ) .show ();
break ;
}
}
} , new IntentFilter( SENT));
//---when the SMS has been delivered---
registerReceiver ( new BroadcastReceiver(){
@Override
public void onReceive( Context arg0, Intent arg1) {
switch ( getResultCode())
{
case Activity.RESULT_OK :
Toast. makeText ( getBaseContext() , "SMS delivered" ,
Toast. LENGTH_SHORT ) .show ();
break ;
case Activity.RESULT_CANCELED :
Toast. makeText ( getBaseContext() , "SMS not delivered" ,
Toast. LENGTH_SHORT ) .show ();
break ;
}
}
} , new IntentFilter( DELIVERED));
SmsManager sms = SmsManager. getDefault ();
sms. sendTextMessage ( phoneNumber, null , message, sentPI, deliveredPI);
}
上面的代码使用一个 PendingIntent 对象( sendPI )来监视发送过程。当 SMS 消息发送后,第一个 BroadcastReceiver 的 onReceive 事件会触发。在这里你可以检查发送过程的状态。第二个 PendingIntent 对象( deliveredPI )监视传送过程。当 SMS 消息成功送达时会触发第二个 BroadcastReceiver 的 onReceive 事件。
现在,你可以按下 F11 来测试应用程序了。为了从一个模拟器实例发送 SMS 消息到另一个实例,进入 SDK 的 Tools 文件夹并运行 Emulator.exe 来简单启动另一个 Android 模拟器实例。
图 3 发送 SMS 消息
图 3 演示了如何从一个模拟器发送 SMS 消息到另一个模拟器;使用目标模拟器的端口号(显示在窗口的左上角)作为它的电话号码。当 SMS 发送成功时,它会显示一个“ SMS sent ”消息。当它成功送达时,它会显示一个“ SMS delivered ”消息。注意,使用模拟器进行测试时,当 SMS 成功送达时,“ SMS delivered ”消息不会显示;它仅在真机上工作。
图 4 显示了接收模拟器中 SMS 消息接收后的样子。消息一开始显示在通知条上(屏幕的上方)。往下拖拽通知条,显示出接收到的消息。查看完整的消息,点击那个消息。
图 4 Android 模拟器接收 SMS 消息
如果你不想触及发送 SMS 消息的所有麻烦,你可以使用一个 Intent 对象来帮助你发送 SMS 消息。下面的代码就演示了如何引发内建的 SMS 程序来帮助你发送 SMS 消息:
Intent sendIntent = new Intent( Intent.ACTION_VIEW );
sendIntent.putExtra ( "sms_body" , "Content of the SMS goes here..." );
sendIntent.setType ( "vnd.android-dir/mms-sms" );
startActivity( sendIntent);
图 5 显示了内建的 SMS 应用程序发送 SMS 消息。
图 5 引发内建的 SMS 程序
接收SMS消息
除了可以程序化的发送 SMS 消息,你还可以使用一个 BroadcastReceiver 对象来拦截新的 SMS 消息。
想看如何在 Android 应用程序中接收 SMS 消息的话,可以在 AndroidManifest.xml 文件中添加 <receiver> 元素,这样,新来的 SMS 消息就能被 SmsReceiver 类拦截:
<?xml version = "1.0" encoding = "utf-8" ?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
package = "net.learn2develop.SMSMessaging"
android:versionCode = "1"
android:versionName = "1.0.0" >
<application android:icon = "@drawable/icon" android:label = "@string/app_name" >
<activity android:name = ".SMS"
android:label = "@string/app_name" >
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name = ".SmsReceiver" >
<intent-filter>
<action android:name =
"android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
<uses-permission android:name = "android.permission.SEND_SMS" >
</uses-permission>
<uses-permission android:name = "android.permission.RECEIVE_SMS" >
</uses-permission>
</manifest>
添加一个新的类文件并命名为 SmsReceiver.java (如图 6 )。
图 6 添加 SmsReceiver.java 文件到工程中
在 SmsReceiver 类中,扩展 BroadcastReceiver 类,并重写 onReceive 方法:
package net.learn2develop .SMSMessaging ;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class SmsReceiver extends BroadcastReceiver
{
@Override
public void onReceive( Context context, Intent intent)
{
}
}
当 SMS 消息接收后, onReceive() 方法会触发。 SMS 消息附着在 Intent 对象上( onReceive 方法的第二个参数, intent )的 Bundle 对象里。消息以 PDU 格式储存在一个对象数组里。要提取每个消息,可以使用 SmsMessage 类的 createFromPdu ()静态方法。然后,使用 Toast 类显示 SMS 消息:
package net.learn2develop .SMSMessaging ;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.gsm.SmsMessage;
import android.widget.Toast;
public class SmsReceiver extends BroadcastReceiver
{
@Override
public void onReceive( Context context, Intent intent)
{
//---get the SMS message passed in---
Bundle bundle = intent. getExtras ();
SmsMessage [] msgs = null ;
String str = "" ;
if ( bundle ! = null )
{
//---retrieve the SMS message received---
Object [] pdus = ( Object []) bundle.get ( "pdus" );
msgs = new SmsMessage[ pdus.length ];
for ( int i=0 ; i< msgs.length ; i++){
msgs [ i] = SmsMessage.createFromPdu (( byte []) pdus[ i]);
str += "SMS from " + msgs [ i] .getOriginatingAddress ();
str += " :" ;
str += msgs [ i] .getMessageBody () .toString ();
str += " /n " ;
}
//---display the new SMS message---
Toast. makeText ( context, str, Toast.LENGTH_SHORT ) .show ();
}
}
}