前言 大家好!欢迎继续来访我的网站。从今天起,我将在这个专题记录我的一个安卓项目实战,我将在此中分享一些个人经验见解,希望能带给大家一点帮助,这个项目将帮助学习安卓SQLite的使用、反射工程、广播服务等等,甚至一些设计模式相关知识(其实目前我也不太懂,只是在读《设计之禅》中形成一点意识,另外也强烈推荐此书)OK,就这样不多说,开始吧!
项目简介 首先介绍一下整个项目,这个项目是关于信息安全方面的,主要是后台悄悄采集用户的程序使用情况,然后将采集到的数据上传到服务器,以供服务器端分析利用数据。难点在于如何隐蔽在后台采集数据,具体需求如下:
需求
实时检测程序(包括一部分系统程序和所有的用户程序 )是否在运行,获取程序的运行信息
获取手机短信并以文件形式储存在SD卡等待上传
控制手机一些设备的开启关闭,比如蓝牙
项目框架 动工第一步便是搭建整个项目的框架,首先工程大体分为两大类,一个是引擎类,主要包含用于获取程序使用情况和获取短信等的操作类,另一个是服务类,包含广播接收者服务和管理各服务运行的服务。这两个大类都需要抽象出接口,便于后期维护扩展。笔者使用的是Android Studio开发,工程视图如下图所示: ![image](AP151116/1-PS.jpg 工程视图)
获取短信操作类 工程的框架搭好之后,我们先从简单的开始,就是获取短信的操作。这个类的工作分为两步,第一步获取短信,第二步则是保存数据。那么如何获取短信呢?学完安卓基础的都知道,安卓的通讯录、短信都是储存在SQLite数据的,所以获取短信就是简单的查询数据库操作。ok,开始吧。 首先提一下短信收件箱的URI,通过这个URI才可以连接上我们所需要的数据库表单:
1 2 3 4 private final String SMS_INBOX_URI = "content://sms/inbox" ;
以下是数据库查询操作:
1 2 3 4 Uri smsUri = Uri.parse(SMS_INBOX_URI); ContentResolver resolver = mContext.getContentResolver(); String[] projection = {"_id" , "address" , "person" , "body" , "date" , "type" }; Cursor cursor = resolver.query(smsUri, projection, null , null , "date desc" );
PS:在这里提一下,我在博客只是粘贴一些关键部分的代码,大家可以去我的[github](https://github.com/a-voyager/MoniterDemo github)网站上去看所有源码 数据库查询没什么讲的,就是查询{“_id”, “address”, “person”, “body”, “date”, “type”}这几列的内容,查询完成之后返回一个游标集cursor。 接下来便要遍历游标集,在此之前需要准备工作:建立一个实例类SmsEntity,用于保存每一个短信短信实例。
1 2 3 4 5 6 7 8 9 public class SmsEntity { private String name; private int id; private long person; private String body; private long date; private int type; ... }
准备工作做好之后就可以开始遍历并保存在List列表中了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int i = 1 ;SmsEntity sms = null ; List<SmsEntity> list = new ArrayList<>(); while (cursor.moveToNext()) { sms = new SmsEntity(); sms.setId(i++); sms.setBody(cursor.getString(3 )); sms.setDate(cursor.getLong(4 )); sms.setType(cursor.getInt(5 )); sms.setName(cursor.getString(2 )); sms.setPerson(cursor.getLong(1 )); Log.i(TAG, sms.toString()); list.add(sms); }
遍历完成之后当然就是保存文件咯,xml解析保存到SD卡就好了,见代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 private boolean saveToFile (List<SmsEntity> list) { FileOutputStream fos = null ; XmlSerializer xmlSerializer = Xml.newSerializer(); File fileDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + Constants.SMS_SAVE_FILE_DIR); if (!fileDir.exists()) fileDir.mkdirs(); File file = new File(fileDir, Constants.SMS_SAVE_FILE); try { fos = new FileOutputStream(file); xmlSerializer.setOutput(fos, "utf-8" ); xmlSerializer.startDocument("utf-8" , true ); xmlSerializer.startTag(null , "SMSs" ); xmlSerializer.attribute(null , "num" , String.valueOf(list.size())); for (SmsEntity sms : list) { xmlSerializer.startTag(null , "sms" ); xmlSerializer.attribute(null , "id" , String.valueOf(sms.getId())); xmlSerializer.startTag(null , "type" ); xmlSerializer.text(String.valueOf(sms.getType())); xmlSerializer.endTag(null , "type" ); xmlSerializer.startTag(null , "name" ); xmlSerializer.text(sms.getName() == null ? "" : sms.getName()); xmlSerializer.endTag(null , "name" ); xmlSerializer.startTag(null , "person" ); xmlSerializer.text(String.valueOf(sms.getPerson())); xmlSerializer.endTag(null , "person" ); xmlSerializer.startTag(null , "date" ); xmlSerializer.text(String.valueOf(sms.getDate())); xmlSerializer.endTag(null , "date" ); xmlSerializer.startTag(null , "body" ); xmlSerializer.text(sms.getBody()); xmlSerializer.endTag(null , "body" ); xmlSerializer.endTag(null , "sms" ); } xmlSerializer.endTag(null , "SMSs" ); xmlSerializer.endDocument(); } catch (Exception e) { Log.e(TAG, "saveToFile() failed" , e); return false ; } finally { try { if (fos != null ) fos.close(); } catch (IOException e) { Log.e(TAG, "fos close failed" ); } } return true ; }
Bingo!就这样,实现了查询短信并保存到文件的功能。
期间注意几个问题:
读取短信权限问题,记得加入和权限
安卓单元测试中,不能够直接在Test类中直接执行测试代码,因为此时Test类还没有构造完成,一些功能尚不完整,比如this.getContext()
xml解析过程start..()/end..()函数建议和打括号一样成对地写
特别注意由于IDE的自动补全功能太过智能,有可能会把安卓权限给大写,导致权限存在但不正确,也不会报错,只是无法执行相关操作,笔者表示在这里被害惨了-_-||待续…