接上一篇:android 发送短信sendTextMessage()真机运行报错,退出,在已申请SEND_SMS权限的情况下Android send SMS not working uid 。。。

重开一篇,完整讲述我这个半吊子的android 入门人员是怎么做出一个可以定时启动并且发送短信,读取回信里面的密码 这个功能的app 至于我为什么要做这个功能,可以看上一篇文章。主要是大学里面宽带密码定期更新手动去获取太麻烦~~

得先放出运行效果视屏才行,如下:只是点击了发送按钮,就会做到自动发送、接收、提取短信收到的密码,更能够在29个小时后自动启动。

有了上面的gif 好解释多了, 不能放视屏这个很头大。。视屏上去截取gif 还不能超过5M。。。我忍。。

权限:

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

其中  SEND_SMS、 READ_SMS 、RECEIVE_SMS 为危险权限,android6.0之后要动态申请,这个一开始也是入坑吃了亏。。

动态申请权限方法:

多个权限一起申请

askPermissions();//        运行时权限 (动态权限申请)if(!permissionList.isEmpty()){String[] permissions = permissionList.toArray(new String[permissionList.size()]);//list-->StringActivityCompat.requestPermissions(MainActivity.this,permissions,1);}

askPermissions();

public void askPermissions(){if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.SEND_SMS)!=PackageManager.PERMISSION_GRANTED){permissionList.add(Manifest.permission.SEND_SMS);}if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_SMS)!=PackageManager.PERMISSION_GRANTED){permissionList.add(Manifest.permission.READ_SMS);}if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.RECEIVE_SMS)!=PackageManager.PERMISSION_GRANTED){permissionList.add(Manifest.permission.RECEIVE_SMS);}//多的同样加进去}

先大致说一下这个App的内部组成构建

不要嫌弃我字丑的一张思路图:

没错,我就是写字写的不好报的计算机系,我去,这个决定看起来太英明了。。。

其中右上角 程序执行到MainActivity之前打了 “x”叉叉, 因为我这个程序是想要让他自动自动的,然后按照原来的循序下去就不能在此自动启动这个MainActivity了 ,所以就加了一个rebootBroadcaseReceiver 这个广播接收器,也就是绿色箭头指向的程序块,让他接受来自sendMessageService发送的广播,继而再通过startActivity的方法启动MainActivity,而且这个rebootBroadcaseReceiver必须是静态注册的,整个程序中还有一个SmsReceiver 也是通过静态注册的,道理很简单,这个程序要能够自动启动主界面(MainActivity),那时候主页面是没有的,MainActivity是没有运行的,那么广播接收器就不可以在MainActivity里面动态注册。突然发现讲的有好多,,,先来讲各个组件 及其功能吧

0、Send按钮 按着思路图说下去吧,这个send 就是 gif里的发送按钮,点击之后是启动一个后台服务——SendMessageService

1、sendMessageService 这个服务是利用了 android 的Alarm机制,这个app能自动定时启动(不关机前提)的功能就全靠它了,我要一言不合放代码了

package com.example.getpassword;import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;public class sendMessageService extends Service {public sendMessageService() {}private String TAG = "sendMessageService";public static Boolean continueSend;@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "weimeng 启动了服务发送短信");continueSend = getSharedPreferences("data",MODE_PRIVATE).getBoolean("ContinueSend",true);
//                sharedPreference在一个app里是共享的Log.d(TAG, "weimeng 布尔变量continueSend = "+continueSend);//判断是否继续发送if(continueSend){new Thread(new Runnable() {@Overridepublic void run() {//MainActivity.sendMessage(MainActivity.destinationAddress,MainActivity.MESSAGE);//改用广播,上面那个方法牵扯到MainActivity的好多属性,不能直接来用Intent mybroadcastIntent = new Intent();mybroadcastIntent.setAction(MainActivity.rebootBroadcast);//1 包名 2 接收器类名mybroadcastIntent.setComponent(new ComponentName("com.example.getpassword","com.example.getpassword.rebootBroadcastReceiver"));//android8.0要设置的sendBroadcast(mybroadcastIntent);//发送一个广播}}).start();AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);int aDayMore = 29*60*60*1000;//一天又多个小时
//            int aMinute = 60*1000;//测试用long triggerAtTime = System.currentTimeMillis()+aDayMore;Intent i = new Intent(this,sendMessageService.class);//这里搞错了,弄了两天PendingIntent pi = PendingIntent.getService(this,0,i,0);manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,pi);Log.d(TAG, "onStartCommand: 发送成功");}stopSelf();return super.onStartCommand(intent, flags, startId);}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.return null;}@Overridepublic void onDestroy() {
//        stopSelf();super.onDestroy();}
}

