之前写过一篇关于短信截获的文章,通过注册 BroadcastReceive 来获取短信信息。
但是我发现,当装了飞信,360手机安全卫士等同样有截获短信功能的程序后,我自己的程序就截获不到短信了;而且我还发现,当安装了飞信以后,Android 系统 的Notification 中就不会再有短信提示了。
在 BroadcastReveive 中,有 abortBroadcast() 方法。该方法的作用是将短信拦截,并且阻止其继续传递。
我们都知道,Android 广播,是组件之前传播数据的一种机制。(详情可参考:《Android系统中的广播(Broadcast)机制简要介绍和学习计划》)
而短信就是一种广播,当有短信到来时,系统会发出广播,随后各接收器就会按照优先级一次接受,一级接收完,传给下一级;而当优先级高的接收器调用了 abortBroadcast() 方法后,广播就被阻拦了,那么优先级低于该级的接收器,则不会再收到信息。
(当我第一次了解该方法时,忍不住对移动做出微微的鄙视 0.0)
截获问题解决了,剩下的就是优先级的问题了。根据 Google 官方发布的文档,在 Manifest.xml 中修改 android:priority 参数可更改优先级,参数为-1000~1000,数值越高,优先级越高。但是当我把优先级设置到1000后,还是无法先于飞信、360,好奇怪……究竟是为什么呢?
于是我开始针对剩下问题着手研究。
开始的时候,我还怀疑过是否和程序安装的先后顺序有关,现在想想真的很好笑。翻阅网上的资料,代码内容和我这个版本都差不多,也许是别人没发现,或者没需求,没有人调研为什么飞信和360会先截获到短信。
后来在偶然的机会,我听说 android:priority 属性的最大值不是1000,而是2147483647,这个数字怎么来的呢?熟悉数据结构的童鞋都知道,Integer 的最大值。针对这种说法,我保持怀疑的态度,因为人家官方文档说是1000最大啊~怀着试试看的心情,我将重新编译了工程源码,很遗憾,失败了。但是自己同时写两个程序并分别以 Integer 最大值和1000设置优先级,这次倒是成功了,验证了优先级最高为 Integer 最大值的说法。
然后心想是不是和 Android 的广播机制有关,于是改变了研究方向。
经过对广播机制的学习,对源码的研究,我发现动态注册要比静态注册的优先级高。于是我将短信截获做成了动态注册,果然成功了!
最后,我将动态注册短信截获和开机自启动相结合,并把优先级设置为了 Integer最大值,成功制作出了优先级高于飞信、360的短信截获程序。
遗憾的是,又有两个问题产生了。
1.如果用“自启动管家”将短信截获程序的开机自启动禁止,那么咱们就截获不到短信了。现在就需要看看源码的写法,如何阻止别的软件对咱们程序的自启动屏蔽。
2.移动和奇虎两个大厂家,做程序不应该会出现对代码理解不够透彻的情况。那么为什么我轻易就越过他们的优先级,因为没法查看源码, 所以原因不得而知。是因为程序安装的先后顺序?还是说他们把优先级设置成了Integer的最大值减1或1000?是为了方便别的程序去运用短信截获(这么无私?)?还是说真的是对 Android 系统理解不够?迷糊……
好了,言归正传,吐槽了这么多,该看代码了。
其实很简单,就是一个短信截获、Service 动态注册和开机自启动 Service 的结合体。
本文中不会对代码做太为详细的介绍,想了解的同学可以分别看三篇文章。
【Android】短信应用——短信信息实时获取
【Android】动态注册广播接收器
【Android】开机自启动Service
创建一个 Service 类,在该类的 onCreate() 方法中动态注册一个内部类,该类继承 BroadcastReceive。
1. IntentFilter filter = new IntentFilter();
2. filter.addAction("android.provider.Telephony.SMS_RECEIVED");
3. filter.setPriority(Integer.MAX_VALUE);
4. registerReceiver(new SmsReceiver(), filter);
addAction() 定义广播接收器类别——短信接收器。
setPriority() 方法设置该广播接收器优先级,Integer 最大值为最高优先级。
在 Service 中添加一个继承 BroadcastReceive 的内部类,在该类中添加短信截获方法。
1. Bundle bundle = intent.getExtras();
2. if (bundle != null) {
3. "pdus");
4. new SmsMessage[pdusObjects.length];
5. for (int i = 0; i < pdusObjects.length; i++) {
6. messages[i] = SmsMessage
7. byte[]) pdusObjects[i]);
8. }
9. for (SmsMessage message : messages) {
10. SMSAddress.append(message
11. .getDisplayOriginatingAddress());
12. SMSContent.append(message.getDisplayMessageBody());
13. "来信号码:" + SMSAddress + "\n短信内容:"
14. + SMSContent);
15. }
16. }
这样我们就能在 Logcat 中看到截获的内容了。
Service 同 Activity,需要在 Manifest.xml 中声明。
1. <service android:name="cn.etzmico.smsreceived.SMSReceivedService" >
2. <intent-filter >
3. "cn.etzmico.service.SMSReceived" />
4.
5. "android.intent.category.default" />
6. </intent-filter>
7. </service>
"cn.etzmico.service.SMSReceived"为 Service 的“标签”。
同时需要添加权限。
1. <uses-permission android:name="android.permission.RECEIVE_SMS" />
我们另外需要新建一个继承于 BroadcastReceiver 的类,区别于之前的,这个 Reveiver 是静态注册的,用于开机自启动。
在 onReceive() 中调用 Service。
1. ctx.startService(new Intent("cn.etzmico.SMSReceived"));
intent中的参数为要启动的 Service 的“标签”。
由于是静态注册,因此自启动类也需要在 Manifest.xml 中声明。
1. <receiver android:name=".BootReceiver" >
2. "2147483647" >
3. "android.intent.action.BOOT_COMPLETED" />
4.
5. "android.intent.category.HOME" />
6. </intent-filter>
7. </receiver>
我们在这里也设置下优先级,方便短信截获。值同样为 Integer 最大值,不过需要用具体数值去表示。
最后别忘了权限。
1. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
好了,全部内容就是这些。
只要不被组织开机自启动,我们的程序是一定会截获到短信的。
至于文章中提到的疑惑,我还是想知道原因。希望了解的童鞋们留言给我~谢谢!