Android实例-手机安全卫士(三十三)-将数据库导入程序中
一、目标
1、 在项目中引入SQLiteDatabase数据库,通过输入输出流将数据库复制至指定path目录下;
2、通过SQLiteDatabase的openDatabase()使用数据库,通过 rawQuery()方法执行SQL语句;
3、初步实现号码归属地查询功能。
数据库结构:
data1: data2:
数据库在项目中: 查询结果:
二、代码实现
1、将电话归属地数据库(名称address.db)拷贝至项目的assets文件夹下;
2、在项目src文件夹中新建数据库工具包(取名com.example.mobilesafe.db.dao),在其下面新建工具类(取名NumberAddQueryUtils);
3、在工具类(NumberAddQueryUtils)中新建返回值类型为String的静态方法(取名queryNumber),该方法需传入String类型的参数number;
4、在新建方法(queryNumber)中通过数据库对象SQLiteDatabase的openDatabase(String path, CursorFactory factory, int flags)方法打开数据库,第二个参数factory表示工厂可用null表示默认,第三个参数flags表示打开方式(可用SQLiteDatabase下的OPEN_READONLY表示以只读的形式打开),对于第一个参数path表示需要加载的数据库的路径,由于SQL数据库无法通过file的形式加载,因此需要在程序初始化的时候将数据库拷贝至系统的data/data/包名(此例为com.example.mobilesafe)/files/数据库名称.db(详见第8步介绍),此时path的值就为“data/data/com.example.mobilesafe/files/address.db”,path定义成类的静态成员变量,并赋值;
5、第4步openDatabase()方法返回的是一个SQLiteDatabase对象(取名database),通过SQLiteDatabase对象(取名database)的rawQuery(String sql, String[] selectionArgs)方法执行数据库查询语句,参数String sql表示需要执行的SQL语句(这个需要根据数据库的特征编写,此例中为select location from data2 where id = (select outkey from data1 where id = ?)),参数String[] selectionArgs为SQL语句中所有?所对应的参数组成的数组(需要通过new实例化,此例为queryNumber方法传入的参数number),由于number为11位,而数据库中只有7位,因此可通过String对象(number)的substring(int start, int end)方法截取前7位数(即0,7,其中7取不到)
6、第5布中通过rawQuery()方法返回Cursor对象(取名cursor),采用while语句判断Cursor对象(cursor)是否还有下一项(moveToNext()),如果有,则在While语句中通过Cursor对象(cursor)的getString(int columnIndex)方法得到归属地,参数int columnIndex为归属地信息所在列的列号;该方法返回String类型的对象(取名location),由于数据库数据不完整或更新不及时,存在某些号码无归属地,所以在queryNumber方法开头先实例化location,并使其值等于号码,当在While语句中查询到归属地时再将值赋给location;
7、通过Cursor对象(cursor)的close()方法释放Cursor对象资源,最后返回location;
queryNumber(String number)方法代码;
1 public static String queryNumber(String number) { 2 3 String location = number; 4 5 SQLiteDatabase database = SQLiteDatabase.openDatabase(path, null, 6 SQLiteDatabase.OPEN_READONLY); 7 Cursor cursor =database.rawQuery( 8 "select location from data2 where id = (select outkey from data1 where id = ?)", 9 new String[] { number.substring(0, 7) }); 10 while (cursor.moveToNext()) { 11 location = cursor.getString(0); 12 } 13 cursor.close(); 14 return ""; 15 }
8、程序初始化时将assets文件夹下的数据库拷贝至制定目录中。在初始化界面(SplashActivity)中新建拷贝数据库至path目录下的方法(取名copyDB()),该方法类型为private。在copyDB()方法中:
(1)通过new File(File dir, String name)方法实例化一个File对象(取名file),参数File dir为文件创建的目录(本例中目录为程序下的file文件中,因此可以通过getFilesDir()方法得到),参数String name为文件名(本例中与拷贝的文件名一致,即open方法中的文件名);
(2)通过getAssets()方法(其实是在content下,此处省略了this)的open(String fileName)方法打开assets文件夹下制定文件名(参数String fileName)的文件,从而得到InputStream对象(取名is)。由于本方法存在异常,所以通过try...catch...语句将异常捕获;
(3)通过new FileOutputStream(File file) 方法实例化一个文件输出流对象FileOutputStream(取名fos),参数File file为(2)中创建的File对象;
(4)通过new实例化一个长度为1024的byte数组对象(取名buffer),再定义值为0的int对象(取名len);
(5)通过InputStream对象(is)的read(byte[] buffer)方法读取其长度并将结果赋值给int对象(len),参数byte[] buffer为(4)中的数组对象,通过while语句判断读取到的结果是否为-1来执行相关操作(当InputStream对象中无数据时,此时会返回-1),如果判断条件不等于-1成立,则执行while语句;
(6)在while语句中通过FileOutputStream对象(fos)的write(byte[] buffer, int byteOffset, int byteCount) 方法将InputStream对象中的数据写至FileOutputStream对象中,参数byte[] buffer为字节数据,即(4)中的buffer,参数int byteOffset表示从buffer的起始位置(为0),参数int byteCount表示从buffer中写入流中的长度(为len)
(7)关闭输入流InputStream对象(is)和输出流FileOutputStream对象(fos);
(8)优化拷贝方法。因此在打开程序执行初始化时总会执行拷贝数据方法(copyDB()),这样会出现重复拷贝,所以在copyDB()方法中创建file文件(即上面的第(1)步)后通过File对象的exists()方法和length()方法判断文件是否存在且长度大于零,如果存在则打印日志或者输出“初始化完成”等语句等均可,如果不存在则执行上述第(1)至第(7)步;
拷贝数据库至指定目录代码:
1 private void copyDB() { 2 try { 3 File file = new File(getFilesDir(), "address.db"); 4 if(file.exists()&&file.length()>0){ 5 //已经存在无需拷贝 6 System.out.println("数据库已经拷贝完成"); 7 }else{ 8 //拷贝数据库 9 InputStream is = getAssets().open("address.db"); 10 FileOutputStream fos = new FileOutputStream(file); 11 byte [] buffer = new byte[1024]; 12 int len = 0; 13 while((len=(is.read(buffer)))!=-1){ 14 fos.write(buffer, 0, len); 15 } 16 is.close(); 17 fos.close(); 18 } 19 } catch (IOException e) { 20 e.printStackTrace(); 21 } 22 }
9、在“号码归属地查询页面”(NumberAddQueryActivity)的“查询号码归属地”方法(numberAddQuery)中判断输入文本框不为空(即else语句中)后,通过工具类(NumberAddQueryUtils)中的queryNumber(String number)方法将前面的电话号码(phone_number)传入,返回一个String类型的地址值(取名address);
10、通过TextView对象(show_number_add)的setText(CharSequence text)方法将9中取得值填入,从而查的号码归属地。
使用工具类中的方法代码:
1 //去数据库查询号码归属地 2 String address = NumberAddQueryUtils.queryNumber(phone_number); 3 show_number_add.setText(address);