前言Android 监听短信的方式有两种1、监听短信数据库,数据库发生改变时回调。2、监听短信广播其中第二种方式由于国内各厂家的定制Android 可能导致无响应 目前测试 魅族 无法监听到短信广播本文介绍第一种方式监听短信一、创建Service前台服务
package com.iwhalecloud.demo.SMS;import android.annotation.SuppressLint;import android.app.Notification;import android.app.NotificationChannel;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.Service;import android.content.Context;import android.content.Intent;import android.database.ContentObserver;import android.database.Cursor;import android.graphics.BitmapFactory;import android.graphics.Color;import android.net.Uri;import android.os.Binder;import android.os.Build;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.text.TextUtils;import android.util.Log;import androidx.annotation.RequiresApi;import com.iwhalecloud.demo.MainActivity;import com.iwhalecloud.demo.R;import java.io.IOException;import java.util.regex.Matcher;import java.util.regex.Pattern;import okhttp3.FormBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;public class MyService extends Service {private static final String TAG = MyService.class.getSimpleName(); private SMSContentObserver smsObserver; public String phoneNo = ""; public String httpUrl = ""; @Override public IBinder onBind(Intent intent) { return new MyBinder(); } public class MyBinder extends Binder { /** * 获取当前Service的实例 * @return */ public MyService getService(){ return MyService.this; } } @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onCreate() { super.onCreate(); //注册观察者 smsObserver = new SMSContentObserver(MyService.this,new Handler()); getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsObserver); } @RequiresApi(api = Build.VERSION_CODES.O) @Override public int onStartCommand(Intent intent, int flags, int startId) { phoneNo=intent.getStringExtra("phoneNum"); httpUrl=intent.getStringExtra("httpUrl"); startForeground(100,getNotification("服务运行中...","正在监听号码:"+phoneNo+",保持应用后台运行...")); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); getContentResolver().unregisterContentObserver(smsObserver); } @RequiresApi(api = Build.VERSION_CODES.O) private Notification getNotification(String title, String message){ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 唯一的通知通道的id. String notificationChannelId = "notification_channel_id_01"; // Android8.0以上的系统,新建消息通道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //用户可见的通道名称 String channelName = "Foreground Service Notification"; //通道的重要程度 int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, channelName, importance); notificationChannel.setDescription("Channel description"); //LED灯 notificationChannel.enableLights(false); //震动 notificationChannel.enableVibration(false); if (notificationManager != null) { notificationManager.createNotificationChannel(notificationChannel); } } NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId); //通知小图标 builder.setSmallIcon(R.mipmap.ic_launcher); //通知标题 builder.setContentTitle(title); //通知内容 builder.setContentText(message); //设定通知显示的时间 builder.setWhen(System.currentTimeMillis()); //设定启动的内容 Intent clickIntent = new Intent(Intent.ACTION_MAIN); //点击回到活动主页 而不是创建新主页 clickIntent.addCategory(Intent.CATEGORY_LAUNCHER); clickIntent.setComponent(new ComponentName(this,MainActivity.class)); clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); //创建通知并返回 return builder.build(); } @SuppressLint("Range") private void setSmsCode() { Toast.makeText(MyService.this,phoneNo+":"+httpUrl,Toast.LENGTH_SHORT).show(); Cursor cursor = null; // 添加异常捕捉 try { cursor = getContentResolver().query( Uri.parse("content://sms"), new String[] { "_id", "address", "body", "date" }, null, null, "date desc"); // if (cursor != null) { cursor.moveToFirst(); final long smsdate = Long.parseLong(cursor.getString(cursor.getColumnIndex("date"))); final long nowdate = System.currentTimeMillis(); Toast.makeText(MyService.this,nowdate+":"+smsdate+"===="+ (nowdate - smsdate),Toast.LENGTH_SHORT).show();// 如果当前时间和短信时间间隔超过60秒,认为这条短信无效 final String strAddress = cursor.getString(cursor.getColumnIndex("address")); // 短信号码 final String strBody = cursor.getString(cursor.getColumnIndex("body")); // 在这里获取短信信息 Toast.makeText(MyService.this,"strAddress:"+strAddress,Toast.LENGTH_SHORT).show(); Toast.makeText(MyService.this,"strBody:"+strBody,Toast.LENGTH_SHORT).show(); if (nowdate - smsdate > 60 * 1000) { Log.i(TAG, "短信过期"); Toast.makeText(MyService.this,"短信过期",Toast.LENGTH_SHORT).show(); return; } final int smsid = cursor.getInt(cursor.getColumnIndex("_id")); if (TextUtils.isEmpty(strAddress) || TextUtils.isEmpty(strBody)) { return; } Log.i(TAG, "phoneNo: "+phoneNo); Log.i(TAG, "httpUrl: "+httpUrl); if (strAddress.equals(phoneNo)){ Log.i(TAG, "是我想要的号码"); Toast.makeText(MyService.this,strAddress+":"+strBody,Toast.LENGTH_SHORT).show(); Pattern continuousNumberPattern = Pattern.compile("(?<![0-9])([0-9]{6})(?![0-9])"); Matcher m = continuousNumberPattern.matcher(strBody); String dynamicPassword = ""; while (m.find()) { dynamicPassword = m.group(); } //连接http服务 String finalDynamicPassword = dynamicPassword; new Thread(new Runnable() { @Override public void run() { OkHttpClient mOkHttpClient = new OkHttpClient(); try { RequestBody requestBody = new FormBody.Builder().add("code", finalDynamicPassword).build(); Request request = new Request.Builder().url(httpUrl).post(requestBody).build(); Response response = mOkHttpClient.newCall(request).execute();//发送请求 String result = response.body().string(); Log.d(TAG, "result: " + result); } catch (IOException e) { e.printStackTrace(); } } }).start(); Log.i(TAG, "onReceiveSms: "+ dynamicPassword); } }else { Toast.makeText(MyService.this,"cursor 为 NULL",Toast.LENGTH_SHORT).show(); } }catch (Exception e) { Toast.makeText(MyService.this,"出错了::::"+e.getMessage(),Toast.LENGTH_SHORT).show(); e.printStackTrace(); } finally { Toast.makeText(MyService.this, String.valueOf(cursor != null),Toast.LENGTH_SHORT).show(); if (cursor != null) { cursor.close(); } } } public class SMSContentObserver extends ContentObserver { private static final int MSG = 1; private int flag = 0; private Context mContext; private Handler mHandler; public SMSContentObserver(Context mContext, Handler mHandler) { super(mHandler); this.mContext = mContext; this.mHandler = mHandler; } @Override public void onChange(boolean selfChange) { // TODO Auto-generated method stub super.onChange(selfChange); //onchange调用两次 过滤掉一次 if (flag%2 == 1){ setSmsCode(); } flag++; } } }
二、主界面(参考)
package com.iwhalecloud.demo;import android.Manifest;import android.content.Intent;import android.content.SharedPreferences;import android.content.pm.PackageManager;import android.os.Build;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import android.widget.ToggleButton;import androidx.appcompat.app.AppCompatActivity;import com.iwhalecloud.demo.SMS.MyService;public class MainActivity extends AppCompatActivity { private static final String TAG = "CC"; final private int REQUEST_CODE_ASK_PERMISSIONS = 1; private boolean serviceFlag = false; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SharedPreferences read = getSharedPreferences("info", MODE_PRIVATE); String phoneNum = "10659874"; String httpUrl= ""; if (read.getString("phoneNum",null)!=null){ phoneNum = read.getString("phoneNum",null); } if (read.getString("httpUrl",null)!=null){ httpUrl = read.getString("httpUrl",null); } TextView v1 = findViewById(R.id.editText1); TextView v2 = findViewById(R.id.editText2); v1.setText(phoneNum); v2.setText(httpUrl); ToggleButton tb = findViewById(R.id.button); tb.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TextView pn = findViewById(R.id.editText1); TextView httpUrl = findViewById(R.id.editText2); if (tb.isChecked()){ Intent service = new Intent(getApplicationContext(), MyService.class); SharedPreferences.Editor editor = getSharedPreferences("info",MODE_PRIVATE).edit(); editor.putString("phoneNum",pn.getText().toString()); editor.putString("httpUrl",httpUrl.getText().toString()); editor.apply(); service.putExtra("phoneNum",pn.getText().toString()); service.putExtra("httpUrl",httpUrl.getText().toString()); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { MainActivity.this.startService(service); } pn.setEnabled(false); httpUrl.setEnabled(false); Toast.makeText(MainActivity.this,"服务开启",Toast.LENGTH_SHORT).show(); }else { Intent service = new Intent(getApplicationContext(), MyService.class); MainActivity.this.stopService(service); pn.setEnabled(true); httpUrl.setEnabled(true); Toast.makeText(MainActivity.this,"服务停止",Toast.LENGTH_SHORT).show(); } } }); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { int hasReadSmsPermission = checkSelfPermission(Manifest.permission.READ_SMS); if (hasReadSmsPermission != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.READ_SMS}, REQUEST_CODE_ASK_PERMISSIONS); return; } } } @Override protected void onResume() { super.onResume(); } @Override protected void onDestroy() { super.onDestroy(); Intent service = new Intent(getApplicationContext(), MyService.class); this.stopService(service); }}
三、Main.XML
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/editText1" android:layout_width="295dp" android:layout_height="58dp" android:hint="监听电话号码" android:textColorHint="#95A1AA" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.465" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.469" /> <EditText android:id="@+id/editText2" android:layout_width="295dp" android:layout_height="58dp" android:text="http://10.0.2.2:8888/api/test/t1" android:hint="验证码请求接口" android:textColorHint="#95A1AA" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.45" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.595" /> <ToggleButton android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textOn="停止服务" android:textOff="启动服务" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/editText1" app:layout_constraintVertical_bias="0.495" /></androidx.constraintlayout.widget.ConstraintLayout>
四、清单文件
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.INTERNET" /> <application .... ....> <service android:name=".SMS.MyService" android:enabled="true" android:exported="false" /> </application>