在应用开发中,不可避免要无时无刻地和数据打交道。


为什么要实现数据持久化?

比如:对象Student中有参数name、sex、age等,在应用开发中可以创建一个Student对象并给其参数赋值,该方式用于存储暂时\瞬时的数据,当退出程序或资源被回收后所保存的数据就消失了,因此当我们开发中需要保存用户登录的账号或密码、保存用户设置等重要信息时,数据持久化就可以帮助我们实现这一需求。


那么,如何实现数据持久化?

在开发中要实现数据持久化,总的来说,无非就是要实现程序退出或杀死后,之前的数据能够保留下来不被清除掉。


下面,介绍实现数据持久化的三种方式及其具体用法。

实现数据持久化的三种方式:文件存储、SharedPreferences存储、数据库存储。


1、文件存储

方式:把所要保存一些文本数据存储到手机的内存中,当有需要的时候读取出来,默认的存储到data/data/<package name> /files目录下,因此当程序被卸载时所在的目录文件都会被删除,这些数据也会随着消失。


实现原理:

<1>存储数据:通过Context类中所提供的openFileOutput()方法:


public FileOutputStream openFileOutput(String name, int mode)

方法openFileOutput返回的数据是FileOutputStream,方法传入的第一个参数是文件名,第二个参数是文件的操作方式:

MODE_PRIVATE(当指定同样文件名时会覆盖原文件中的内容)、MODE_APPEND(当该文件已存在时就往文件中追加内容,不会创建新文件)。

