【Android】Android解析短信操作
目录结构:
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); } }