目錄結構:
1.獲取短信
在AndroidManifest.xml中,添加權限:
<uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.READ_SMS"/>
MainActivity.java
public class MainActivity extends Activity { private List<Map<String,String>> list=new ArrayList<Map<String,String>>(); private Uri SMS_INBOX = Uri.parse("content://sms/"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSmsFromPhone(); for(Map map:list){ Log.i(map.get("num").toString(),map.get("mess").toString()); } } public void getSmsFromPhone() { ContentResolver cr = getContentResolver(); String[] projection = new String[] {"_id", "address", "person","body", "date", "type" }; Cursor cur = cr.query(SMS_INBOX, projection, null, null, "date desc"); if (null == cur) { Log.i("ooc","************cur == null"); return; } while(cur.moveToNext()) { String number = cur.getString(cur.getColumnIndex("address"));//手機號 String name = cur.getString(cur.getColumnIndex("person"));//聯系人姓名列表 String body = cur.getString(cur.getColumnIndex("body"));//短信內容 //至此就獲得了短信的相關的內容, 以下是把短信加入map中,構建listview,非必要。 Map<String,String> map = new HashMap<String,String>(); map.put("num",number); map.put("mess",body); list.add(map); } } }
也可以獲取其他的屬性,如下:
String number = cur.getString(cur.getColumnIndex("想獲得的屬性")); //獲取方法
SMS的結果字段,讀者可以通過getColumnNames()來獲取所有的結構字段,下面讀者列出常用的結構字段:
_id:短信序號,如100 thread_id:對話的序號,如100,與同一個手機號互發的短信,其序號是相同的 address:發件人地址,即手機號,如+8613811810000 person:發件人,如果發件人在通訊錄中則為具體姓名,陌生人為null date:日期,long型,如1256539465022,可以對日期顯示格式進行設置 protocol:協議0SMS_RPOTO短信,1MMS_PROTO彩信 read:是否閱讀0未讀,1已讀 status:短信狀態-1接收,0complete,64pending,128failed type:短信類型1是接收到的,2是已發出 body:短信具體內容
接下來是一個Demo。這個Demo的原理為:首先向程序中注冊一個廣播接收器,重寫里面的onChange方法,當程序監聽到內容改變的時候,就獲取其中的短信,然后將短信內容存儲一個集合中。當點擊“獲取最新短信”的按鈕的時候,再把內容顯示出來。
SmsObserver.java文件
package com.example.smsobervable; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; public class SmsObserver extends ContentObserver{ Context context; public SmsObserver(Context context,Handler handler){ super(handler); this.context=context; } @Override public void onChange(boolean selfChange) { Object localObject = Uri.parse("content://sms"); String paramBoolean_a = ""; ContentResolver resolver =context.getContentResolver(); Cursor paramBoolean_b = resolver.query((Uri)localObject, null, paramBoolean_a, null, "date desc");//獲得一個游標 if (paramBoolean_b != null && paramBoolean_b.getCount() >0) { boolean haveData = paramBoolean_b.moveToFirst(); for(int i=0;i<1 && haveData;i++)//每次只取第一條 { try { String[] strs=paramBoolean_b.getColumnNames();//獲取當前游標下行的所有的列的名稱 for(String s:strs){ int index=paramBoolean_b.getColumnIndex(s);//獲得下標 String result=paramBoolean_b.getString(index);//獲得數據 MainActivity.smsContainer.add(s+":"+result); } haveData = paramBoolean_b.moveToNext(); } catch(Exception ex) { } } } paramBoolean_b.close(); } }
MainActivity.java文件
package com.example.smsobervable; import java.util.ArrayList; import java.util.List; import java.util.UUID; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.content.Context; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; public class MainActivity extends Activity { static List<String> smsContainer=new ArrayList<String>(); LinearLayout mainLayout; Context context; int btnId=UUID.randomUUID().variant();//創建一個隨機數 int tvid=UUID.randomUUID().variant();//創建一個隨機數 TextView tv; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mainLayout= (LinearLayout)findViewById(R.id.main_layout); context=this; SmsObserver smsObserver=new SmsObserver(context, handler);//創建一個廣播 接受者 context.getContentResolver().registerContentObserver(Uri.parse("content://sms/"),true,smsObserver);//注冊廣播 Button btn= addBtn("獲取最新短信",btnId); tv=addTextView("",tvid); //創建一個ScrolView btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { //將消息顯示出來 for(String str : smsContainer){ tv.append("\n"+str); } if(smsContainer.size()<1){ tv.append("\n沒接受到消息"); } } }); } /** * 添加按鈕 * @param text 按鈕的文本內容 * @param id 按鈕的id * @return 一個Button類型的屬性,表示創建號的按鈕。 */ Button addBtn(String text,int id){ Button btn=new Button(context); btn.setText(text); btn.setId(id); mainLayout.addView(btn);//將按鈕添加到布局中 return btn;//返回按鈕 } /** * 添加文本,具有滾動條 * @param text 文本 * @param id id * @return 一個TextView類型的數據,表示添加的文本對象。 */ TextView addTextView(String text,int id){ TextView tv=new TextView(context); tv.append(text); tv.setId(id); ScrollView scv=new ScrollView(context);//創建一個滾動條 scv.addView(tv);//將文本添加到滾動條中 mainLayout.addView(scv);//再建滾動條添加到布局中 return tv;//返回文本 } }
Activity_main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <LinearLayout android:id="@+id/main_layout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > </LinearLayout> </RelativeLayout>
AndroidManifest.java
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.smsobervable" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.READ_SMS"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.smsobervable.MainActivity" 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> </manifest>
讀者向其發送一個短信,然后再點擊“獲取最新短信”的按鈕,那么就可以在屏幕上顯示剛才發送的短信了。效果圖如下:

在上面的程序中,讀者也許已經發現,通過 getContentResolver(); 方法獲得ContentResolver,然后再獲得短信內容,這里獲得的是手機中所有的短信內容。如果有一項業務需求是自動填充驗證碼,那么利用上面這種方法實現就會有點繁瑣,下面介紹一下實現的自動填充驗證碼的思路:
1.首先必需注冊一個廣播,
2.在廣播中調用getContentResolver()方法,獲得所有短信,也可以只獲得第一條(如果該段時間內可以確保客戶端只會接受一條短信,那么可以用這種方法,但是在實際業務中是不能確定當前時刻客戶端收到了幾條短信)。
3.通過時間和短信號碼,確定出短信內容。
利用上面思路實現短信驗證碼自動填充的難點就是如何確定當前時刻客戶端收到了到多少條短信,這種情況下一般不會只獲取第一條短信,而是前好幾條,因為一般情況下不能確定客戶端中第一條短信是我們發送過去的,必須多獲得幾條,然后篩選。
如果說,獲得的短信內容就是新發送過來的,那么就解決了上面的問題了。利用Intent對象獲取的短信內容就只是新發來的,
代碼為:
private SmsReciver smsReciver = new SmsReciver(); /** 收到短信Action **/ String ACTION_SMS_RECIVER = "android.provider.Telephony.SMS_RECEIVED"; /** * 注冊廣播接受者監聽短信驗證碼自動回寫 可在onCreate()中進行注冊; */ private void registSmsReciver() { IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_SMS_RECIVER); // 設置優先級 不然監聽不到短信 filter.setPriority(1000); registerReceiver(smsReciver, filter); } /** * 短信廣播接受者 用戶監聽短信,自動填寫驗證碼 */ private class SmsReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Object[] objs = (Object[]) intent.getExtras().get("pdus"); for (Object obj : objs) { byte[] pdu = (byte[]) obj; SmsMessage sms = SmsMessage.createFromPdu(pdu); // 短信的內容 String message = sms.getMessageBody(); Log.d("log", "message " + message); // 短息的手機號,如果你們公司發送驗證碼的號碼是固定的這里可以進行一個號碼的校驗 String from = sms.getOriginatingAddress(); Log.d("log", "from " + from); analysisVerify(message); } } } /** * 解析短信並且回寫 這里解析的是純數字的短信,如果小伙伴的驗證碼包含字母的話,可用正則替換 * * @param message */ private void analysisVerify(String message) { char[] msgs = message.toCharArray(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < msgs.length; i++) { if ('0' <= msgs[i] && msgs[i] <= '9') { sb.append(msgs[i]); } } mEtVerifyCode.setText(sb.toString()); } @Override protected void onDestroy() { super.onDestroy(); // 取消短信廣播注冊 if (smsReciver != null) { unregisterReceiver(smsReciver); smsReciver = null; } }
在這里需要注意三點,
第一點:上面的程序中,利用循環Object[]對象,然后再利用每一個Object創建一個SmsMessage然后獲得內容,這里需要注意的就是,SmsMessage中的內容可能並不是一個完整的短信內容,因為如果短信的長度太長的話,就會截斷。
第二點:在OnReceive方法的傳入參數Intent對象中,除了封裝有“pdus”的數據,還有“format”,"slot","phone","rTime",'subscription'五項數據,通過傳入的數據我們知道了,每當Android手機收到一條短信后,就會立即發送一條廣播,並且會把當前短信內容封裝到Intent對象中,一同通過廣播發送出去。
通過一二點我們知道了,如果短信長度長度過長,可以使用如下的方法來重寫onReceive方法來獲取完整的短信:
//電話,筆者測試總為0 Integer phone = (Integer) intent.getExtras().get("phone"); //格式信息,有3gpp2和3gpp String format = (String) intent.getExtras().get("format"); String sloty = (String) intent.getExtras().get("sloty"); //時間 Long rTime = (Long) intent.getExtras().get("rTime"); //描述,筆者測試總為5 Integer subscription = (Integer) intent.getExtras().get( "subscription"); Object[] objs = (Object[]) intent.getExtras().get("pdus"); String messageInfo = null; for (Object obj : objs) { byte[] pdu = (byte[]) obj; int lh = pdu.length; SmsMessage sms = SmsMessage.createFromPdu(pdu); String message = sms.getMessageBody(); messageInfo += message; } // 對完整的短信內容進行驗證 analysisVerify(messageInfo);
第三點,就業務而言,我們監聽短信只會在輸入驗證碼的Activity里面才會用到,采用代碼注冊的形式,在當前Activity銷毀的時候取消廣播注冊,更符合我們的預期,提高應用的性能。
第四點,優先級的問題
filter.setPriority(1000);
可以看到,我們這里把優先級設置成了最大。保證我們的應用能夠盡可能的接受到短信。注意,我使用的是“盡可能”,也就是說我們不能保證短信自動填寫一定能執行成功,有個小伙伴可能會問,我們不是把優先級設置成了最高了嗎?為什么還不能保證了? 原因其實很簡單,你能把監聽短信的優先級設置成最大,同樣的,其他的應用也能把短信監聽的優先級設置成最大。比如說,你的手機安裝有360安全衛士,把你們公司的驗證碼視為垃圾短信攔截了,這個時候短信攔截就失效了。
2.發送短信
第一種:調用系統短信接口直接發送短信;主要代碼如下:
/** * 直接調用短信接口發短信 * @param phoneNumber * @param message */ public void sendSMS(String phoneNumber,String message){ //獲取短信管理器 android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault(); smsManager.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI); }
第二種:如果短信的長度超過了發送了發送限制,那么可以使用如下的方法:
/** * 直接調用短信接口發短信 * @param phoneNumber * @param message */ public void sendMSM(String phoneNumber, String message) { android.telephony.SmsManager smsManager = android.telephony.SmsManager .getDefault(); ArrayList<String> divideContents = smsManager.divideMessage(message); ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(); ArrayList<PendingIntent> deliverIntents = new ArrayList<PendingIntent>(); for (String text : divideContents) { sentIntents.add(sentPI); deliverIntents.add(deliverPI); } smsManager.sendMultipartTextMessage(phoneNumber, null, divideContents, sentIntents, deliverIntents); }
別忘了權限,記得在AndroidMannifest.xml中設置權限:
<uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
這種方法可以監控發送狀態和對方接收狀態。
處理返回的發送狀態:
//處理返回的發送狀態 String SENT_SMS_ACTION = "SENT_SMS_ACTION"; Intent sentIntent = new Intent(SENT_SMS_ACTION); PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, sentIntent, 0); // register the Broadcast Receivers context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(context, "短信發送成功", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: break; case SmsManager.RESULT_ERROR_RADIO_OFF: break; case SmsManager.RESULT_ERROR_NULL_PDU: break; } } }, new IntentFilter(SENT_SMS_ACTION));
處理返回的接收狀態 :
//處理返回的接收狀態 String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; // create the deilverIntent parameter Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); PendingIntent deliverPI = PendingIntent.getBroadcast(context, 0, deliverIntent, 0); context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { Toast.makeText(context, "收信人已經成功接收", Toast.LENGTH_SHORT) .show(); } }, new IntentFilter(DELIVERED_SMS_ACTION));
發送短信參數說明:
smsManager.sendTextMessage(destinationAddress, scAddress, text, sentIntent, deliveryIntent)
-- destinationAddress:目標電話號碼
-- scAddress:短信中心號碼,測試可以不填
-- text: 短信內容
-- sentIntent:發送 -->中國移動 --> 中國移動發送失敗 --> 返回發送成功或失敗信號 --> 后續處理 即,這個意圖包裝了短信發送狀態的信息
-- deliveryIntent: 發送 -->中國移動 --> 中國移動發送成功 --> 返回對方是否收到這個信息 --> 后續處理 即:這個意圖包裝了短信是否被對方收到的狀態信息(供應商已經發送成功,但是對方沒有收到)。
接下來是一個Demo,android版本為4.4
首頁界面:

AndroidManifest.xml文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.smstest" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.smstest.MainActivity" 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> </manifest>
MainActivity.java文件:
package com.example.smstest; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.telephony.SmsManager; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private LinearLayout llt; private Context context; private int PHONENUMBER=1001; private int SMSCONTENT=1002; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); llt= (LinearLayout)findViewById(R.id.main_layout); context=this; addText("電話號碼"); addTextField(PHONENUMBER); addText("短信內容"); addTextField(SMSCONTENT); Button btn= addBtn("發送短信"); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { //處理返回的發送狀態 String SENT_SMS_ACTION = "SENT_SMS_ACTION"; Intent sentIntent = new Intent(SENT_SMS_ACTION); PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, sentIntent, 0); // register the Broadcast Receivers context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(context, "短信發送成功", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: break; case SmsManager.RESULT_ERROR_RADIO_OFF: break; case SmsManager.RESULT_ERROR_NULL_PDU: break; } } }, new IntentFilter(SENT_SMS_ACTION)); //處理返回的接收狀態 String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; // create the deilverIntent parameter Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); PendingIntent deliverPI = PendingIntent.getBroadcast(context, 0, deliverIntent, 0); context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { Toast.makeText(context, "收信人已經成功接收", Toast.LENGTH_SHORT) .show(); } }, new IntentFilter(DELIVERED_SMS_ACTION)); //獲取短信手機號碼 EditText ett= (EditText)llt.findViewById(PHONENUMBER); String phoneNumber= ett.getText().toString(); //獲得短信信息 EditText smsEtt=(EditText)llt.findViewById(SMSCONTENT); String smscontent=smsEtt.getText().toString(); //創建一個短信管理器 android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault(); ArrayList<String> divideContents= smsManager.divideMessage(smscontent); ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(); ArrayList<PendingIntent> deliverIntents = new ArrayList<PendingIntent>(); for(String text : divideContents){ sentIntents.add(sentPI); deliverIntents.add(deliverPI); } smsManager.sendMultipartTextMessage(phoneNumber, null, divideContents, sentIntents, deliverIntents); } }); } Button addBtn(String text){ Button btn=new Button(context); btn.setText(text); llt.addView(btn); return btn; } void addTextField(int id){ EditText ett=new EditText(context); ett.setId(id); llt.addView(ett); } void addText(String text){ TextView tv=new TextView(context); tv.setText(text); llt.addView(tv); } }