public void saveData(String inputText) {
		FileOutputStream out = null;
		BufferedWriter writer = null;
		if (inputText != null && !TextUtils.isEmpty(inputText)) {
			try {
				out = openFileOutput("save", Context.MODE_PRIVATE);
				writer = new BufferedWriter(new OutputStreamWriter(out));
				writer.write(inputText);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if (out != null) {
						out.close();
					}
					if (writer != null) {
						writer.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

<2>读取数据:通过Context类中所提供的openFileInput()方法:

public FileInputStream openFileInput(String name)

方法openFileInput返回的数据是FileInputStream,方法传入的第一个参数是所要读取的文件名

public String readData() {
		FileInputStream in = null;
		BufferedReader reader = null;
		try {
			in = openFileInput("save");
			reader = new BufferedReader(new InputStreamReader(in));
			StringBuffer buffer = new StringBuffer();
			String len = "";
			while ((len = reader.readLine()) != null) {
				buffer.append(len);
			}
			return buffer.toString();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (in != null) {
					in.close();
				}
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "";
	}

注:在涉及到读写操作中记得在AndroidManifest中添加上读写权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


2、SharedPreferences存储

方式:SharedPreferences通过键值对的方式存储数据,它的储存方式相对简单易用。


实现原理:
<1>存储数据:通过Context类所提供的getSharedPreferences方法获取SharedPreferences对象:

public SharedPreferences getSharedPreferences(String name, int mode)


方法getSharedPreferences传入的第一个参数是文件名,如果文件不存在则会自动创建一个,默认的储存路径是:data/data/<package name>/shared_prefs/下,第二个参数是指定操作模式:MODE_PRIVATE(表示只有当前应用程序可以对sharedPreferences文件读写)、MODE_MULTI_PROCESS(用于会有多个进程中对同一个sharedPreferences文件读取)。

获取到SharedPreferences对象后,通过SharedPreferences类所提供的方法edit获取Editor对象,由所获取的Editor对象中的相应方法存储所要保存的数据,如:

putInt(String key, int value);
putString(String key, String value);
putLong(String key, long value);
putFloat(String key, float value);

Editor对象中的方法所传入的第一个参数是键值,第二个参数是所要保存的值。

<2>读取数据:通过Context类所提供的getSharedPreferences方法获取SharedPreferences对象:

public SharedPreferences getSharedPreferences(String name, int mode)


方法getSharedPreferences传入的第一个参数是文件名,第二个参数是指定操作模式:MODE_PRIVATE(表示只有当前应用程序可以对sharedPreferences文件读写)、MODE_MULTI_PROCESS(用于会有多个进程中对同一个sharedPreferences文件读取)。

获取到SharedPreferences对象后,通过SharedPreferences类所提供的相应方法获取所要的数据,如:



int getInt(String key, int defValue);
String getString(String key, String defValue);
long getLong(String key, long defValue);
float getFloat(String key, float defValue);

SharedPreferences类所提供的获取数据的方法中所传入的第一个参数是所要获取的数据对应的键值,第二个参数是默认的值。

对于SharedPreferences存储的方式,这里提供了一个工具类以方便使用:

import java.util.Map;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * @description SharedPreferences 工具类
 */
public class SharedPreferencesUtil {
	// 所要保存数据的文件名
	public static String FILLNAME = "save";

	/**
	 * 存入某个key对应的value值
	 * 
	 * @param context
	 * @param key
	 * @param value
	 */
	public static void put(Context context, String key, Object value) {
		put(context, key, value, FILLNAME, Context.MODE_PRIVATE);
	}

	public static void put(Context context, String key, Object value,
			String fileName) {
		put(context, key, value, fileName, Context.MODE_PRIVATE);
	}

	public static void put(Context context, String key, Object value, int mode) {
		put(context, key, value, FILLNAME, mode);
	}

	public static void put(Context context, String key, Object value,
			String fileName, int mode) {
		SharedPreferences sp = context.getSharedPreferences(fileName, mode);
		Editor editor = sp.edit();
		if (value instanceof String) {
			editor.putString(key, (String) value);
		} else if (value instanceof Integer) {
			editor.putInt(key, (Integer) value);
		} else if (value instanceof Boolean) {
			editor.putBoolean(key, (Boolean) value);
		} else if (value instanceof Float) {
			editor.putFloat(key, (Float) value);
		} else if (value instanceof Long) {
			editor.putLong(key, (Long) value);
		}
		editor.apply();
		// editor.commit(); 同步效率低
	}

	/**
	 * 得到某个key对应的值
	 * 
	 * @param context
	 * @param key
	 * @param defValue
	 * @return
	 */

	public static Object get(Context context, String key, Object defValue) {
		return get(context, key, defValue, FILLNAME, Context.MODE_PRIVATE);
	}

	public static Object get(Context context, String key, Object defValue,
			String fileName) {
		return get(context, key, defValue, fileName, Context.MODE_PRIVATE);
	}

	public static Object get(Context context, String key, Object defValue,
			int mode) {
		return get(context, key, defValue, FILLNAME, mode);
	}

	public static Object get(Context context, String key, Object defValue,
			String fileName, int mode) {
		SharedPreferences sp = context.getSharedPreferences(fileName, mode);
		if (defValue instanceof String) {
			return sp.getString(key, (String) defValue);
		} else if (defValue instanceof Integer) {
			return sp.getInt(key, (Integer) defValue);
		} else if (defValue instanceof Boolean) {
			return sp.getBoolean(key, (Boolean) defValue);
		} else if (defValue instanceof Float) {
			return sp.getFloat(key, (Float) defValue);
		} else if (defValue instanceof Long) {
			return sp.getLong(key, (Long) defValue);
		}
		return null;
	}

	/**
	 * 返回所有数据
	 * 
	 * @param context
	 * @return
	 */
	public static Map<String, ?> getAll(Context context) {
		return getAll(context, FILLNAME, Context.MODE_PRIVATE);
	}

	public static Map<String, ?> getAll(Context context, String fileName) {
		return getAll(context, fileName, Context.MODE_PRIVATE);
	}

	public static Map<String, ?> getAll(Context context, String fileName,
			int mode) {
		SharedPreferences sp = context.getSharedPreferences(fileName, mode);
		return sp.getAll();
	}

	/**
	 * 移除某个key值已经对应的值
	 * 
	 * @param context
	 * @param key
	 */
	public static void remove(Context context, String key) {
		remove(context, key, FILLNAME, Context.MODE_PRIVATE);
	}

	public static void remove(Context context, String key, String fileName) {
		remove(context, key, fileName, Context.MODE_PRIVATE);
	}

	public static void remove(Context context, String key, String fileName,
			int mode) {
		SharedPreferences sp = context.getSharedPreferences(fileName, mode);
		Editor editor = sp.edit();
		editor.remove(key);
		editor.apply();
		// editor.commit(); 同步效率低
	}

	/**
	 * 清除所有内容
	 * 
	 * @param context
	 */
	public static void clear(Context context) {
		clear(context, FILLNAME, Context.MODE_PRIVATE);
	}

	public static void clear(Context context, String fileName) {
		clear(context, fileName, Context.MODE_PRIVATE);
	}

	public static void clear(Context context, String fileName, int mode) {
		SharedPreferences sp = context.getSharedPreferences(fileName, mode);
		Editor editor = sp.edit();
		editor.clear();
		editor.apply();
	}
}




3、数据库存储

方式:其实在安卓系统中内置了SQLite数据库,它是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用,不仅支持标准SQL语法,还遵循ACID(数据库事务)原则,使用起来非常方便!

使用场景:在上面两种实现数据持久化方式中,都是存储一些简单的数据,当我们开发中需要储存大量复杂的关系型数据的时候,例如保存客户信息、联系人信息等,前两种方式就胜任不了了,这个时候我们就可以使用安卓内置的数据库。


实现原理:

<1>创建数据库:在数据库的创建中,安卓给我们提供了SQLiteOpenHelper的两个方法,onCreate( )与onUpgrade( )来实现:

onCreate(database):首次运行自定义继承SQLiteOpenHelper的类时生成数据库表
onUpgrade(database,oldVersion,newVersion):在数据库的版本发生变化时会被调用, 一般在软件升级时才需改变版本号。

步骤:


Step 1:自定义一个类继承SQLiteOpenHelper类
Step 2:在该类的构造方法的super中设置好要创建的数据库名,版本号
Step 3:重写onCreate( )方法创建表结构
Step 4:重写onUpgrade( )方法定义版本号发生改变后执行的操作

代码:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class StudentSQLiteOpenHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "student";
    private static final int DATABASE_VERSION = 1;
    public static final String TABLE_NAME = "table_student";
    private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS ";

    private final String ID = "id";
    private final String NAME = "name";
    private final String SEX = "sex";
    private final String AGE = "age";
    
    private static StudentSQLiteOpenHelper instance = null;
    public static StudentSQLiteOpenHelper getInstance(Context context) {
        if(instance == null){
            synchronized (StudentSQLiteOpenHelper.class) {
                if(instance == null)
                    instance = new StudentSQLiteOpenHelper(context);
            }
        }
        return instance;
    }

    public StudentSQLiteOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // 数据库第一次创建时被调用
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE + TABLE_NAME + "(" 
                + ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 
                + NAME + " VARCHAR(20)," 
                + SEX + " VARCHAR(10)," 
                + AGE + " INTEGER"
                + ");");
    }

    // 软件版本号发生改变时调用
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

在第一次运行自定义继承SQLiteOpenHelper的类时,会创建这个student.db的文件,并且会执行onCreate()里的方法,创建一个Student的表,Student表中包含四个字段:主键id、姓名name、性别sex和年龄age;接着如果修改db的版本号,那么下次启动就会调用onUpgrade()里的方法,注:如果是插入一个字段,则表中数据不会丢失,如果是重建表的话,表中的数据会全部丢失!



<2>对数据库进行增、删、改、查操作

------------------------------------------------------------<增>----------------------------------------------------------------

以上面创建的Student表为例,往表中添加数据可以通过SQLiteDatabase类所提供的insert方法:

public long insert(String table, String nullColumnHack, ContentValues values)

方法

insert中传入的第一个参数是数据所要添加的表,第二个参数是当values没有值的时候默认添加到表中的数据,第三个参数是所要添加的数据以HashMap<String,Object>型式保存。

public void addDataToDB(String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			ContentValues values = new ContentValues();
			values.put(NAME, name);
			values.put(SEX, sex);
			values.put(AGE, age);
			db.insert(TABLE_NAME, null, values);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}


------------------------------------------------------------<删>----------------------------------------------------------------

以上面创建的Student表为例,往表中添加数据可以通过SQLiteDatabase类所提供的delete方法:

public int delete(String table, String whereClause, String[] whereArgs)

方法delete中传入的第一个参数是要删除的数据所在的表,第二个参数是根据表中的字段作为删除依据,第三个参数是第二个参数对应的所要删除的数据。

public void deleteDataById(int id) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			db.delete(TABLE_NAME, ID + " = ? ", new String[] { String.valueOf(id) });// 根据id删除数据
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}

上面所给出的代码是根据id进行数据删除,同时也可以精确到某条数据进行删除:

public void deleteData(String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			db.delete(TABLE_NAME, NAME + " =? and " + SEX + " =? and " 
					+ AGE + " =? ", new String[] { name, sex, String.valueOf(age) });// 精确到某条数据进行删除
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}

 


------------------------------------------------------------<改>----------------------------------------------------------------

以上面创建的Student表为例,往表中添加数据可以通过SQLiteDatabase类所提供的update方法:

public int update(String table, ContentValues values, String whereClause, String[] whereArgs)

方法

update 中传入的第一个参数是要更新的数据所在的表名,第二个参数是新的数据,第三个参数是要更新数据的查找条件,第四个参数是条件的参数。

public void updateDataToDB(int id, String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			ContentValues values = new ContentValues();
			values.put(NAME, name);
			values.put(SEX, sex);
			values.put(AGE, age);
			db.update(TABLE_NAME, values, ID + " =? ", new String[] { String.valueOf(id) });
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}


------------------------------------------------------------<查>----------------------------------------------------------------

以上面创建的Student表为例,往表中添加数据可以通过SQLiteDatabase类所提供的query方法:

public Cursor query(String table, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having,
            String orderBy)

方法

update 中传入的参数依次是:表名、列名、where约束条件、where中占位符提供具体的值、指定group by的列、进一步约束、指定查询结果的排序方式。


/**
 * 
 * Student类
 *
 **/
public class StudentInfo {

	private String name;
	private String sex;
	private int age;
	
	public StudentInfo(){
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}
public List<StudentInfo> queryData(){
		List<StudentInfo> list = new ArrayList<StudentInfo>();
		SQLiteDatabase db = getReadableDatabase();
		try {
			Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);
			if(cursor != null){
				if(cursor.moveToFirst()){
					do {
						StudentInfo studentInfo = new StudentInfo();
						studentInfo.setName(cursor.getString(cursor.getColumnIndex(NAME)));
						studentInfo.setSex(cursor.getString(cursor.getColumnIndex(SEX)));
						studentInfo.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
						list.add(studentInfo);
					} while (cursor.moveToNext());
				}
				cursor.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			db.close();
		}
		return list;
	}

对于以上对数据库的增、删、改、查的操作,提供StudentSQLiteOpenHelper类作为参考:

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.transition.ArcMotion;

/**
 * 
 * 自定义继承SQLiteOpenHelper的类StudentSQLiteOpenHelper
 * 数据库的增、删、改、查操作
 *
 **/
public class StudentSQLiteOpenHelper extends SQLiteOpenHelper {

	private static final String DATABASE_NAME = "student";
	private static final int DATABASE_VERSION = 1;
	public static final String TABLE_NAME = "table_student";
	private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS ";

	private final String ID = "id";
	private final String NAME = "name";
	private final String SEX = "sex";
	private final String AGE = "age";
	
	private static StudentSQLiteOpenHelper instance = null;
	public static StudentSQLiteOpenHelper getInstance(Context context) {
		if(instance == null){
			synchronized (StudentSQLiteOpenHelper.class) {
				if(instance == null)
					instance = new StudentSQLiteOpenHelper(context);
			}
		}
		return instance;
	}

	public StudentSQLiteOpenHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	// 数据库第一次创建时被调用
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(CREATE_TABLE + TABLE_NAME + "(" 
				+ ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 
				+ NAME + " VARCHAR(20)," 
				+ SEX + " VARCHAR(10)," 
				+ AGE + " INTEGER"
				+ ");");
	}

	// 软件版本号发生改变时调用
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}

	public void add(String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			ContentValues values = new ContentValues();
			values.put(NAME, name);
			values.put(SEX, sex);
			values.put(AGE, age);
			db.insert(TABLE_NAME, null, values);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}

	public void delete(int id) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			db.delete(TABLE_NAME, ID + " = ? ",
					new String[] { String.valueOf(id) });// 根据id删除数据
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}

	public void delete(String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			db.delete(TABLE_NAME, NAME + " =? and " + SEX + " =? and " + AGE
					+ " =? ", new String[] { name, sex, String.valueOf(age) });// 精确到某条数据进行删除
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}

	public void updateData(int id, String name, String sex, int age) {
		SQLiteDatabase db = getWritableDatabase();
		try {
			ContentValues values = new ContentValues();
			values.put(NAME, name);
			values.put(SEX, sex);
			values.put(AGE, age);
			db.update(TABLE_NAME, values, ID + " =? ", new String[] { String.valueOf(id) });
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			db.close();
		}
	}
	
	public List<StudentInfo> queryData(){
		List<StudentInfo> list = new ArrayList<StudentInfo>();
		SQLiteDatabase db = getReadableDatabase();
		try {
			Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);
			if(cursor != null){
				if(cursor.moveToFirst()){
					do {
						StudentInfo studentInfo = new StudentInfo();
						studentInfo.setName(cursor.getString(cursor.getColumnIndex(NAME)));
						studentInfo.setSex(cursor.getString(cursor.getColumnIndex(SEX)));
						studentInfo.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
						list.add(studentInfo);
					} while (cursor.moveToNext());
				}
				cursor.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			db.close();
		}
		return list;
	}
}

注:在对SQLiteDatabase和Cursor对象使用过程中,使用完后一定要记得调用close方法将其关闭,否则将会造成内存泄露。