我们都知道,android为了操作数据库,一般是继承SQLiteOpenHelper类,并实现他的三个函数。
如下所示:
package jz.his.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class MessageDataBase extends SQLiteOpenHelper { public MessageDataBase(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table lgx_table(_id integer primary key autoincrement ," + "name varchar(20),content varchar(40),time varchar(20) ,head varchar(20),isCheck byte)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
</pre><p>可以看到创建了一个名为lgx_table的表,里面有一id,name,content等列。</p><p></p>然后在Activity里,通过getWritableDatabase或者getReadableDatabase()方法来实例化一个SQLiteDatabase<p></p><p></p><pre name="code" class="java">MessageDataBase messageDataBase = new MessageDataBase(context, "lgx", null, 1); SQLiteDatabase database = messageDataBase.getWritableDatabase();
我们可以看到,创建了一个名字为“lgx”的数据库。
这里提出一个问题,通过以上的步骤后,数据库保存在哪里了呢?
数据库保存在data/data/[your packageName]/databses,
1.如果是模拟器,直接通过Eclipse下,通过这样的步骤去看 DBMS--->File Explorer-->data---->data--->your packageName
网上很多介绍,我这里不介绍。
2.如果是真机,首先这个真机是root过了,下载一个Root Explorer。我的测试机是华为荣耀3c。
当我们执行完了以上的步骤后,进入data/data/jz.his.jzhis/databases/会看到这样的情景。
其实lgx就是我们刚刚创建的数据库,lgx-journal是数据库日志。
现在我们知道了,数据库存储的位置了,你以为这就是我写这篇博客的目的?继续往下看吧,嘿嘿。
如果我不想再手机系统内存中保存数据库,而是想将我的数据库放在手机sd卡中,那应该怎么做呢。
首先,我在res/raw中放一个现成的数据库,待会在代码里,将它拷入手机sd卡中。
看下面的代码:
package com.example.province; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.os.Environment; public class CopyOfCityInfoDataSupport2 { private static CopyOfCityInfoDataSupport2 cityInfoDataSupport; /** * 数据库在手机里的路径 */ private static String DATABASE_PATH = Environment .getExternalStorageDirectory() + "/aaaaa/"; /** * 数据库的名称 */ public static final String dbName = "mzk_db"; private SQLiteDatabase mSDB; public static CopyOfCityInfoDataSupport2 getInstance(Context context) { initDataBase(context); if (cityInfoDataSupport == null) { cityInfoDataSupport = new CopyOfCityInfoDataSupport2(); } return cityInfoDataSupport; } /** * 初试化数据库 */ private static void initDataBase(Context context) { boolean dbExist = checkDataBase(); if (dbExist) { } else { // 如果不存在,则将raw里的数据存入手机sd卡 copyDataBase(context); } } /** * 复制数据库到手机指定文件夹下 * * @throws IOException */ private static void copyDataBase(Context context) { String databaseFilenames = DATABASE_PATH + dbName; File dir = new File(DATABASE_PATH); FileOutputStream os = null; InputStream is = null; // 判断文件夹是否存在,不存在就创建一个 if (!dir.exists()) { dir.mkdirs(); } try { // 得到数据库的输出流 os = new FileOutputStream(databaseFilenames); // 得到数据文件的输入流 is = context.getResources().openRawResource(R.raw.mzk_db); byte[] buffer = new byte[8192]; int count = 0; while ((count = is.read(buffer)) != -1) { os.write(buffer, 0, count); os.flush(); } // 之所以不在这里初始化,是因为这边是静态的方法,而mSDB并没有设置为静态的,也不推荐设为静态的 // mSDB = SQLiteDatabase.openOrCreateDatabase(DATABASE_PATH + // dbName, null); } catch (Exception e) { e.printStackTrace(); } finally { try { os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 判断数据库是否存在 * * @return */ private static boolean checkDataBase() { SQLiteDatabase checkDB = null; String databaseFilename = DATABASE_PATH + dbName; // 要自己加上try catch方法 try { // 返回最新的数据库 checkDB = SQLiteDatabase.openDatabase(databaseFilename, null, SQLiteDatabase.OPEN_READONLY); } catch (SQLiteException e) { // TODO: handle exception } if (checkDB != null) { checkDB.close(); } // 如果checkDB为null,则没有数据库,返回false return checkDB == null ? false : true; } /** * 查询所有省份的信息 * * @return 省份信息 */ public ArrayList<City> queryProvince() { // 创建数据库的实例 mSDB = SQLiteDatabase .openOrCreateDatabase(DATABASE_PATH + dbName, null); ArrayList<City> list = new ArrayList<City>(); String sql = "select * from fs_province"; Cursor cursor = mSDB.rawQuery(sql, null); while (cursor.moveToNext()) { City city = new City(); String id = cursor.getString(cursor.getColumnIndex("ProvinceID")); String name = cursor.getString(cursor .getColumnIndex("ProvinceName")); city.setName(name); city.setId(id); list.add(city); } if (cursor != null) { cursor.close(); } return list; } public void closeDataBase() { if (mSDB != null) { mSDB.close(); } } }
我们看到,如果将数据库写到手机sd卡中,都不需要SQLiteOpenHelper类了,而是直接通过
mSDB = SQLiteDatabase.openOrCreateDatabase(DATABASE_PATH + dbName, null);就可以获得数据库的实例了。
但是这个方法有个缺点,就是不能进行数据库的升级了。显然这样是非常不好的。
那么如果我们还想用SQLiteOpenHelper,又将其写到sd卡中,又该怎么做呢。
下面的这段代码是有错误的,你只需要注意看51行,正是因为下面的代码,我才研究了onCreate方法到底什么时候执行。
package jz.his.db; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import jz.his.jzhis.R; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.os.Environment; import android.util.Log; public class CityInfoDataSupport extends SQLiteOpenHelper { private final static String TAG = "CityInfoDataSupport"; public static final String dbName = "cityego"; // 数据库在手机里的路径 private static String DATABASE_PATH = Environment .getExternalStorageDirectory().getAbsolutePath()+"/com.bcinfo.pwzs/"; private static int version = 1; private final String GEOCODING_TABLE_NAME = "GEOCODING"; private SQLiteDatabase mSDB = getReadableDatabase(); private static CityInfoDataSupport mDataSupport; Context context; public static CityInfoDataSupport getInstance(Context context) { initDatabse(context); if (mDataSupport == null) { mDataSupport = new CityInfoDataSupport(context); } return mDataSupport; } CityInfoDataSupport(Context context) { super(context, DATABASE_PATH+dbName, null, version); } @Override public void onCreate(SQLiteDatabase db) { executeAssetsSQL(db, "geocoding_create.sql"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { String sql = "drop table if exits " + GEOCODING_TABLE_NAME; db.execSQL(sql); onCreate(db); } private void loadSql(SQLiteDatabase db, String schemaName) { InputStream inputS; try { inputS = context.getAssets().open(schemaName); BufferedReader reader = new BufferedReader(new InputStreamReader(inputS)); String sql = null; while ((sql = reader.readLine()) != null) { db.execSQL(sql.replace(";", "")); } reader.close(); reader = null; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 读取数据库文件(.sql),并执行sql语句 * */ private void executeAssetsSQL(SQLiteDatabase db, String schemaName) { Log.e("DataSupport", "executeAssetsSQL"); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(context.getAssets().open(schemaName))); String line; String buffer = ""; while ((line = in.readLine()) != null) { buffer += line; if (line.trim().endsWith(";")) { db.execSQL(buffer.replace(";", "")); buffer = ""; } } } catch (IOException e) { Log.e("db-error", e.toString()); } finally { try { if (in != null) in.close(); } catch (IOException e) { Log.e("db-error", e.toString()); } } } public synchronized void insertCityInfo() { loadSql(mSDB, "geocoding_data.txt"); } public synchronized List<City> queryDataById(String field, String id) { String sql = ""; List<City> cityList = new ArrayList<City>(); if (field.equals("grade")) { sql = "select * from " + GEOCODING_TABLE_NAME + " where grade = ? "; } else if (field.equals("parent")) { sql = "select * from " + GEOCODING_TABLE_NAME + " where parent = ? "; } String[] params = new String[] { id }; Cursor c = mSDB.rawQuery(sql, params); while (c.moveToNext()) { City city = new City(); city.setGbCode(c.getString(c.getColumnIndex("gbcode"))); city.setGbName(c.getString(c.getColumnIndex("gbname"))); city.setGrade(c.getString(c.getColumnIndex("grade"))); city.setLongitude(c.getString(c.getColumnIndex("longtitude"))); city.setLatitude(c.getString(c.getColumnIndex("latitude"))); city.setParent(c.getString(c.getColumnIndex("parent"))); cityList.add(city); } if (c != null) { c.close(); } return cityList; } public void deleteAppTempTraffic() { String sql = "delete from " + GEOCODING_TABLE_NAME; mSDB.execSQL(sql); } public static void initDatabse(Context cntext) { boolean dbExist = checkDataBase(); //判断数据库是否存在 不存在就把raw里的数据库写入手机 if (!dbExist) { try { copyDataBase(cntext); } catch (IOException e) { throw new Error("Error copying database"); } } } /** * 判断数据库是否存在 * @return false or true */ public static boolean checkDataBase() { SQLiteDatabase checkDB = null; try { String databaseFilename = DATABASE_PATH + dbName; checkDB = SQLiteDatabase.openDatabase(databaseFilename, null, SQLiteDatabase.OPEN_READONLY); } catch (SQLiteException e) { } if (checkDB != null) { checkDB.close(); } return checkDB != null ? true : false; } /** * 复制数据库到手机指定文件夹下 * @throws IOException */ public static void copyDataBase(Context context) throws IOException { String databaseFilenames = DATABASE_PATH + dbName; File dir = new File(DATABASE_PATH); FileOutputStream os = null; // 判断文件夹是否存在,不存在就新建一个 if (!dir.exists()) { dir.mkdirs(); } try { // 得到数据库文件的写入流 os = new FileOutputStream(databaseFilenames); } catch (FileNotFoundException e) { e.printStackTrace(); } // 得到数据库文件的数据流 InputStream is = context.getResources().openRawResource(R.raw.cityego); byte[] buffer = new byte[8192]; int count = 0; try { while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); os.flush(); } } catch (IOException e) { } try { is.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } } }
通过上面的这个代码,我的onCreate方法一直没有执行。最终经过我多次试验,我知道了问题所在,所以在这里进行总结。
那么onCreate方法到底什么时候执行呢?
SQLiteOpenHelper的onCreate方法一定是在getReadableDatabase方法之后的
SQLiteDatabase mSDB = getReadableDatabase()这个方法首先检查手机中,是否有已经存在的数据库,如果没有,则执行onCreate方法,如果有,则不执行---->但是,这里有个前提是,你的supre(context, DATABASE_PATH+dbName, null, version),的第二个参数不能是已存在的数据库路径。
我这里,将第二个参数,弄成了已存在的数据库文件,所以onCreate方法永远不会执行。
那么当super(context, dbName, null, version);第二个参数正确和并且执行了getReadableDatabase这个方法,才会在系统内存有数据库。
折磨了我一个下午啊啊啊啊。