获取短信有两种方式,第一种是通过广播接收器,第二种通过读取短信数据库

第一种:

静态注册了一个广播,通过接收包含android.provider.Telephony.SMS_RECEIVED动作的广播,获取新收到的短信内容。

        <receiverandroid:name=".GetSMS"android:enabled="true"android:exported="true"><intent-filter android:priority="100000"><action android:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter></receiver>

想要接收广播,并且读取广播内容,声明权限自然就少不了。6.0之后的系统则需要进行运行时权限处理。

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />

当新收到短信后,广播接收器就会收到一个有序广播,回调onReceive(Context context,Intent intent)方法。在该方法中执行读取短信的逻辑。

public class GetSMS extends BroadcastReceiver {@TargetApi(24)public void onReceive(Context context, Intent intent){Bundle bundle=intent.getExtras();   String format=intent.getStringExtra("format");Object[] obj=(Object[]) bundle.get("pdus");for(Object object:obj){SmsMessage smsMessage=SmsMessage.createFromPdu((byte[])object,format);Toast.makeText(context,smsMessage.getDisplayMessageBody(),Toast.LENGTH_SHORT).show();Log.d("GetSMS",smsMessage.getDisplayMessageBody());}}
}

分析下上面的代码,通过intent.getExtras()获取Intent中附加的Bundle数据,并通过Bundle取出key为“pdus”对象数组,这里就涉及到短信的格式了,PDU(Protocol Data Unit,协议数据单元),PDU模式下不仅能发送中文短信,也能发送英文短信。PDU收发短信有三种编码可用:7-bit、8-bit和UCS2编码。7-bit编码用于发送普通的ASCII字符,即英文短信,最多可发送160字符。8- bit编码通常用于发送数据消息。UCS2编码用于发送Unicode字符,可发送中文字符,最多发送70字符。如果发送的短信过长,就会得到多组PDU格式的短信,因此可以解释从Bundle取出的“pdus”为对象数组。然后将对象数组中的每个object转化为SmsMessage。可以通过SmsMessage对象得到短信中的内容。

SmsMessage.createFromPdu((byte[ ])object,format) 中的(byte[ ])object可以如下理解:

byte[ ] A=new byte[10];
Object B=A;
byte[ ]  A=(byte[ ])B;  也就是说接收到的短信数据原本就是字节数组。另外:

createFromPdu(byte[ ] pdu) 这个方法不推荐使用了,然后查阅安卓的参考说从 API level 23 以后应该用createFromPdu(byte[], String)代替. 继续查阅得知第二个参数应该是短信的类型, 大概是GSM与CDMA短信的解码方式不同,所以应该传入这个参数, 那么, 我应该怎么得到这个格式字符串呢?
在安卓源码中的Telephony类中有相关的示例方法.按照这个方法代码示例可以解决这个问题.

public static SmsMessage[] getMessagesFromIntent(Intent intent) {Object[] messages = (Object[]) intent.getSerializableExtra("pdus");String format = intent.getStringExtra("format");int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,SubscriptionManager.getDefaultSmsSubId());Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);int pduCount = messages.length;SmsMessage[] msgs = new SmsMessage[pduCount];for (int i = 0; i < pduCount; i++) {byte[] pdu = (byte[]) messages[i];msgs[i] = SmsMessage.createFromPdu(pdu, format);msgs[i].setSubId(subId);}return msgs;
}

得到SmsMessage对象后,就可以调用getDisplayMessageBody获得短信文本。

BUT!!!因为很多手机系统都是国内厂商定制的,这个系统广播会被拦截,因此系统发出的这个有序广播无论你的接收器权限多么高,都无法接收到。这就需要用第二种方法读取短信啦。

第二种:

先了解一下什么是ContentObserver。

ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。

注册/取消注册ContentObserver方法,抽象类ContentResolver类中的方法原型如下:
public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
参数:uri 需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)notifyForDescendents 为false 表示精确匹配,即只匹配该Uri为true 表示可以同时匹配其派生的Uri,举个栗子:
假设UriMatcher 里注册的Uri共有一下类型:
1 、content://com.test.zz/teacher
2 、content://com.test.zz/teacher/#
3、 content://com.test.zz/teacher/new (派生的Uri)
假设我们当前需要观察的Uri为content://com.test.zz/teacher,如果发生数据变化的Uri为content://com.test.zz/teacher/new,当notifyForDescendents为 false,那么该ContentObserver会监听不到,但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。
public final voidunregisterContentObserver(ContentObserver observer)
功能:取消对给定Uri的观察
参数: observer ContentObserver的派生类实例

