在我们的android实际开发过程,必不可少的一种行为操作对象就是数据。有些数据,对于用户而言是一次性的,这就要求我们每次进到App的时候,都需要去刷新数据。有些数据,对于用户而言又是具有一定时效性的,比如用户账号数据。这种情况下,就要求我们采用一定的数据保存措施,在这篇博客里面,我将为大家分享一些android里面常用的数据保存方法。
首先在android里面我们用的比较多的小数量存储方式可能就是SharedPreferences,那么什么是SharedPreferences呢?为了保存软件的设置参数,Android 平台为我们提供了一个SharedPreferences 类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences 保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs 目录下。说白了,它实际上也是一种文件存储方式。下面让我们来看看它的具体用法:
public static void openSharep(Context context,String name){ SharedPreferences sharedPreferences=context.getSharedPreferences(name,Context.MODE_PRIVATE); SharedPreferences.Editor editor=sharedPreferences.edit(); editor.putString("name","hello"); editor.commit(); }
上面是将一个字符串,通过键值对的方式存储到xml文件里面。这个时候,你也许会问:在什么情况下,会将SharedPreferences里面的数据删掉呢?当我们卸载app的时候,SharePreferences是否还在呢?当我的应用程序更新的时候,SharePreferences文件是否还在呢?我想这有可能跟你当前所使用android系统版本有关系,本人也没有细查这个问题,如有不对还请留言讨论。通过上面的代码你可能就能够发现,SharePreferences里面保存的数据还是相当有限的,主要是一些标识类的数据。
那么如果你需要在项目里面保存大量的数据,你会怎么办呢?其实常用的有两种方式:一种是文件存储;另外一种就是sqlite数据库;文件储存的方式,我们就不做过多的解释了,直接上代码,大家肯定就能够明白了。
String fileName = etFileName.getText().toString(); String fileContent = etFileContent.getText().toString(); FileOutputStream out = null; try { out = context.openFileOutput(fileName, Context.MODE_PRIVATE); out.write(fileContent.getBytes("UTF-8")); Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); }finally { try{ out.close(); }catch (Exception e){ e.printStackTrace(); } }
上面的代码我们直接定义一个输出流就可以将我们希望保存的信息写入文件系统里面了。然后我们再来看看读取流的代码:
String fileName=etFileName.getText().toString(); FileInputStream in=null; ByteArrayOutputStream bout=null; byte[] buf=new byte[1024]; bout=new ByteArrayOutputStream(); int length=0; try{ in=context.openFileInput(fileName); while((length=in.read(buf))!=-1){ bout.write(buf,0,length); } byte[] content = bout.toByteArray(); tvInfo.setText(new String(content,"UTF-8")); //设置文本框为读取的内容 }catch (Exception e){ e.printStackTrace(); } tvInfo.invalidate(); //刷新屏幕 try{ in.close(); bout.close(); } catch(Exception e){}
通过上面的文件操作代码,我们可以很直观的发现文件系统无非就是一些中规中矩的文件读写操作了。接下来,我们将重点看看数据保存的最重要角色sqlite数据库。通过数据库保存下来的文件,我们都知道相对于普通的文件系统具有更好的结构性。就是说我们在操作数据的时候,可以更加有条理性的进行。
首先让我们来看看android里面应该怎样定义一个数据库操作对象呢?其实很简单,我们只需要定义一个类,然后让它继承SQLiteOpenHelper就可以了。请看测试类定义:
public class SqliteTest extends SQLiteOpenHelper { public SqliteTest(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
通过上面的类定义我们可以看到,当我们实现一个构造方法、onCreate、onUpgrade方法之后,我们具有一个数据库操作的对象了。接下来让我们首先来完善一下上面SqliteTest类,提供完整的代码实现:
public class SqliteTest extends SQLiteOpenHelper { private static final int VERSION = 1; private static final String DB_NAME = "test.db"; public SqliteTest(Context context) { super(context, DB_NAME, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS person" + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age INTEGER, info TEXT)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("ALTER TABLE person ADD COLUMN other STRING"); } }
然后当我们拿到这个数据库操作的对象之后,就应该考虑怎样使用这个对象了?比如我现在想要往这个数据库里面插入一些数据,请看下面的测试代码:
private SQLiteDatabase db; private SqliteTest helper; private void getSqliteDb(){ db=helper.getWritableDatabase(); } private void addData(List<Person> persons){ db.beginTransaction(); //开始事务 try { for (Person person : persons) { db.execSQL("INSERT INTO person VALUES(null, ?, ?, ?)", new Object[]{person.name, person.age, person.info}); } db.setTransactionSuccessful(); //设置事务成功完成 } finally { db.endTransaction(); //结束事务 } }
通过上面的代码我就可以实现数据的插入操作了。
在这篇博客的最后,让我们来看看SQLiteOpenHelper类的源码,探索一下为什么实现该类之后就能够进行数据库操作了呢?我们可以先来看看如下方法:
public SQLiteDatabase getWritableDatabase() { synchronized (this) { return getDatabaseLocked(true); } }
当我们调用getWriteableDatabase方法之后,程序会同步的调用getDatabaseLocked方法:
private SQLiteDatabase getDatabaseLocked(boolean writable) { if (mDatabase != null) { if (!mDatabase.isOpen()) { // Darn! The user closed the database by calling mDatabase.close(). mDatabase = null; } else if (!writable || !mDatabase.isReadOnly()) { // The database is already open for business. return mDatabase; } } if (mIsInitializing) { throw new IllegalStateException("getDatabase called recursively"); } SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { if (writable && db.isReadOnly()) { db.reopenReadWrite(); } } else if (mName == null) { db = SQLiteDatabase.create(null); } else { try { if (DEBUG_STRICT_READONLY && !writable) { final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } else { db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, mFactory, mErrorHandler); } } catch (SQLiteException ex) { if (writable) { throw ex; } Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex); final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } } onConfigure(db); final int version = db.getVersion(); if (version != mNewVersion) { if (db.isReadOnly()) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + mName); } db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { if (version > mNewVersion) { onDowngrade(db, version, mNewVersion); } else { onUpgrade(db, version, mNewVersion); } } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } onOpen(db); if (db.isReadOnly()) { Log.w(TAG, "Opened " + mName + " in read-only mode"); } mDatabase = db; return db; } finally { mIsInitializing = false; if (db != null && db != mDatabase) { db.close(); } } }
调用这个方法之后系统就能够给我们提供一个SQLiteDatabase对象,然后我们就可以利用SQLiteDatabase利用的方法进行数据库操作了。
至于SQLiteDatabase层的源码,我自己还没有怎么研究过,如有什么不对的地方,欢迎拍砖!