本文通過運行兩個Android模擬器,介紹在Android中如何實現簡訊服務(SMS,short message service)的功能。通過這個例子,我想帶給大家的是:更加熟悉之前介紹過的Android應用程式的概念及技術細節,且通過實例調度大家的興趣。我之所以選擇SMS為例子,主要原因是SMS已經非常成熟了,從中可以發掘更多的信息和技術細節,而且我相信大部分人發簡訊比打電話多。
本文的主要內容如下:
廣播接收者:一個廣播接收者是這樣一個組件,它不做什麼事,僅是接受廣播公告並作出相應的反應。許多廣播源自於系統代碼,例如公告時區的改變、電池電量低、已採取圖片、用戶改變了語言偏好。應用程式也可以發起廣播,例如為了他其他程序知道某些數據已經下載到設備且他們可以使用這些數據
BroadcastReceiver類:是接受sendBroadcast()發送的意圖(intents)的基類。可以用Context.registerReceiver()動態地註冊這個類的實例,或者通過AndroidManifest.xml中<receiver>標籤靜態發布。
廣播接收者不顯示一個用戶界面。然而,它們啟動一個活動去響應收到的信息,或者他們可能使用NotificationManager去通知用戶。通知可以使用多種方式獲得用戶的注意——閃爍的背光、振動設備、播放聲音等等。典型的是放在一個持久的圖標在狀態欄,用戶可以打開獲取信息。
2、準備工作:SMS涉及的主要類SmsManager實現SMS主要用到SmsManager類,該類繼承自java.lang.Object類,下面我們介紹一下該類的主要成員。
公有方法:
ArrayList<String> divideMessage(String text)常量:
public static final int RESULT_ERROR_GENERIC_FAILURE1)、首先,編輯布局文件res/layout/main.xml,達到我們想要的結果,界面如下:
圖1、程序運行界面
對應的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="@string/txtPhoneNo"/> <!-- text's value define in res/values/strings.xml --> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/edtPhoneNo"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/txtContent"/> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:minLines="3" android:id="@+id/edtContent"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btnText" android:id="@+id/btnSend"/></LinearLayout>
相應的要在res/values/strings.xm中添加上面定義的視圖的text的值,如下:
<?xml version="1.0" encoding="utf-8"?><resources> <string name="txtPhoneNo">Please input phone NO:</string> <string name="txtContent">Please input SMS\'s content:</string> <string name="btnText">send!</string> <string name="app_name">SMS</string></resources>
2)、做完這些準備工作之後,我麼要開始編寫代碼實現簡單的簡訊發送了。
通過第一步我們構建好界面之後,現在要在上面的基礎上編寫業務邏輯了。大致過程為:在java源文件中,獲取用戶在edtPhoneNo中輸入的電話號碼,edtContent中輸入要發送的內容;然後點擊btnSend按鈕發送簡訊,要達到這個目的我們要設置btnSend的OnClickListener以達到當點擊它觸發發送簡訊的功能,而且要發送簡訊就要用到我們前面介紹的SmsManager類提供的方法接口。
設置btnSend的OnClickListener的代碼如下:
btnSend.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String phoneNo = edtPhoneNo.getText().toString(); String message = edtContent.getText().toString(); if (phoneNo.length() > 0 && message.length() > 0){ //call sendSMS to send message to phoneNo sendSMS(phoneNo, message); } else Toast.makeText(getBaseContext(), "Please enter both phone number and message.", Toast.LENGTH_SHORT).show(); }});
發送簡訊的功能的代碼如下:
private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this,TextMessage.class), 0); //if message's length more than 70 , //then call divideMessage to dive message into several part //and call sendTextMessage() //else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, pi, null); } } else { sms.sendTextMessage(phoneNumber, null, message, pi, null); } Toast.makeText(TextMessage.this, "簡訊發送完成", Toast.LENGTH_LONG).show();}
如果你已經看了第2節介紹的SmsManager類的介紹,代碼應該很好理解。在這裡要說明的是,sendTextMessage方法中的第4個和第5個參數PendingIntent設為null,這樣的話不能根據簡訊發出之後的狀態做相應的事情,如簡訊發送失敗後的提醒、接收者成功接收後的回執……完整的流程源碼如下:
package skynet.com.cnblogs.www;import java.util.ArrayList;import android.app.Activity;import android.app.PendingIntent;import android.content.Intent;import android.os.Bundle;import android.telephony.SmsManager;import android.view.View;import android.widget.*;public class TextMessage extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnSend = (Button) findViewById(R.id.btnSend); edtPhoneNo = (EditText) findViewById(R.id.edtPhoneNo); edtContent = (EditText) findViewById(R.id.edtContent); btnSend.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String phoneNo = edtPhoneNo.getText().toString(); String message = edtContent.getText().toString(); if (phoneNo.length() > 0 && message.length() > 0) { // call sendSMS to send message to phoneNo sendSMS(phoneNo, message); } else Toast.makeText(getBaseContext(), "Please enter both phone number and message.", Toast.LENGTH_SHORT).show(); } }); } private Button btnSend; private EditText edtPhoneNo; private EditText edtContent; private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, TextMessage.class), 0); // if message's length more than 70 , // then call divideMessage to dive message into several part ,and call // sendTextMessage() // else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, pi, null); } } else { sms.sendTextMessage(phoneNumber, null, message, pi, null); } Toast.makeText(TextMessage.this, "簡訊發送完成", Toast.LENGTH_LONG).show(); }}
3)運行前,還要在清單文件AndroidManifest.xml中加入允許發送簡訊的權限:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="skynet.com.cnblogs.www" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".TextMessage" 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"/></manifest>
3.1、運行SMS程序給另一個android模擬器發簡訊運行上面我們編寫的TextMessage程序,另外在Windows的命令行下切換到tools目錄下,並輸入emulator –data smsReceiver,我的如下:
這樣就會啟動一個android模擬器,如下所示:(注意它的編號:5556,就是用這個編號與它通信的)
圖2、通過emulator啟動一個android模擬器
通過我們TextMessage程序啟動的android模擬器,編寫簡訊:
圖3、TextMessage程序個5556模擬器發簡訊
點擊發送之後,通過命令行啟動的5556號android模擬器會收到我們剛才發送的簡訊,如下所示:
圖4、收到簡訊的提示
tips:如果通過命令行的emulator啟動android模擬器提示「NO DNS servers found!」,這時我們發的簡訊模擬器是收不到的。
在Windows下,如果電腦沒有介入網絡,即找不DNS伺服器的話會出現這種情況!
在Mac下,如果提示這個警告的話,可以這樣解決:檢查你是否有 /etc/resolv.conf文件,如果沒有的話,通過下面的命令行
ln -s /private/var/run/resolv.conf /etc/resolv.conf可以解決。
上面我們實現了一個簡單的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。下面我們就實現和註冊這個廣播接收者:
String SENT_SMS_ACTION="SENT_SMS_ACTION";String DELIVERED_SMS_ACTION="DELIVERED_SMS_ACTION";//create the sentIntent parameter Intent sentIntent=new Intent(SENT_SMS_ACTION);PendingIntent sentPI=PendingIntent.getBroadcast( this, 0, sentIntent, 0);//create the deilverIntent parameter Intent deliverIntent=new Intent(DELIVERED_SMS_ACTION);PendingIntent deliverPI=PendingIntent.getBroadcast( this, 0, deliverIntent, 0);//register the Broadcast Receivers registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context _context,Intent _intent) { switch(getResultCode()){ case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS sent success actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(getBaseContext(), "SMS generic failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(getBaseContext(), "SMS radio off failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(getBaseContext(), "SMS null PDU failure actions", Toast.LENGTH_SHORT).show(); break; } }},new IntentFilter(SENT_SMS_ACTION));registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context _context,Intent _intent) { Toast.makeText(getBaseContext(), "SMS delivered actions", Toast.LENGTH_SHORT).show(); }},new IntentFilter(DELIVERED_SMS_ACTION));
在基本完成了要做的工作,接下來要做的就是將sendTextMessage的第4個和第5個參數改為sentPI、deliverPI,這樣工作基本完成,修改後的sendSMS方法如下:
private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); String SENT_SMS_ACTION = "SENT_SMS_ACTION"; String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; // create the sentIntent parameter Intent sentIntent = new Intent(SENT_SMS_ACTION); PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent, 0); // create the deilverIntent parameter Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); PendingIntent deliverPI = PendingIntent.getBroadcast(this, 0, deliverIntent, 0); // register the Broadcast Receivers registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS sent success actions", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(getBaseContext(), "SMS generic failure actions", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast .makeText(getBaseContext(), "SMS radio off failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(getBaseContext(), "SMS null PDU failure actions", Toast.LENGTH_SHORT) .show(); break; } } }, new IntentFilter(SENT_SMS_ACTION)); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { Toast.makeText(getBaseContext(), "SMS delivered actions", Toast.LENGTH_SHORT).show(); } }, new IntentFilter(DELIVERED_SMS_ACTION)); // if message's length more than 70 , // then call divideMessage to dive message into several part ,and call // sendTextMessage() // else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, sentPI, deliverPI); } } else { sms.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI); }}
運行之後的,發送簡訊成功的話就可以看到如下界面:
圖5、增強SMS(一)
閱讀:Android 簡訊的收發及在android模擬器之間實踐(二)