Android底层监听短信
一、
背景介绍
在现代手机应用中,短信作为一种重要的通讯方式,仍然在某些特定场景下发挥重要作用,用户注册、密码找回和双重身份验证等操作通常需要通过短信进行验证,了解如何在Android系统中实现短信监听功能是非常必要的,本文将详细介绍如何在Android底层实现短信监听功能,并探讨相关的权限声明、广播接收器以及短信内容解析等内容。
目标读者
本文适合Android开发者,尤其是那些希望在其应用程序中集成短信监听功能的开发者,对于对Android系统内部机制感兴趣的技术人员,也具有一定的参考价值。
文章结构
本文将从以下几个方面详细介绍Android底层监听短信的实现:
SMS监听的基本概念
权限声明与请求
创建BroadcastReceiver
注册与注销BroadcastReceiver
短信内容的解析
使用ContentObserver监听短信数据库
锁屏状态下的短信监听
注意事项与常见问题
上文归纳
二、SMS监听的基本概念
1. BroadcastReceiver简介
BroadcastReceiver是Android四大组件之一,用于接收和处理广播消息,当系统或其他应用发送广播时,BroadcastReceiver可以捕获这些广播并执行相应的逻辑。
短信广播的种类
在Android中,短信相关的广播主要有以下几种:
android.provider.Telephony.SMS_RECEIVED
:收到新的短信时触发。
android.provider.Telephony.SMS_SENT
:短信发送成功时触发。
android.intent.action.INPUT_METHOD_CHANGED
:输入法变化时触发。
SMS监听的基本原理
SMS监听的基本原理是通过创建一个继承自BroadcastReceiver的类,并在该类中重写onReceive
方法来处理收到的短信广播,通过在AndroidManifest.xml文件中注册该BroadcastReceiver,使其能够接收系统的短信广播。
三、权限声明与请求
所需权限列表
为了读取短信内容,需要在AndroidManifest.xml文件中声明以下权限:
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.INTERNET" />
如果还需要上传短信到服务器,可能还需要添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />
2. 动态权限请求(针对Android 6.0及以上)
从Android 6.0(API级别23)开始,读取短信属于危险权限,需要在运行时动态请求用户授权,以下是一个简单的权限请求示例:
import android.Manifest; import android.content.pm.PackageManager; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; public class MainActivity extends AppCompatActivity { private static final int SMS_PERMISSION_CODE = 123; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, SMS_PERMISSION_CODE); } } }
四、创建BroadcastReceiver
SmsListener类的实现
我们需要创建一个继承自BroadcastReceiver的类,用于处理收到的短信广播,在这个类中,我们将重写onReceive
方法,解析短信内容并进行处理。
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; import android.util.Log; public class SmsListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 获取短信数据 Bundle bundle = intent.getExtras(); if (bundle != null) { Object[] pdus = (Object[]) bundle.get("pdus"); for (Object pdu : pdus) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu); String sender = smsMessage.getDisplayOriginatingAddress(); String messageBody = smsMessage.getMessageBody(); // 打印短信发件人及内容 Log.d("SmsListener", "Sender: " + sender + ", Message: " + messageBody); // 这里可以加入自定义处理逻辑,例如发送通知、弹出对话框等 } } } }
广播接收器的生命周期
BroadcastReceiver的生命周期相对简单,主要包含以下几个方法:
onReceive(Context context, Intent intent)
:当接收到广播时调用,这个方法应该在短时间(约10秒)内完成所有操作,因为系统不会为它分配长时间的后台任务。
onStart()
和onStop()
:用于管理广播接收器的启动和停止。
onReceive方法详解
onReceive
方法是BroadcastReceiver的核心方法,当接收到符合过滤条件的广播时,系统会调用这个方法,在这个方法中,我们可以获取广播携带的数据,并进行相应的处理,上面的代码中我们通过Bundle
对象获取短信的内容和发件人信息,并将其打印出来。
五、注册与注销BroadcastReceiver
1. 在AndroidManifest.xml中注册广播接收器
为了确保广播接收器能够在应用未运行时也能接收到短信广播,我们需要在AndroidManifest.xml文件中进行注册,具体步骤如下:
<receiver android:name=".SmsListener" > <intent-filter android:priority="2147483647" > <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
这里的android:priority="2147483647"
表示广播接收器的优先级,数值越大优先级越高,需要注意的是,不同设备和系统版本对优先级的支持可能有所不同,某些情况下可能需要调整优先级以确保广播接收器能够正常工作。
动态注册与静态注册的区别
动态注册是指在代码中通过registerReceiver
方法注册广播接收器,适用于需要在特定时间段内监听广播的情况,静态注册则是在AndroidManifest.xml文件中进行注册,适用于需要长期监听广播的情况,两者的主要区别在于生命周期和作用范围:
动态注册:广播接收器的生命周期与注册它的组件(如Activity)一致,一旦组件销毁,广播接收器也随之销毁,适用于短时间内需要监听广播的场景。
静态注册:广播接收器的生命周期独立于应用的其他组件,即使应用不运行,只要设备接收到符合条件的广播,广播接收器就会被激活,适用于长期监听广播的场景。
注销广播接收器的重要性
当不再需要监听广播时,及时注销广播接收器是非常重要的,未注销的广播接收器可能会导致内存泄漏和应用崩溃,可以通过调用unregisterReceiver
方法来注销广播接收器:
@Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(smsListener); }
确保在Activity或Application的生命周期结束时调用该方法,以释放资源。
六、短信内容的解析
在onReceive
方法中,我们通过Bundle
对象获取短信的内容和发件人信息,具体步骤如下:
Object[] pdus = (Object[]) bundle.get("pdus"); for (Object pdu : pdus) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu); String sender = smsMessage.getDisplayOriginatingAddress(); String messageBody = smsMessage.getMessageBody(); }
这里,pdus
数组包含了所有接收到的短信协议数据单元(PDU),通过遍历该数组可以获取所有短信的信息。SmsMessage.createFromPdu((byte[]) pdu)
方法用于将PDU转换为SmsMessage
对象,进而通过getDisplayOriginatingAddress()
和getMessageBody()
方法获取发件人和短信内容。
处理多条短信的情况
有时一条短信可能会被分割成多个PDU发送,因此我们需要遍历整个pdus
数组以确保完整地获取短信内容,上述代码已经演示了如何处理这种情况。
解析短信时间戳与其他信息
除了发件人和内容外,有时我们还需要解析短信的时间戳等信息,可以通过以下方式获取:
long timestamp = smsMessage.getTimestampMillis(); Date date = new Date(timestamp); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String receiveTime = format.format(date); Log.i("SmsListener", "Received time: " + receiveTime);
这里的getTimestampMillis()
方法返回短信的时间戳(毫秒级),然后通过SimpleDateFormat
将其格式化为可读的日期时间字符串。
七、使用ContentObserver监听短信数据库
ContentObserver简介
ContentObserver
用于观察指定URI的变化,当观察到的数据发生变化时,会触发相应的回调方法,通过使用ContentObserver
,我们可以监听短信数据库的变化,从而获取新增、删除或修改的短信记录。
2. 创建ContentObserver子类
我们需要创建一个继承自ContentObserver
的子类,并重写其onChange
方法:
import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.widget.Toast; public class SmsContentObserver extends ContentObserver { public SmsContentObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); // 在这里处理短信数据库的变化,例如查询最新的短信记录 } }
在onChange
方法中,我们可以添加查询最新短信记录的逻辑,需要注意的是,由于onChange
方法会在主线程中调用,因此应避免在该方法中执行耗时操作。
3. 注册与注销ContentObserver
我们需要在合适的位置注册和注销ContentObserver
,通常可以在Activity或Service的onCreate
和onDestroy
方法中进行:
import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private SmsContentObserver smsContentObserver; private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); smsContentObserver = new SmsContentObserver(handler) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); // 查询最新的短信记录并进行处理 Uri uri = Uri.parse("content://sms/inbox"); // 收件箱URI Cursor cursor = getContentResolver().query(uri, null, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { String address = cursor.getString(cursor.getColumnIndex("address")); String body = cursor.getString(cursor.getColumnIndex("body")); Log.d("SmsContentObserver", "New SMS from " + address + ": " + body); } cursor.close(); } } }; getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver); } @Override protected void onDestroy() { super.onDestroy(); getContentResolver().unregisterContentObserver(smsContentObserver); } }
在上面的代码中,我们在onCreate
方法中注册了ContentObserver
,并在onDestroy
方法中注销它,这样,当短信数据库发生变化时,onChange
方法会被调用,我们可以在其中添加查询最新短信记录的逻辑。
八、锁屏状态下的短信监听
Android后台服务简介
为了在锁屏状态下继续监听短信,我们需要使用Android的后台服务(Service),后台服务可以在应用退到后台或锁屏时继续运行,确保短信监听功能不中断。
创建后台服务类
我们需要创建一个继承自Service
的服务类:
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; import androidx.annotation.Nullable; public class SmsService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; // 不需要绑定服务 } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d("SmsService", "Service started"); // 在这里添加短信监听逻辑,例如注册BroadcastReceiver或ContentObserver return START_STICKY; // 确保服务在终止后自动重启 } @Override public void onDestroy() { super.onDestroy(); Log.d("SmsService", "Service destroyed"); // 在这里注销短信监听逻辑,例如注销BroadcastReceiver或ContentObserver } }
在上面的代码中,我们继承了Service
类,并重写了onStartCommand
和onDestroy
方法,在onStartCommand
方法中,我们可以添加短信监听逻辑;在onDestroy
方法中,注销监听逻辑以防止内存泄漏。START_STICKY
标志确保服务在终止后会自动重启。
启动与停止后台服务的方法
我们需要在合适的位置启动和停止后台服务,通常可以在Activity或Application的生命周期方法中进行:
import android.content.Intent; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启动后台服务监听短信 Intent serviceIntent = new Intent(this, SmsService.class); startService(serviceIntent); } @Override protected void onDestroy() { super.onDestroy(); // 停止后台服务监听短信(可选) Intent serviceIntent = new Intent(this, SmsService.class); stopService(serviceIntent); } }
在上面的代码中,我们在MainActivity
的onCreate
方法中启动了SmsService
,并在onDestroy
方法中停止它,这样可以确保在Activity销毁时,后台服务也会随之停止,如果希望服务在应用退出时仍然继续运行,可以省略停止服务的步骤。
九、注意事项与常见问题
权限问题导致的监听失败解决方法
在某些情况下,由于权限问题可能导致短信监听失败,确保已在AndroidManifest.xml文件中声明了必要的权限,并在运行时动态请求用户授权,检查设备设置中的权限管理,确保已授予应用相应的权限,如果问题仍然存在,可以尝试重启设备或重新安装应用。
2. 广播接收器的优先级设置与冲突解决策略
在Android中,多个应用可以同时监听同一个广播事件,但系统会根据广播接收器的优先级来决定哪个应用先收到广播,为了提高自己应用的优先级,可以在AndroidManifest.xml中设置较高的优先级值(如2147483647),如果遇到优先级冲突的问题,可以尝试调整优先级值或与其他应用开发者协商解决,可以考虑使用有序广播(OrderedBroadcast)来确保广播按特定顺序传递,有序广播允许广播接收器依次处理广播,并根据需要终止广播传递,通过这种方式,可以避免多个接收器之间的冲突,需要注意的是,并非所有广播都支持有序广播,具体取决于广播的类型和系统版本,在使用有序广播时,还需要注意性能问题,因为每个接收器都需要等待前一个接收器处理完成后才能继续,在使用有序广播时应谨慎考虑其对性能的影响,还可以通过编程方式设置广播接收器的优先级,在代码中创建PendingIntent时可以指定FLAG_FROM_BACKGROUND标志以提高优先级,PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(action), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_FROM_BACKGROUND);这样设置后即使应用处于后台状态也能优先接收到广播,但是要注意不要滥用此标志以免影响用户体验和其他应用的正常功能,总之合理设置广播接收器的优先级可以有效解决冲突问题但同时也要注意避免过度使用导致其他潜在问题的发生,在实际开发过程中应根据具体需求和场景选择合适的解决方案以确保应用的稳定性和用户体验。
以上就是关于“Android底层监听短信”的问题,朋友们可以点击主页了解更多内容,希望可以够帮助大家!
原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1291550.html
本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。
发表回复