Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

引言

本文通過運行兩個Android模擬器,介紹在Android中如何實現短信服務(SMS,short message service)的功能。通過這個例子,我想帶給大家的是:更加熟悉之前介紹過的Android應用程序的概念及技術細節,且通過實例調度大家的興趣。我之所以選擇SMS爲例子,主要原因是SMS已經非常成熟了,從中可以發掘更多的信息和技術細節,而且我相信大部分人發短信比打電話多。

本文的主要內容如下:

  1. 1、溫故知新
  2. 2、準備工作:SMS涉及的主要類SmsManager
  3. 3、簡單的SMS發送程序
    1. 3.1、運行SMS程序給另一個android模擬器發短
  4. 4、SMS增強(一)
  5. 5、SMS增強(二)
  6. 6、SMS接收程序(下篇)
  7. 7、emulator工具(下篇)
  8. 8、…

1、溫故知新

廣播接收者:一個廣播接收者是這樣一個組件,它不做什麼事,僅是接受廣播公告並作出相應的反應。許多廣播源自於系統代碼,例如公告時區的改變、電池電量低、已採取圖片、用戶改變了語言偏好。應用程序也可以發起廣播,例如爲了他其他程序知道某些數據已經下載到設備且他們可以使用這些數據

BroadcastReceiver類:是接受sendBroadcast()發送的意圖(intents)的基類。可以用Context.registerReceiver()動態地註冊這個類的實例,或者通過AndroidManifest.xml中<receiver>標籤靜態發佈。

廣播接收者不顯示一個用戶界面。然而,它們啓動一個活動去響應收到的信息,或者他們可能使用NotificationManager去通知用戶。通知可以使用多種方式獲得用戶的注意——閃爍的背光、振動設備、播放聲音等等。典型的是放在一個持久的圖標在狀態欄,用戶可以打開獲取信息。

2、準備工作:SMS涉及的主要類SmsManager

實現SMS主要用到SmsManager類,該類繼承自java.lang.Object類,下面我們介紹一下該類的主要成員。

公有方法:

  1. ArrayList<String> divideMessage(String text)
    當短信超過SMS消息的最大長度時,將短信分割爲幾塊。
    參數text——初始的消息,不能爲空
    返回值:有序的ArrayList<String>,可以重新組合爲初始的消息
  2. static SmsManager getDefault()
    獲取SmsManager的默認實例。
    返回值SmsManager的默認實例
  3. void SendDataMessage(String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)
    發送一個基於SMS的數據到指定的應用程序端口。
    參數
    1)、destinationAddress——消息的目標地址
    2)、scAddress——服務中心的地址or爲空使用當前默認的SMSC 3)destinationPort——消息的目標端口號
    4)、data——消息的主體,即消息要發送的數據
    5)、sentIntent——如果不爲空,當消息成功發送或失敗這個PendingIntent就廣播。結果代碼是Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示錯誤。對應RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括額外的“錯誤代碼”包含一個無線電廣播技術特定的值,通常只在修復故障時有用。
    每一個基於SMS的應用程序控制檢測sentIntent。如果sentIntent是空,調用者將檢測所有未知的應用程序,這將導致在檢測的時候發送較小數量的SMS。
    6)、deliveryIntent——如果不爲空,當消息成功傳送到接收者這個PendingIntent就廣播。
    異常:如果destinationAddressdata是空時,拋出IllegalArgumentException異常。
  4. void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent>  deliverIntents)
    發送一個基於SMS的多部分文本,調用者應用已經通過調用divideMessage(String text)將消息分割成正確的大小。
    參數
    1)、destinationAddress——消息的目標地址
    2)、scAddress——服務中心的地址or爲空使用當前默認的SMSC
    3)、parts——有序的ArrayList<String>,可以重新組合爲初始的消息
    4)、sentIntents——跟SendDataMessage方法中一樣,只不過這裏的是一組PendingIntent
    5)、deliverIntents——跟SendDataMessage方法中一樣,只不過這裏的是一組PendingIntent
    異常:如果destinationAddressdata是空時,拋出IllegalArgumentException異常。
  5. void sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)
    發送一個基於SMS的文本。參數的意義和異常前面的已存在的一樣,不再累述。