voidonChange(boolean selfChange)
功能:当观察到的Uri发生变化时,回调该方法去处理。所有ContentObserver的派生类都需要重载该方法去处理逻辑。

观察特定Uri的步骤如下:
1、 创建我们特定的ContentObserver派生类,必须调用父类构造方法,必须重载onChange()方法去处理回调后的功能实现
2、 利用context.getContentResolover()获得ContentResolove对象,接着调用registerContentObserver()方法去注册内容观察者
3、 由于ContentObserver的生命周期不同步于Activity和Service等,因此,在不需要时,需要手动的调用unregisterContentObserver()去取消注册。

短信的Uri共有一下几种:
content://sms/inbox 收件箱 
content://sms/sent 已发送 
content://sms/draft 草稿 
content://sms/outbox 发件箱 (正在发送的信息)
content://sms/failed 发送失败
content://sms/queued 待发送列表 (比如开启飞行模式后,该短信就在待发送列表里)

了解了上面的知识后就可以开始第二种方法的编码啦。代码如下:

public class GetService extends Service {private String SMS_URI="content://sms/";private SMSObserver smsObserver;public GetService() {}@Overridepublic IBinder onBind(Intent intent) {throw new UnsupportedOperationException("Not yet implemented");}public int onStartCommand(Intent intent,int flags,int id){Uri uri=Uri.parse(SMS_URI);smsObserver=new SMSObserver(handler);getContentResolver().registerContentObserver(uri,true,smsObserver);return START_STICKY;}public class SMSObserver extends ContentObserver {public SMSObserver(Handler handler){super(handler);}public void onChange(boolean a){super.onChange(a);StringBuilder sbuilder=new StringBuilder();StringBuilder sbuilder2=new StringBuilder();Uri uri=Uri.parse(SMS_URI);Cursor cursor=getContentResolver().query(uri,new String[]{"body"},"read=?",new String[]{"0"},null);if(cursor.moveToFirst()){do{String body=cursor.getString(cursor.getColumnIndex("body"));sbuilder.append(body);}while(cursor.moveToNext());}String sms=sbuilder.toString();Pattern pattern=Pattern.compile("[0-9]{3,}");Matcher matcher=pattern.matcher(sms);if(matcher.find()){sbuilder2.append(matcher.group());}ClipboardManager clipboardManager=(ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);clipboardManager.setPrimaryClip(ClipData.newPlainText(null,sbuilder2.toString()));Toast.makeText(getApplicationContext(),"已将验证码复制到剪贴板",Toast.LENGTH_SHORT).show();sbuilder.setLength(0);sbuilder2.setLength(0);cursor.close();}}public Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};public void onDestroy(){super.onDestroy();if(smsObserver!=null){getContentResolver().unregisterContentObserver(smsObserver);}}
}

着重分析onChange方法中的逻辑。通过ContentResolver查询未读短信。read=0表示短信未读,为1表示短信已读。遍历Cursor,获得短信的内容。是不是感觉这个方法更简单?接着通过正则表达式来截取短信内容中的验证码。

那么如何将得到的验证码复制到剪贴板呢?利用ClipboardManager就能实现。利用newPlainText(label, text)返回ClipData对象,数据是文字text,描述是label,MIME类型是MIMETYPE_TEXT_PLAIN,将验证码封装在ClipData对象中,利用setPrimaryClip(ClipData data)将数据保存在剪贴板中。这样就成功实现了读取短信,复制验证码的功能啦。

sms主要结构:
  
  _id:短信序号,如100
  
  thread_id:对话的序号,如100,与同一个手机号互发的短信,其序号是相同的
  
  address:发件人地址,即手机号
  
  person:发件人,如果发件人在通讯录中则为具体姓名,陌生人为null
  
  date:日期,long型,如1346988516,可以对日期显示格式进行设置
  
  protocol:协议0SMS_RPOTO短信,1MMS_PROTO彩信
  
  read:是否阅读0未读,1已读
  
  status:短信状态-1接收,0complete,64pending,128failed
  
  type:短信类型1是接收到的,2是已发出
  
  body:短信具体内容
  
  service_center:短信服务中心号码编号

demo地址:https://github.com/zycoJamie/GetCode

