之前写过一篇关于短信截获的文章,通过注册 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" />




        好了,全部内容就是这些。
        只要不被组织开机自启动,我们的程序是一定会截获到短信的。
        至于文章中提到的疑惑,我还是想知道原因。希望了解的童鞋们留言给我~谢谢!