常量:

  1. public static final int RESULT_ERROR_GENERIC_FAILURE
    表示普通錯誤,值爲1(0x00000001)
  2. public static final int RESULT_ERROR_NO_SERVICE
    表示服務當前不可用,值爲4 (0x00000004)
  3. public static final int RESULT_ERROR_NULL_PDU
    表示沒有提供pdu,值爲3 (0x00000003)
  4. public static final int RESULT_ERROR_RADIO_OFF
    表示無線廣播被明確地關閉,值爲2 (0x00000002)
  5. public static final int STATUS_ON_ICC_FREE
    表示自由空間,值爲0 (0x00000000)
  6. public static final int STATUS_ON_ICC_READ
    表示接收且已讀,值爲1 (0x00000001)
  7. public static final int STATUS_ON_ICC_SENT
    表示存儲且已發送,值爲5 (0x00000005)
  8. public static final int STATUS_ON_ICC_UNREAD
    表示接收但未讀,值爲3 (0x00000003)
  9. public static final int STATUS_ON_ICC_UNSENT
    表示存儲但爲發送,值爲7 (0x00000007)

3、簡單的SMS發送程序

1)、首先,編輯佈局文件res/layout/main.xml,達到我們想要的結果,界面如下:

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

圖1、程序運行界面

 對應的xml代碼如下:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:orientation="vertical"   
  4.     android:layout_width="fill_parent" 
  5.     android:layout_height="fill_parent" > 
  6.     <TextView android:layout_width="fill_parent" 
  7.             android:layout_height="wrap_content"   
  8.             android:text="@string/txtPhoneNo"/>    <!-- text's value define in res/values/strings.xml --> 
  9.       
  10.     <EditText android:layout_width="fill_parent" 
  11.             android:layout_height="wrap_content" 
  12.             android:id="@+id/edtPhoneNo"/> 
  13.       
  14.     <TextView android:layout_width="fill_parent" 
  15.             android:layout_height="wrap_content" 
  16.             android:text="@string/txtContent"/> 
  17.       
  18.     <EditText android:layout_width="fill_parent" 
  19.             android:layout_height="wrap_content" 
  20.             android:minLines="3" 
  21.             android:id="@+id/edtContent"/> 
  22.      
  23.     <Button android:layout_width="wrap_content" 
  24.             android:layout_height="wrap_content" 
  25.             android:text="@string/btnText" 
  26.             android:id="@+id/btnSend"/> 
  27. </LinearLayout> 

相應的要在res/values/strings.xm中添加上面定義的視圖的text的值,如下:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <resources> 
  3.     <string name="txtPhoneNo">Please input phone NO:</string> 
  4.     <string name="txtContent">Please input SMS\'s content:</string> 
  5.     <string name="btnText">send!</string> 
  6.     <string name="app_name">SMS</string> 
  7. </resources> 

2)、做完這些準備工作之後,我麼要開始編寫代碼實現簡單的短信發送了。

通過第一步我們構建好界面之後,現在要在上面的基礎上編寫業務邏輯了。大致過程爲:在java源文件中,獲取用戶在edtPhoneNo中輸入的電話號碼,edtContent中輸入要發送的內容;然後點擊btnSend按鈕發送短信,要達到這個目的我們要設置btnSend的OnClickListener以達到當點擊它觸發發送短信的功能,而且要發送短信就要用到我們前面介紹的SmsManager類提供的方法接口。

設置btnSend的OnClickListener的代碼如下:

  1. btnSend.setOnClickListener(new View.OnClickListener() {  
  2.     public void onClick(View v) {  
  3.         String phoneNo = edtPhoneNo.getText().toString();  
  4.         String message = edtContent.getText().toString();  
  5.         if (phoneNo.length() > 0 && message.length() > 0){  
  6.             //call sendSMS to send message to phoneNo  
  7.             sendSMS(phoneNo, message);  
  8.         }  
  9.         else 
  10.             Toast.makeText(getBaseContext(),  
  11.                 "Please enter both phone number and message.",  
  12.                 Toast.LENGTH_SHORT).show();  
  13.     }  
  14. }); 