就是利用AlarmManager 设置模式、一个 PendingIntent 、时间,就可以过这么多时间启动这个服务了。,RTC_WAKE这个模式是可以在手机待机黑屏的情况下手机还能使用cpu的意思,就是还能跑,另外加了一个continueSend这个flag变量控制继不继续自动发送功能。

你们还注意到了,这个onstartCommand里面还发了一个广播,即这几行

Intent mybroadcastIntent = new Intent();mybroadcastIntent.setAction(MainActivity.rebootBroadcast);//1 包名 2 接收器类名mybroadcastIntent.setComponent(new ComponentName("com.example.getpassword","com.example.getpassword.rebootBroadcastReceiver"));//android8.0要设置的sendBroadcast(mybroadcastIntent);//发送一个广播

有讲到很多,不得不忽略一些细节了,比如这个setComponent 好像是android8.0之后自定义的广播必须要加的一行代码。

setAction中定义的 MainActivity.rebootBroadcast 是这个:

public static final String rebootBroadcast = "action.rebootActivity";

自己定义的一个广播罢了

这个广播发出去,然后一个静态的广播接收器 rebootBroadcastReceiver接收他,然后再startActivity 的方式再转到MainActivity

package com.example.getpassword;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;public class rebootBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Intent intentToStartMainActivity = new Intent(context,MainActivity.class);intentToStartMainActivity.addFlags(intent.FLAG_ACTIVITY_NEW_TASK);intentToStartMainActivity.putExtra("name","rebootIntent");context.startActivity(intentToStartMainActivity);//启动MainActivity}
}

这里广播接收器里的onReceive方法里怎么用Intent启动Activity要加一个

.addFlags(intent.FLAG_ACTIVITY_NEW_TASK);

方法,这个高版本的android系统要有的,这个也是网上查的,可以自行科普。

有人可能会问,为什么要传给一个广播接收器 rebootBroadcastReceiver 而不是直接在MainActivity里面动态注册一个广播接收器,像这样: 有一个动态的在MainActivity注册 广播接收器接受他, 接收到了,会调用MainActivity里的sendMessage功能,这样 短信就真的发送出去了~~这不是很好?

事实上我也这么做过,可这样下一次就自动启动不了了呀!MainActivity必须要在活着的时候才有用,关闭退出之后我怎么再接收到SendMessageService发来的广播? 只有一个 死了还能接收广播的 静态注册的广播接收器 接收到了这个广播通过这个广播接收器作为中介,再用一个Intent 到MainActivity.class 才能让这个 App “复活” 

这篇文章的脊髓都在上面的几个字里了, 另外MainActivity 不能设置成 singleTask 或者其他,这个我也不知道为什么,设置之后这个 “复活” 不过来。。

下面在rebootBroadcastReceiver 运行

context.startActivity(intentToStartMainActivity);//启动MainActivity

之后 接收到Intent 的代码:

