为了记录同学们的学习时长以及签到情况,学院说要我弄一个签到签离助手吧。首先想到的是GitHub,去上面搜寻了一番发现真正能直接拿来用的寥寥无几,可能是因为我比较菜鸡。然后打算自己写,花了两天时间写完前后端以及app,本文主要介绍一下开发原理。没有用到任何框架,都是简单代码。不擅长安卓,班门弄斧啦,仅供学习参考!
界面展示
|
使用思路
同学打开App就进入登录页面进行注册登录,一次登录后如果不退出下次就不需要再次登录,除非主动退出登录。学生注册信息学号作为登录账号,手机可用于找回密码等等。一切准备好了之后当学生点击登录时,如果没有连接指定wifi则提示“请连接指定wifi,签到失败!”,如果已经连接指定wifi则将签到按钮设置为不可点击状态,释放签离按钮为可点击状态,此时记录下当前时刻即为签到时刻,保存到本地手机存储中。当点击签离时同样进行指定wifi判断,如果正确则获取此刻的签离时间,立即计算出签到、签离的时间差即为本次签到的学习时长。然后再释放签到按钮为可点击状态,设置签离按钮为不可点击状态。
面临的问题
1、避免在后台运行activity进程被杀死。
这里解决方法比较多,可以直接百度解决,但是有些手机还需要在省电管理里面加入白名单或者锁定后台。
2、android 9 以后的手机获取wifi信息需要各种授权,且授权方式发生了很大改变,只有授权开启定位信息才可能获取wifi MAC地址成功。
我只能说网上那些解决“获取wifi信息失败”的博客都是渣渣(建议不看,解决不了问题的,可能是过时了),毫无营养的垃圾,浪费时间生命。这里给一句话如果获取不到wifi或者路由器的信息一定是没有权限,没有动态申请用户权限。参考郭霖的权限解决方案一定能够解决的。这才是正确的方法,按照官方的思路去做,不要投机取巧瞎扯。
3、android 绝对不能使用代码直接访问数据库,需要搭建后台,通过服务器回传进行数据修改。
网上所有说“android访问mysql数据库”的博客都是垃圾文章,不要看,没有任何用处,如果你敢在android里面直接用sql语句,在企业三天之内必定滚蛋。谷歌强烈不建议这样子做,而且极其危险,相当于裸奔。最最最重要的是android7以上新版本不支持,而且报错不会直接说,让你怀疑人生为什么别人的博客是这样的可以,那是因为他们可能是低版本老版本android。
设计思路
1、获取wifi(路由器)的MAC地址确定唯一wifi,这里权限申请非常恶心,请参考郭霖大神的博客,不要参考那些垃圾博客,真的是浪费生命而且解决不了。android真的非常恶心,各种版本更新不兼容,还要适配各种机型,可能这就是开源的缺点吧。
2、数据库,我选择的是MySql数据库,当然可以选择SQLServer数据库等等。适合自己就好。
2、搭建后台,这里可以选择asp.net 或者 java 搭建。主要就是通过安卓发送http请求,然后返回请求结果,在服务器上处理请求。比如注册,将学生注册信息通过http请求传给服务器后台指定程序,后台指定程序进行处理——将学生注册数据插入到数据库中然后返回1(成功)或者0(失败)。所以我们的SQL语句实在服务器上运行的,不是在APP,APP把它理解为html,只是作为前端界面交互功能。当然不能完全等价于html。
反思总结
1、http传输信息非常不安全,不过考虑到我们的用途,安全性要求不高,没有谁这么无聊会去搞事情。如果在企业请用https协议。
2、可以改进为自动签离,无需点击。实现方法:使用android的server(服务)功能,使用线程不断检测wifi连接情况,如果断开wifi就立即签离。同样要保证服务不要被杀死。
3、还有其他问题后续再更新,希望大家有什么意见与建议多多提出!谢谢!
项目文件涉及到了太多学校私密信息,为了服务器安全性着想就不再给大家了。有什么困难可以解答哈!
难点参考
1、申请权限代码(不全,请参考郭霖的博客,不要照抄下面的没有用)
@Override
public int getPermissionsRequestCode () {
return 0;
}
@Override
public String[] getPermissions () {
return mPermissions;
}
@Override
public void requestPermissionsSuccess () {
// Toast.makeText(this, "成功授予权限!", Toast.LENGTH_SHORT).show();
Log.d("555", "requestPermissionsSuccess: "+"成功授予权限!");
}
@Override
public void requestPermissionsFail() {
StringBuilder sb = new StringBuilder();
mPermissions = PermissionUtil.getDeniedPermissions(this, mPermissions);
for (String s : mPermissions) {
if (s.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
sb.append("定位权限(用于获取您的位置信息;\n");
}
}
PermissionUtil.PermissionDialog(this, "程序运行需要如下权限:\n" + sb.toString() + "请在应用权限管理进行设置!");
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] mPermissions, int[] grantResults) {
if (permissionHelper.requestPermissionsResult(requestCode, mPermissions, grantResults)) {
//权限请求结果,并已经处理了该回调
return;
}
super.onRequestPermissionsResult(requestCode, mPermissions, grantResults);
}
2、实现签到的Http请求
//签到功能
public boolean sign(String st_id,String start) throws InterruptedException {
is=false;
final int[] flag = {1}; //线程同步问题,控制线程同步,必须执行了子线程才能返回is值
new Thread(new Runnable() {
@Override
public void run() {
String path="http://服务器地址:端口号/Base1/Sign?st_id="+st_id+"&start="+start;
Log.d("qqqqqq", path);
URL url = null; //新建url并实例化
try {
url = new URL(path);
} catch (MalformedURLException e) {
e.printStackTrace();
}
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
try {
connection.setRequestMethod("GET");//获取服务器数据
} catch (ProtocolException e) {
e.printStackTrace();
}
connection.setReadTimeout(8000);//设置读取超时的毫秒数
connection.setConnectTimeout(8000);//设置连接超时的毫秒数
InputStream in = null;
try {
in = connection.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String result = null;//读取服务器进行逻辑处理后页面显示的数据
try {
result = reader.readLine();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
Log.d("qqqqqq","run: "+result+"");
if (result.equals("1")){
is=true;
}
// flag[0] =0;
connection.disconnect();
}
}).start();
// while(flag[0]==1){
// Log.d("isisisis", "isLogin: "+is);
// }
Thread.sleep(1000);
return is;
}