發送短信的功能的代碼如下:

  1. private void sendSMS(String phoneNumber, String message) {  
  2.     // ---sends an SMS message to another device---  
  3.     SmsManager sms = SmsManager.getDefault();  
  4.     PendingIntent pi = PendingIntent.getActivity(this0,                new Intent(this,TextMessage.class), 0);  
  5.     //if message's length more than 70 ,  
  6.     //then call divideMessage to dive message into several part         //and call sendTextMessage()  
  7.     //else direct call sendTextMessage()  
  8.     if (message.length() > 70) {  
  9.         ArrayList<String> msgs = sms.divideMessage(message);  
  10.         for (String msg : msgs) {  
  11.             sms.sendTextMessage(phoneNumber, null, msg, pi, null);  
  12.         }  
  13.     } else {  
  14.         sms.sendTextMessage(phoneNumber, null, message, pi, null);  
  15.     }  
  16.     Toast.makeText(TextMessage.this"短信發送完成", Toast.LENGTH_LONG).show();  

如果你已經看了第2節介紹的SmsManager類的介紹,代碼應該很好理解。在這裏要說明的是,sendTextMessage方法中的第4個和第5個參數PendingIntent設爲null,這樣的話不能根據短信發出之後的狀態做相應的事情,如短信發送失敗後的提醒、接收者成功接收後的回執……完整的流程源碼如下:

  1. package skynet.com.cnblogs.www;  
  2.  
  3. import java.util.ArrayList;  
  4.  
  5. import android.app.Activity;  
  6. import android.app.PendingIntent;  
  7. import android.content.Intent;  
  8. import android.os.Bundle;  
  9. import android.telephony.SmsManager;  
  10. import android.view.View;  
  11. import android.widget.*;  
  12.  
  13. public class TextMessage extends Activity {  
  14.     /** Called when the activity is first created. */ 
  15.     @Override 
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.  
  19.         setContentView(R.layout.main);  
  20.         btnSend = (Button) findViewById(R.id.btnSend);  
  21.         edtPhoneNo = (EditText) findViewById(R.id.edtPhoneNo);  
  22.         edtContent = (EditText) findViewById(R.id.edtContent);  
  23.  
  24.         btnSend.setOnClickListener(new View.OnClickListener() {  
  25.             public void onClick(View v) {  
  26.                 String phoneNo = edtPhoneNo.getText().toString();  
  27.                 String message = edtContent.getText().toString();  
  28.                 if (phoneNo.length() > 0 && message.length() > 0) {  
  29.                     // call sendSMS to send message to phoneNo  
  30.                     sendSMS(phoneNo, message);  
  31.                 } else 
  32.                     Toast.makeText(getBaseContext(),  
  33.                             "Please enter both phone number and message.",  
  34.                             Toast.LENGTH_SHORT).show();  
  35.             }  
  36.         });  
  37.     }  
  38.  
  39.     private Button btnSend;  
  40.     private EditText edtPhoneNo;  
  41.     private EditText edtContent;  
  42.  
  43.     private void sendSMS(String phoneNumber, String message) {  
  44.         // ---sends an SMS message to another device---  
  45.         SmsManager sms = SmsManager.getDefault();  
  46.         PendingIntent pi = PendingIntent.getActivity(this0new Intent(this,  
  47.                 TextMessage.class), 0);  
  48.         // if message's length more than 70 ,  
  49.         // then call divideMessage to dive message into several part ,and call  
  50.         // sendTextMessage()  
  51.         // else direct call sendTextMessage()  
  52.         if (message.length() > 70) {  
  53.             ArrayList<String> msgs = sms.divideMessage(message);  
  54.             for (String msg : msgs) {  
  55.                 sms.sendTextMessage(phoneNumber, null, msg, pi, null);  
  56.             }  
  57.         } else {  
  58.             sms.sendTextMessage(phoneNumber, null, message, pi, null);  
  59.         }  
  60.         Toast.makeText(TextMessage.this"短信發送完成", Toast.LENGTH_LONG).show();  
  61.     }  

3)運行前,還要在清單文件AndroidManifest.xml中加入允許發送短信的權限:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
  3.       package="skynet.com.cnblogs.www" 
  4.       android:versionCode="1" 
  5.       android:versionName="1.0"> 
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name"> 
  7.         <activity android:name=".TextMessage" 
  8.                   android:label="@string/app_name"> 
  9.             <intent-filter> 
  10.                 <action android:name="android.intent.action.MAIN" /> 
  11.                 <category android:name="android.intent.category.LAUNCHER" /> 
  12.             </intent-filter> 
  13.         </activity> 
  14.  
  15.     </application> 
  16.     <uses-permission android:name="android.permission.SEND_SMS"/> 
  17. </manifest>  

3.1、運行SMS程序給另一個android模擬器發短信

運行上面我們編寫的TextMessage程序,另外在Windows的命令行下切換到tools目錄下,並輸入emulator –data smsReceiver,我的如下:

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

