Android App 可以定时启动! 并且完成短信自动发送获取内容功能 (以获取闪讯密码为例 大学宿舍宽带)
接上一篇: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 可以定时启动! 并且完成短信自动发送获取内容功能 (以获取闪讯密码为例 大学宿舍宽带)相关推荐
- Android App 可以定时启动! 并且完成短信自动发送获取内容功能 (以获取闪讯密码为例 大学宿舍宽带)
接上一篇:android 发送短信sendTextMessage()真机运行报错,退出,在已申请SEND_SMS权限的情况下Android send SMS not working uid ... 重 ...
- unicloud使用云开发每天定时向女朋友发送短信(api获取/数据库固定+情话用完短信警告/自定义情话/晚安)
上次我们使用云函数定时向女朋友推送邮件 使用云函数每天定时向女朋友发送邮件推送天气 代码已放置github https://github.com/dmhsq/uniCloud-demo 开发工具 Hb ...
- android 短信位置,浅析Android手机卫士之手机实现短信指令获取位置
推荐阅读: 获取位置 新建一个service的包 新建一个GPSService类继承系统的Service类 清单文件中注册一下 重写onCreate()方法,服务创建的时候回调 重写onDestroy ...
- Android 读取、接收、发送 手机短信
:https://www.cnblogs.com/ycclmy/tag/android/ 1.Android 读取手机短信 From:https://www.cnblogs.com/ycclmy/p/ ...
- XE5 Android 开发实现手机打电话和发短信 [转]
其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent ...
- 针对有APP签名的系统中,短信不能收发的问题解决过程
宇朔项目中,因为客户的需求,我把很多无关的APP都给去掉了,只保留了客户需要的电话.短信.蓝牙.通讯录.设置等几个基本APP.后来,客户反映,我们的系统,不能正常地发送接收短信,之前我没有测试过这个, ...
- delphi android 短信,delphi xe5 android 开发实现手机打电话和发短信
其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent ...
- csv短信回复到android,如何在Android手机上恢复已删除的短信?
原标题:如何在Android手机上恢复已删除的短信? 文本消息在我们的交流中变得越来越重要.如果您丢失或意外删除了Android手机中的短信,该如何恢复呢?许多Android用户认为无法检索已删除的消 ...
- Android短信验证码自动填写功能的实现
本文出自:黄敏争的博客 前言: android应用经常会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,通常,用户收到短信需要最小化应用去查看短信再填入验证码,必然比较麻烦, ...
最新文章
- 设计模式之Builder(建造者)(转)
- python 用matplotlib.pyplot画(绘制)图表时中文显示不出来怎么办?
- 简单java题_java
- python_fullstack基础(十八)-并发编程
- mysql5.7 gruop by报错this is incompatible with sql_mode=only_full_group_by
- Oracle期末考试总复习资料
- Nagios配置文件nagios.cfg详解
- Java 实现区块链中的区块,BLOCK的实现
- Python 的RS485 串口通讯
- word2016如何插入目录以及页码
- 优秀数智生态伙伴|上海用诚软件廖晓军:以人才迭代推动云转型,实现专业化发展...
- ShareX:一款你值得拥有的截图识别工具
- LeetCode.868-二进制距离(Binary Gap)
- 树结构(Java实现)
- java设计模式之状态机模式
- 如何阻止某款软件访问网络
- BZOJ 1934: [Shoi2007]Vote 善意的投票
- 笔记25 笨办法习题35分支和函数路线图
- 桌面型计算机与台式机的区别,干货分享|迷你电脑主机跟普通的台式机有何区别呢?...
- windows xp下如何添加开机自启动的程序