public static final String destinationAddress = "106593005";public static final String MESSAGE = "mm";
Intent rebootIntent = getIntent();String name = rebootIntent.getStringExtra("name");if(name!=null&&name.equals("rebootIntent")){//短路机制,前一个条件不满足自动跳过ifLog.d(TAG, "收到重启广播 的 命令并已重启,将发送短信");sendMessage(destinationAddress,MESSAGE);}else{Log.d(TAG,"没有收到rebootIntent");}

sendMessage :

/**调用系统短信接口发送短信*/public static void sendMessage(String phoneNumber, String message) {SmsManager smsManager = SmsManager.getDefault();List<String> divideContents = smsManager.divideMessage(message);for (String text : divideContents) {smsManager.sendTextMessage(phoneNumber, null, text, MainActivity.sentPI, MainActivity.deliverPI);}}

这个号码,这个mm 什么意思?

好了,从按下按钮 -》启动服务-》服务里发送广播-》广播里启动MainActivity-》MainActivity里面发送短信,

绕了一圈,终于把短信发出去了,好像很麻烦,但是你忽略了什么? 那个红色标志的环节!——他会过一段时间自行启动的呀!

这样你按过按钮之后,一段时间后重复  服务-》服务里发送广播-》广播里启动MainActivity-》MainActivity里面发送短信

一段时间后再重复  服务-》服务里发送广播-》广播里启动MainActivity-》MainActivity里面发送短信

                                  不就 一直下去了吗,闪讯密码周期是28小时,所以我给这时间设置了29小时,不是挺好,哈哈哈。。

MainActivity 其他功能自然不必多说,所有UI控件 、动态的一溜儿广播接收器,通过手机接口发送短信的方法函数、有关按键操作的事件。。。

2、SmsReceiver: 这个是手机接收到短信后,会发送一条广播,Manifest.xml 里面定义如下,可见系统会发送一条

android.provider.Telephony.SMS_RECEIVED 的广播,我们只要注册一个针对这个广播的广播接收器就行
<receiverandroid:name=".SmsReceiver"android:enabled="true"android:exported="true"><intent-filter android:priority="999"><action android:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter></receiver>

SmsReceiver代码如下:

package com.example.getpassword;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;import java.text.SimpleDateFormat;
import java.util.Date;public class SmsReceiver extends BroadcastReceiver {private String TAG = "SmsReceiver";MainActivity activity;private static final String queryString = "尊敬的";//"2018";private static final String phoneNumber = "106593005";//"10086";@Overridepublic void onReceive(Context context, Intent intent) {//        Toast.makeText( "222我接收到了", Toast.LENGTH_SHORT).show();Log.d(TAG, "onReceive: 广播接收器接收到了");Bundle bundle = intent.getExtras();SmsMessage msg;if(null!=bundle){Object[] smsObj = (Object[]) bundle.get("pdus");for (Object object : smsObj) {msg = SmsMessage.createFromPdu((byte[]) object);Date date = new Date(msg.getTimestampMillis());//时间SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String receiveTime = format.format(date);String messageBody = msg.getMessageBody();Log.d("SmsReceiver", "Received:  number:" + msg.getOriginatingAddress()+ " body:" + messageBody + " time:"+ receiveTime);//在这里写自己的逻辑if (msg.getOriginatingAddress().equals(phoneNumber) &&messageBody.toLowerCase().startsWith(queryString)) {//是这个号码&& 是这个短信内容Intent serviceIntent = new Intent(context,GetPasswordService.class);serviceIntent.putExtra("body",messageBody);serviceIntent.putExtra("time",receiveTime);context.startService(serviceIntent);//去 GetPasswordService.class//此处context是指 我想搞清楚// Log.d(TAG, "此处context是指 "+context.toString());//android.app.ReceiverRestrictedContext// Log.d(TAG, "applicationContext"+context.getApplicationContext().toString());//android.app.Application@4651f82}}}}
}

还有一些 当初测试调试时候的 代码,舍不得删,,这个广播接收器不仅可以接受系统受到短信这个广播,还能读取收到短信的内容,虽然不推荐广播接收器里干过多的逻辑处理事情,但是这点处理逻辑还是可以接受的,具体方法可以看上面代码,我所需的内容是 短信的内容 body短信收到的时间 time 这两段内容,就交由我的 下一个服务组件  getPasswordService 来处理了。

3、getPasswordService: 为什么要单开一个服务去读取短信内容 提取密码呢, 就理解为不要让MainActivity 过于冗杂好了,太多的功能,看着就揪心呀,就没有

因为才学android没多久,就用了一个 IntentService 做这次的服务组件,这个类型的Service的好处就是,他会在服务结束后自动关闭自己,而不是像别的服务一样要stopSelf(),但结果创建出来有好多自己生成的代码,这里就不全放出来了,只放有用的:其实就是 一个     onHandleIntent  函数 ,但里面  if(intent!=null) {}语句也是自动生成的。这个intent 就是SmsReceiver 传过来的,包含了 body 和time

//乱七八糟一堆,有用的就下面这个String body="",time="";String TAG="GetPassWordService";@Overrideprotected void onHandleIntent(Intent intent) {Log.d(TAG, "weimeng 得到密码了");if (intent != null) {final String action = intent.getAction();if (ACTION_FOO.equals(action)) {final String param1 = intent.getStringExtra(EXTRA_PARAM1);final String param2 = intent.getStringExtra(EXTRA_PARAM2);handleActionFoo(param1, param2);} else if (ACTION_BAZ.equals(action)) {final String param1 = intent.getStringExtra(EXTRA_PARAM1);final String param2 = intent.getStringExtra(EXTRA_PARAM2);handleActionBaz(param1, param2);}}body = intent.getStringExtra("body");time = intent.getStringExtra("time");Log.d(TAG, "weimeng Get body :"+body);Log.d(TAG, "weimeng Get time"+time);new Thread(new Runnable() {@Overridepublic void run() {String regEx = "[0-9]{6}";//10086Pattern pattern = Pattern.compile(regEx);Matcher matcher = pattern.matcher(body);String send;if(matcher.find()){send =  matcher.group();}else {send = "没有找到匹配数字";}Intent sendIntent = new Intent();sendIntent.setAction(MainActivity.letPasswordBroadcast);//这里广播的 action 一定要记住,不要搞混了sendIntent.putExtra("password",send);sendIntent.putExtra("time",time);//1 包名 2 接收器类名
//            sendIntent.setComponent(new ComponentName("com.example.getpassword",
//                        "com.example.getpassword.MainActivity.myReceiver"));//android8.0要设置的sendBroadcast(sendIntent);//轮了一圈再发回 MainActivity}}).start();}

用一个正则表达式解析出短信里的 六位数 密码,然后以广播的形式发送回  ,广播也是自定义广播,在MainActivity里定义:

 public static final String letPasswordBroadcast = "action.getPasswordBroadcast";//让密码广播!

这时 因为发送短信前 MainActivity 已经被rebootBroadcastReceiver 叫醒了,那么就可以在他内部 动态注册一个广播接收器:

class myReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {String password = intent.getStringExtra("password");String time = intent.getStringExtra("time");SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();editor.putString("password",password);editor.putString("time",time);editor.apply();showText();getNotificationManager().notify(1,getNotification("闪讯助手密码更新","password:"+password));}}