這樣就會啓動一個android模擬器,如下所示:(注意它的編號:5556,就是用這個編號與它通信的

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

圖2、通過emulator啓動一個android模擬器

通過我們TextMessage程序啓動的android模擬器,編寫短信:

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

圖3、TextMessage程序個5556模擬器發短信

點擊發送之後,通過命令行啓動的5556號android模擬器會收到我們剛纔發送的短信,如下所示:

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

圖4、收到短信的提示

tips:

如果通過命令行的emulator啓動android模擬器提示“NO DNS servers found!”,這時我們發的短信模擬器是收不到的。

  1. 在Windows下,如果電腦沒有介入網絡,即找不DNS服務器的話會出現這種情況!
  2. 在Mac下,如果提示這個警告的話,可以這樣解決:檢查你是否有/etc/resolv.conf文件,如果沒有的話,通過下面的命令行
    ln -s /private/var/run/resolv.conf /etc/resolv.conf可以解決。

4、SMS增強(一)

上面我們實現了一個簡單的SMS程序,下面我們要對它進行增強!你肯定已經注意到了,我們上面的SMS程序的sendTextMessage方法中的第4個和第5個參數PendingIntent設爲null,即sentIntent和deliveryIntent。

第4個參數-sendIntent,當消息成功發送或發送失敗都將被觸發。廣播接收者的結果碼,Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示錯誤。對應RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括額外的“錯誤代碼”包含一個無線電廣播技術特定的值,通常只在修復故障時有用。第5個參數-deliveryIntent,僅當目標接收到你的SMS消息才觸發。

爲了跟蹤發出的短信的狀態,實現和註冊Broadcast Receiver(廣播接收者)監聽傳遞給sendTextMessage方法的參數Pending Intents。下面我們就實現和註冊這個廣播接收者:

  1. String SENT_SMS_ACTION="SENT_SMS_ACTION";  
  2. String DELIVERED_SMS_ACTION="DELIVERED_SMS_ACTION";  
  3.  
  4. //create the sentIntent parameter  
  5. Intent sentIntent=new Intent(SENT_SMS_ACTION);  
  6. PendingIntent sentPI=PendingIntent.getBroadcast(  
  7.         this,  
  8.         0,   
  9.         sentIntent,   
  10.         0);  
  11.  
  12. //create the deilverIntent parameter  
  13. Intent deliverIntent=new Intent(DELIVERED_SMS_ACTION);  
  14. PendingIntent deliverPI=PendingIntent.getBroadcast(  
  15.         this,  
  16.         0,   
  17.         deliverIntent,   
  18.         0);  
  19.  
  20. //register the Broadcast Receivers  
  21. registerReceiver(new BroadcastReceiver(){  
  22.     @Override 
  23.     public void onReceive(Context _context,Intent _intent)  
  24.     {  
  25.         switch(getResultCode()){  
  26.             case Activity.RESULT_OK:  
  27.                 Toast.makeText(getBaseContext(),   
  28.                         "SMS sent success actions",  
  29.                         Toast.LENGTH_SHORT).show();  
  30.                 break;  
  31.             case SmsManager.RESULT_ERROR_GENERIC_FAILURE:  
  32.                 Toast.makeText(getBaseContext(),   
  33.                         "SMS generic failure actions",  
  34.                         Toast.LENGTH_SHORT).show();  
  35.                 break;  
  36.             case SmsManager.RESULT_ERROR_RADIO_OFF:  
  37.                 Toast.makeText(getBaseContext(),  
  38.                         "SMS radio off failure actions",  
  39.                         Toast.LENGTH_SHORT).show();  
  40.                 break;  
  41.             case SmsManager.RESULT_ERROR_NULL_PDU:  
  42.                 Toast.makeText(getBaseContext(),   
  43.                         "SMS null PDU failure actions",  
  44.                         Toast.LENGTH_SHORT).show();  
  45.                 break;  
  46.         }  
  47.     }  
  48. },  
  49. new IntentFilter(SENT_SMS_ACTION));  
  50. registerReceiver(new BroadcastReceiver(){  
  51.     @Override 
  52.     public void onReceive(Context _context,Intent _intent)  
  53.     {  
  54.         Toast.makeText(getBaseContext(),   
  55.                 "SMS delivered actions",  
  56.                 Toast.LENGTH_SHORT).show();               
  57.     }  
  58. },  
  59. new IntentFilter(DELIVERED_SMS_ACTION)); 

在基本完成了要做的工作,接下來要做的就是將sendTextMessage的第4個和第5個參數改爲sentPI、deliverPI,這樣工作基本完成,修改後的sendSMS方法如下:

  1. private void sendSMS(String phoneNumber, String message) {  
  2.     // ---sends an SMS message to another device---  
  3.     SmsManager sms = SmsManager.getDefault();  
  4.     String SENT_SMS_ACTION = "SENT_SMS_ACTION";  
  5.     String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION";  
  6.  
  7.     // create the sentIntent parameter  
  8.     Intent sentIntent = new Intent(SENT_SMS_ACTION);  
  9.     PendingIntent sentPI = PendingIntent.getBroadcast(this0, sentIntent,  
  10.             0);  
  11.  
  12.     // create the deilverIntent parameter  
  13.     Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION);  
  14.     PendingIntent deliverPI = PendingIntent.getBroadcast(this0,  
  15.             deliverIntent, 0);  
  16.  
  17.     // register the Broadcast Receivers  
  18.     registerReceiver(new BroadcastReceiver() {  
  19.         @Override 
  20.         public void onReceive(Context _context, Intent _intent) {  
  21.             switch (getResultCode()) {  
  22.             case Activity.RESULT_OK:  
  23.                 Toast.makeText(getBaseContext(),  
  24.                         "SMS sent success actions", Toast.LENGTH_SHORT)  
  25.                         .show();  
  26.                 break;  
  27.             case SmsManager.RESULT_ERROR_GENERIC_FAILURE:  
  28.                 Toast.makeText(getBaseContext(),  
  29.                         "SMS generic failure actions", Toast.LENGTH_SHORT)  
  30.                         .show();  
  31.                 break;  
  32.             case SmsManager.RESULT_ERROR_RADIO_OFF:  
  33.                 Toast  
  34.                         .makeText(getBaseContext(),  
  35.                                 "SMS radio off failure actions",  
  36.                                 Toast.LENGTH_SHORT).show();  
  37.                 break;  
  38.             case SmsManager.RESULT_ERROR_NULL_PDU:  
  39.                 Toast.makeText(getBaseContext(),  
  40.                         "SMS null PDU failure actions", Toast.LENGTH_SHORT)  
  41.                         .show();  
  42.                 break;  
  43.             }  
  44.         }  
  45.     }, new IntentFilter(SENT_SMS_ACTION));  
  46.     registerReceiver(new BroadcastReceiver() {  
  47.         @Override 
  48.         public void onReceive(Context _context, Intent _intent) {  
  49.             Toast.makeText(getBaseContext(), "SMS delivered actions",  
  50.                     Toast.LENGTH_SHORT).show();  
  51.         }  
  52.     }, new IntentFilter(DELIVERED_SMS_ACTION));  
  53.  
  54.     // if message's length more than 70 ,  
  55.     // then call divideMessage to dive message into several part ,and call  
  56.     // sendTextMessage()  
  57.     // else direct call sendTextMessage()  
  58.     if (message.length() > 70) {  
  59.         ArrayList<String> msgs = sms.divideMessage(message);  
  60.         for (String msg : msgs) {  
  61.             sms.sendTextMessage(phoneNumber, null, msg, sentPI, deliverPI);  
  62.         }  
  63.     } else {  
  64.         sms.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI);  
  65.     }  

