Android数据存储
1.SP存储
2.文件存储
3.DiskLruchCache的使用
4.数据库存储(GreenDao使用)
一、SP存储
简介:
SharedPreference类提供了一个总体框架,可以保存和检索的任何基本数据类型( boolean, float, int, long, string)的持久键-值对(基于XML文件存储的“key-value”键值对数据);通常用来存储程序的一些配置信息。其存储在“data/data/程序包名/shared_prefs目录下
使用方式:
getSharedPreferences (String name, int mode);
Mode分为四种模式:
- MODE_PRIVATE: 私有方式存储,其他应用无法访问
- MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取
- MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入
- MODE_MULTI_PROCESS:适用于多进程访问(目前已被废弃,google官方推荐使用ContentProvider来实现进程间共享访问)
示例:
存储数据:
SharedPreferences sp = mContext.getSharedPreferences(KEY, Context.MODE_PRIVATE);
SharedPreferences.Editor et = sp.edit();
et.putString(key, value);
et.commit();
取数据:
SharedPreferences sp = mContext.getSharedPreferences(KEY, Context.MODE_PRIVATE);
String data = sp.getString(key, "");
SP优缺点:
SP使用起来很方便,简洁。但存储数据类型比较单一(只有基本数据类型),无法进行条件查询,只能在不复杂的存储需求下使用,比如保存配置信息等。
二、文件存储
关于文件存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中。
文件可用来存放大量数据,如文本、图片、音频等。
默认位置:/data/data/<包>/files/***.***。
public void save()
{
try {
FileOutputStream outStream=this.openFileOutput("a.txt",Context.MODE_WORLD_READABLE);
outStream.write(text.getText().toString().getBytes());
outStream.close();
Toast.makeText(MyActivity.this,"Saved",Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
return;
}
catch (IOException e){
return ;
}
}
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。
创建的文件保存在/data/data/<package name>/files目录,如: /data/data/com.test/files/a.txt
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:
Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入: openFileOutput("test.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。
读取文件示例:
public void load()
{
try {
FileInputStream inStream=this.openFileInput("a.txt");
ByteArrayOutputStream stream=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int length=-1
while((length=inStream.read(buffer))!=-1) {
stream.write(buffer,0,length);
}
stream.close();
inStream.close();
text.setText(stream.toString());
Toast.makeText(MyActivity.this,"Loaded",Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e){
return ;
}
}
对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。
Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录。
getFilesDir()方法用于获取/data/data/<package name>/files目录。
把文件存入SDCard:
使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,我们可以把它存放在SDCard。
在程序中访问SDCard,你需要申请访问SDCard的权限。
在AndroidManifest.xml中加入访问SDCard的权限如下:
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();/获取SDCard目录
File saveFile = new File(sdCardDir, “a.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("test".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。
Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,当然要获取SDCard的目录,你也可以这样写:
File saveFile = new File("/sdcard/a.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("test".getBytes());
outStream.close();
三、DiskLruchCache的使用
首先DiskLruCache是不能new出实例的,如果我们要创建一个DiskLruCache的实例,则需要调用它的open()方法
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
open()方法接收四个参数,第一个参数指定的是数据的缓存地址,第二个参数指定当前应用程序的版本号,第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1,第四个参数指定最多可以缓存多少字节的数据。
缓存地址通常都会存放在 /sdcard/Android/data//cache 这个路径下面,但同时我们又需要考虑如果这个手机没有SD卡,或者SD正好被移除了的情况,因此专门写一个方法来获取缓存地址:
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,否则就调用getCacheDir()方法来获取缓存路径。前者获取到的就是 /sdcard/Android/data//cache 这个路径,而后者获取到的是 /data/data//cache 这个路径。
因此一个open()方法应该如下所示:
//得到缓存文件
File diskCacheDir = getDiskCacheDir(mContext, "Bitmap");
//如果文件不存在 直接创建
if (!diskCacheDir.exists()) {
diskCacheDir.mkdirs();
}
try {
mDiskCache = DiskLruCache.open(diskCacheDir, 1, 1, 1024 * 1024 * 20);
} catch (IOException e) {
e.printStackTrace();
}
有了DiskLruCache的实例之后,我们就可以对缓存的数据进行操作了,首先进行写入操作:
/**
* 将Bitmap写入缓存
*/
private Bitmap addBitmapToDiskCache(String url) throws IOException {
if (mDiskCache == null) {
return null;
}
//设置key,并根据URL保存输出流的返回值决定是否提交至缓存
String key = hashKeyFormUrl(url);
//得到Editor对象
DiskLruCache.Editor editor = mDiskCache.edit(key);
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
if (downloadUrlToStream(url, outputStream)) {
//提交写入操作
editor.commit();
} else {
//撤销写入操作
editor.abort();
}
mDiskCache.flush();
}
return getBitmapFromDiskCache(url);
}
此方法就是访问urlS中传入的网址,并通过outputStream写入到本地。有了这个方法之后,下面我们就可以使用DiskLruCache来进行写入了,写入的操作是借助DiskLruCache.Editor这个类完成的。类似地,这个类也是不能new的,需要调用DiskLruCache的edit()方法来获取实例,
而edit()方法接收一个参数key,这个key将会成为缓存文件的文件名,并且必须要和图片的URL是一一对应的.因此将图片的URL进行MD5编码,编码后的字符是唯一的,并且只会包含0-F字符,也符合文件的命名规则.
/**
* 将URL转换成key
*/
private String hashKeyFormUrl(String url) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(url.hashCode());
}
return cacheKey;
}
/**
* 将Url的字节数组转换成哈希字符串
*/
private String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* 将URL中的图片保存到输出流中
*/
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), 1024 * 8);
out = new BufferedOutputStream(outputStream, 1024 * 8);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
return true;
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return false;
}
缓存成功写入之后就需要读取缓存中的内容,如下所示:
/**
* 从缓存中取出Bitmap
*/
private Bitmap getBitmapFromDiskCache(String url) throws IOException {
//如果缓存中为空 直接返回为空2017/4/6 18:53:47 2017/4/6 18:53:47
if (mDiskCache == null) {
return null;
}
//通过key值在缓存中找到对应的Bitmap
Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
//通过key得到Snapshot对象
DiskLruCache.Snapshot snapShot = mDiskCache.get(key);
if (snapShot != null) {
//得到文件输入流
InputStream ins = snapShot.getInputStream(0);
bitmap = BitmapFactory.decodeStream(ins);
}
return bitmap;
}
四、数据库存储(GreenDao使用)
关于GreenDao
greenDao是一个将对象映射到SQLite数据库中的轻量且快速的ORM解决方案。关于greenDAO的概念可以看官网 http://greenrobot.org/greendao/
greenDAO 优势
1、一个精简的库 2、性能最大化 3、内存开销最小化 4、易于使用的 APIs 5、对 Android 进行高度优化
GreenDao 3.0使用
GreenDao 3.0采用注解的方式来定义实体类,通过gradle插件生成相应的代码。
一,在as中导入相关的包
compile'org.greenrobot:greendao:3.0.1'
compile'org.greenrobot:greendao-generator:3.0.0'
二,在build.gradle中进行配置:
apply plugin: 'org.greenrobot.greendao'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0'
}
}
在gradle的根模块中加入上述代码。
三,自定义路径
greendao {
schemaVersion 1
daoPackage 'com.yangshibai.savetest.gen'
targetGenDir 'src/main/java'
}
在gradle的根模块中加入上述代码,就完成了我们的基本配置了。
属性介绍:
schemaVersion--> 指定数据库schema版本号,迁移等操作会用到 ;
daoPackage --> dao的包名,包名默认是entity所在的包;
targetGenDir --> 生成数据库文件的目录;
四,创建一个User的实体类
@Entity //表示这个实体类一会会在数据库中生成对应的表
public class User {
@Id //表示该字段是id
private Long id;
private String name;
@Transient //表示这个属性将不会作为数据表中的一个字段
private int tempUsageCount; // not persisted
}
五,MakeProject
编译项目,User实体类会自动编译,生成get、set方法并且会在com.anye.greendao.gen目录下生成三个文件;
greenDao GreenDao使用
public class MyApplication extends Application {
private DaoMaster.DevOpenHelper mHelper;
private SQLiteDatabase db;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
public static MyApplication instances;
@Override public void onCreate() {
super.onCreate();
instances = this;
setDatabase();
}
public static MyApplication getInstances(){
return instances;
}
/**
* 设置greenDao
*/
private void setDatabase() {
// 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
// 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。
mHelper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = mHelper.getWritableDatabase();
// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
}
public DaoSession getDaoSession() {
return mDaoSession;
}
public SQLiteDatabase getDb() {
return db;
}
} 获取UserDao对象:
mUserDao = MyApplication.getInstances().getDaoSession().getUserDao();
简单的增删改查实现:
-
增
/** * 增加数据 */ private void addDate() { mUser = new User((long) i, "测试" + i); mUserDao.insert(mUser);//添加一个 mContext.setText(mUser.getName()); Toast.makeText(GreenDaoActivity.this, "增加索引为" + i + ",内容为:" + mUser.getName(), Toast.LENGTH_SHORT).show(); i++; }
-
删
/**
- 删除数据 */ private void deleteDate() { deleteUserById(j); Toast.makeText(GreenDaoActivity.this, “删除索引为” + j, Toast.LENGTH_SHORT).show(); j++; }
/**
- 根据主键删除User *
- @param id User的主键Id */ public void deleteUserById(long id) { mUserDao.deleteByKey(id); }
-
改
/** * 更改数据 */ private void updateDate() { mUser = new User((long) k, "id为" + k + "的数据已经更改"); mUserDao.update(mUser); Toast.makeText(GreenDaoActivity.this, "更改索引为" + k, Toast.LENGTH_SHORT).show(); k++; }
-
查
/** * 查找数据 */ private void findDate() { List<User> users = mUserDao.loadAll(); String userName = ""; for (int i = 0; i < users.size(); i++) { userName += users.get(i).getName() + ","; } mContext.setText("查询全部数据==>" + userName); }