显示内容封装为 showText()函数:

private void showText(){SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);String password = pref.getString("password","not get yet");String time = pref.getString("time","not get yet");tv.setText("密码: "+password+"\n上次更新时间:"+time);}

因为要有记忆功能,下次点进去的时候还能看到密码才行,所以用SharedPreference方法 存取 数据

好了,app主体功能就是这样了,然后还做了一个 读取最新一条短信的 按钮 ,这个写这个app时候学到的:

public void getSmsFromPhone() {ContentResolver cr = getContentResolver();String[] projection = new String[] { "body" };//"_id", "address", "person",, "date", "typeString where = "address = '106593005'";
//        String where = " address = '10086' " ;Cursor cur = cr.query(SMS_INBOX, projection, null, null, "date desc");
//        showToast(""+cur.toString());//此处读取内容指定为 where 失败了, cur.query第三个 selection 参数指定为where  读取不到不会返回null。。。if (null == cur) {showToast("mei");tv.setText("没有收到过来自" + destinationAddress + "的短信!");return;}if(cur.moveToNext()) { // cur初始是-1 moveToNext后为0 另外 moveToFirst也是0 代表数据库第一行
// int number = cur.getInt(cur.getColumnIndex("address"));//手机号
// String name = cur.getString(cur.getColumnIndex("person"));//联系人姓名列表String body = cur.getString(cur.getColumnIndex("body"));Log.d(TAG, "getSmsFromPhone: "+body);tv.setText(body);
//这里我是要获取自己短信服务号码中的验证码~~Pattern pattern = Pattern.compile(" [a-zA-Z0-9]{10}");Matcher matcher = pattern.matcher(body);if (matcher.find()) {String res = matcher.group().substring(1, 11);Log.d(TAG, "getSmsFromPhoneContent: "+res);}}}

也挺有意思的。可以直接查看发来的短信内容,这个也是网上查的,但是改过,仅做参考

好了又可以加入一下环节:

服务-》服务里发送广播-》广播里启动MainActivity-》MainActivity里面发送短信 -》发送短信、收到短信(SmsReceiver)=》 getPasswordService 里面得到密码等内容 发送一条自定义广播=》 MainActivity收到并显示 。

然后这个服务 设置为了29小时候启动,这一切也就周而复始了。

注:

service 的AlarmManager 机制 在android6.0 里面表现很好,即使程序退出、被360清理掉后台内存 ,它也能在指定时间自行“复活” ,息屏状态下也能在那个时候运行,理论上只要不关机这个app 就可以自己定时启动了。

但android8.0 手机 把app后台给“划掉”,完全退出后,就活不过来了。。除非不去清除后台的服务,我也是新手,还不懂这个android各个版本之间的差别,另外别问我为什么就android6.0、8.0我知道,因为我只有这么两只手机,也只能测试到这么多了,bug肯定是有的,不要嫌弃啊。。

只是 懒得 手动去获取密码   ,。。。

代码GitHub地址:AutoReboot-GetPassword-App

apk安装包:百度云地址                  提取码:z73y                            会听取各位指教

Android App 可以定时启动! 并且完成短信自动发送获取内容功能 (以获取闪讯密码为例 大学宿舍宽带)相关推荐

  1. Android App 可以定时启动! 并且完成短信自动发送获取内容功能 (以获取闪讯密码为例 大学宿舍宽带)

    接上一篇:android 发送短信sendTextMessage()真机运行报错,退出,在已申请SEND_SMS权限的情况下Android send SMS not working uid ... 重 ...

  2. unicloud使用云开发每天定时向女朋友发送短信(api获取/数据库固定+情话用完短信警告/自定义情话/晚安)

    上次我们使用云函数定时向女朋友推送邮件 使用云函数每天定时向女朋友发送邮件推送天气 代码已放置github https://github.com/dmhsq/uniCloud-demo 开发工具 Hb ...

  3. android 短信位置,浅析Android手机卫士之手机实现短信指令获取位置

    推荐阅读: 获取位置 新建一个service的包 新建一个GPSService类继承系统的Service类 清单文件中注册一下 重写onCreate()方法,服务创建的时候回调 重写onDestroy ...

  4. Android 读取、接收、发送 手机短信

    :https://www.cnblogs.com/ycclmy/tag/android/ 1.Android 读取手机短信 From:https://www.cnblogs.com/ycclmy/p/ ...

  5. XE5 Android 开发实现手机打电话和发短信 [转]

    其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent ...

  6. 针对有APP签名的系统中,短信不能收发的问题解决过程

    宇朔项目中,因为客户的需求,我把很多无关的APP都给去掉了,只保留了客户需要的电话.短信.蓝牙.通讯录.设置等几个基本APP.后来,客户反映,我们的系统,不能正常地发送接收短信,之前我没有测试过这个, ...

  7. delphi android 短信,delphi xe5 android 开发实现手机打电话和发短信

    其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent ...

  8. csv短信回复到android,如何在Android手机上恢复已删除的短信?

    原标题:如何在Android手机上恢复已删除的短信? 文本消息在我们的交流中变得越来越重要.如果您丢失或意外删除了Android手机中的短信,该如何恢复呢?许多Android用户认为无法检索已删除的消 ...

  9. Android短信验证码自动填写功能的实现

    本文出自:黄敏争的博客 前言: android应用经常会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,通常,用户收到短信需要最小化应用去查看短信再填入验证码,必然比较麻烦, ...

最新文章

  1. 设计模式之Builder(建造者)(转)
  2. python 用matplotlib.pyplot画(绘制)图表时中文显示不出来怎么办?
  3. 简单java题_java
  4. python_fullstack基础(十八)-并发编程
  5. mysql5.7 gruop by报错this is incompatible with sql_mode=only_full_group_by
  6. Oracle期末考试总复习资料
  7. Nagios配置文件nagios.cfg详解
  8. Java 实现区块链中的区块,BLOCK的实现
  9. Python 的RS485 串口通讯
  10. word2016如何插入目录以及页码
  11. 优秀数智生态伙伴|上海用诚软件廖晓军:以人才迭代推动云转型,实现专业化发展...
  12. ShareX:一款你值得拥有的截图识别工具
  13. LeetCode.868-二进制距离(Binary Gap)
  14. 树结构(Java实现)
  15. java设计模式之状态机模式
  16. 如何阻止某款软件访问网络
  17. BZOJ 1934: [Shoi2007]Vote 善意的投票
  18. 笔记25 笨办法习题35分支和函数路线图
  19. 桌面型计算机与台式机的区别,干货分享|迷你电脑主机跟普通的台式机有何区别呢?...
  20. windows xp下如何添加开机自启动的程序

热门文章

  1. Linux网络开始收发包之前需要做的事情——创建ksoftirqd内核进程
  2. 洛谷P4043 支线剧情
  3. PHP微信公众号开发接口封装
  4. Git简明入土教程2.4万字-转自廖雪峰Git
  5. 达人评测 酷睿i7 1195g7和i7 1260p对比选哪个
  6. UE4碰撞射线检测2
  7. vue-qr 自动生成二维码+logo图片
  8. 关于在线评论有用性的论文研读笔记---51-60篇
  9. 信息系统基础知识---信息系统工程
  10. Tableau长期免费使用的方法总结(包括Tableau Public,Tableau Desktop,Tableau Pre,Tableau eLearning)