運行之後的,發送短信成功的話就可以看到如下界面:

Android 開發之旅:短信的收發及在android模擬器之間實踐(一)

圖5、增強SMS(一)

5、SMS增強(二)

下面這個增強是使SMS能夠發送二進制數據。要發送數據要使用SmsManager類的sendDataMessage方法,跟sendTextMessage方法類似,只不過該方法多了一個目標端口的參數,構建該SMS的過程跟前面的類似這裏就不在累述。

 

本系列的其它文章:

  1. Android 開發之旅:環境搭建及HelloWorld
  2. Android 開發之旅:HelloWorld項目的目錄結構
  3. Android 開發之旅:android架構
  4. Android 開發之旅:應用程序基礎及組件
  5. Android 開發之旅:應用程序基礎及組件(續)
  6. Android 開發之旅:活動與任務
  7. Android 開發之旅:進程與線程
  8. Android 開發之旅:組件生命週期(一)
  9. Android 開發之旅:組件生命週期(二)
  10. Android 開發之旅:組件生命週期(三)
  11. Android 開發之旅:又見Hello World!
  12. Android 開發之旅:深入分析佈局文件&又是"Hello World!"
  13. Android 開發之旅:view的幾種佈局方式及實踐
  14. Android 開發之旅:短信的收發及在android模擬器之間實踐(一)
  15. 請期待Android 開發之旅:短信的收發及在android模擬器之間實踐(二)…
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章