读取短信并复制验证码小工具相关推荐

  1. python读取短信验证码_我用Python给你发了个短信验证码,你也来试试

    点击上方毛利学python,选择置顶或星标 第一时间送达Python 技术干货! 在互联网时代,为了保证操作的安全性,我们几乎所有的登录.注册等操作都需要用到短信验证码,一是为了防止自己的平台被机器频 ...

  2. 自动化软件测试 - 通过 Android Studio 开发 App 自动拦截读取短信验证码

        之前一期介绍了怎样通过 Python 的代码去获取短信验证码去进行软件测试,这一期将主要介绍 怎样 通过 Android Studio 开发 App 自动拦截读取短信验证码.     Andr ...

  3. 自动化软件测试 - 利用短信转发器结合Selenium读取短信验证码

        上一篇博客介绍了怎样通过 Android Studio 开发 App 自动拦截读取短信验证码,今天将介绍另外一种非开发App 的方法去实现,通过利用第三方工具短信转发器 结合 Selenium ...

  4. 腾讯云短信服务发送验证码

    腾讯云短信服务发送验证码 1.前言 2.进入短信服务控制台 3.创建签名 4.创建模板 5.短信-应用管理-应用列表 6.创建密钥 7.SpringBoot实现短信验证码发送 7.1 引入依赖 7.2 ...

  5. android app读取短信,uni-app读取短信

    在我们的日常开发中,我们可能会遇到,读取用短信中的验证码,然后实现一个自动填充的功能. 但是呢,在插件市场,我暂时还没有找到相应的插件,来使用, 所以呢,我想到了一种曲线的方式,那就是获取到设备的短信 ...

  6. Android之发送短信和接收验证码

    最近项目需求需要发送短信和接收验证码并将验证码显示在输入框中 以下是我的记录 前提---权限 <uses-permission android:name="android.permis ...

  7. Java 集成阿里大鱼平台短信服务发送验证码到手机

    点击前往:阿里大鱼 --- 短信接口调用错误码(错误原因及处理方式) 上一篇:阿里大鱼短信服务 --- 发送验证码.短信通知 下一篇:Java 集成阿里大鱼平台短信服务发送验证码 --- 补齐注册部分 ...

  8. Android读取短信和联系人

    读取短信和联系人经常会用到,要了解的是这是内容提供者(contentProvider)的知识点,大家都知道数据库是在data-->data 对应的包目录下,其他应用是不可以访问到的,如果有些数据 ...

  9. Android 读取短信内容(模拟器)

    读取短信内容 读取短信的内容就要知道短信放在哪里,然后通过查询语句把结果封装到实体类的List中,再全部打印出来即可. 下面是具体的代码: activity_main.xml <?xml ver ...

最新文章

  1. [android] setOnTouchEvent 设置返回值为true 和 false的区别
  2. [转载]《博客园精华集》Winform筛选结果(共105篇)
  3. kafka-python 停止消费
  4. python线下培训-Python培训是应该选择线上还是线下呢?
  5. PayPal 开发详解(六):下载paypal立即付款SDK 并编译打包
  6. 安装DNN时可能出现的错误
  7. 零基础入门学习Python,我与python的第一次亲密接触后的感受
  8. vsftpd搭建及配置参数
  9. Kohana - PHP5框架 - 我看过的开源框架
  10. Google chrome浏览器快捷方式
  11. 三国志战略版:当锋无法破防的司马盾
  12. flac转mp3方法,flac转mp3步骤
  13. linux下ace安装教程,linux下安装ACE开发环境
  14. android拍照保存照片方向,android 拍照的照片方向问题,读取图片EXIF信息
  15. 清除本地dns(Mac,win)
  16. DICOM协议学习笔记(二)
  17. python爬取b站弹幕分析_B站弹幕爬取原理解析(python)
  18. Linux的文件操作
  19. 小程序发送模板消息给用户 —— 一次性模板实现“长期订阅”
  20. 实验三 201521410003 15网一 叶萌熙

热门文章

  1. 极客日报第 31 期:编写贩卖《和平精英》游戏外挂,5人被判刑;苹果推出轻App码
  2. 淘宝直播赚佣金项目玩法
  3. Autojs: 坚果云文本文件上传/下载
  4. 互联网突破和 SD-WAN
  5. java显示数据库_java查询数据库中的数据并显示
  6. 常用测试网络联通方法
  7. 股市学习稳扎稳打(四)当宏观经济出现复苏时,不同的行业分别以什么顺序进行轮动上涨
  8. python + selenium + unittest 实现网站登录注册自动化测试
  9. 雷石 扩展文件服务器,雷石发布KTV软硬件新品:智能手表可点歌
  10. Python 绘制圆锥体(3D图)