背景
公司同事身处不同省份,都需要访问一个甲方的系统,登录系统需要短信验证码。手机在技术手上,经常打断工作去看验证码实在是影响工作效率,因此结合前面开发的QQ机器人,结合APP实现转发短信到服务器,再由QQ机器人发到群里的功能。
前提
公网IP
非鸿蒙系统(实现方式不同)
安卓版本低于8.0(API 26)(对静态广播有限制)
目前发现的问题
APP退出时间长了以后收不到静态广播,推测是被回收掉了。
傻瓜式解决方案:APP置于前台不退出
根源式解决方案:进程保活
技术实现
BroadcastReceiver + socket + python
因为是给QQ机器人接入的新功能,所以采用socket的方式。
换个思路也可以做一个API,接收到短信以后保存到数据库里,通过python定时访问数据库检查是否有新的短信,有就发出来。考虑到验证码的时效性,这个查询的周期会比较短,一个程序定时高频率操作数据库感觉不够奈斯。
代码
Android部分
main.class随便写写就行了,反正也不用。
SmsRecevier.class
静态注册一个BroadcastReceiver,监听短信广播,然后通过socket发送给服务器。
静态注册的广播,APP退出以后会继续工作。
public class SmsRecevier extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();//通过getExtras()方法获取短信内容
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");//通过关键词“pdus”从Bundle实例中获取到短信字节数组—pdus
if (pdus == null) {
return;
}
SmsMessage[] messages = new SmsMessage[pdus.length];//把短信字节数组—pdus转换进->SmsMessage[]
for (int i = 0; i < messages.length; i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String format = bundle.getString("format");
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
} else {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
try {
String msgStr = messages[i].getMessageBody();//短信内容
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String t1 =simpleDateFormat.format(new Date(messages[i].getTimestampMillis())) //发送时间
String s1 =messages[i].getOriginatingAddress()) //发件人手机号
transport(msgStr);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void transport(String m) {
final String message = m + "\r\n";//数据的结尾加上换行符才可让服务器端的readline()停止阻塞
try {
new Thread() {
@Override
public void run() {
try {
String host = "1.2.3.4";
int port = 1234;
Socket socket = new Socket(host, port);
if (socket.isConnected()) {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(message.getBytes("UTF-8"));
outputStream.flush();
outputStream.close();
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
super.run();
}
}.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
AndroidManifest.xml
添加权限和静态注册广播
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<application
…………
<receiver android:name="com.xxx.SmsRecevier" android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
</application>
这条权限不能少,没有这个权限无法读取信息内容:
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
pythton部分
和python+go-cqhttp搭建自动回复QQ机器人中的自动回复部分差不多,只是如果手机和服务器不在同一个局域网下,那么服务器上的socket监听的IP地址要写成服务器的公网地址,和BroadcastReceiver中socket发送消息的IP地址一样。