官网链接
http://developer.android.com/training/basics/data-storage/index.html
一、保存key-value集
key-value 对广大coder来说已经再熟悉不过了。
为了方便使用者快捷的存取数据在andorid中key-value保存在一个文件中的。1.1 获取SharedPreferences实例
在Activity中可以直接获得SharedPreferences实例,否则需要先得到Activity实例
SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences( "sharedFile", Context.MODE_PRIVATE);
两种方式有区别,之前不是说这些key-value数据存放在文件中吗,所以getPreferences是当前Activity实例的一个文件,而getSharedPreference是新建一个文件来存,所以需要提供一个名字。
第二个参数是模式的选择的,简单说点,MODE_PRIVATE是私有的,不给其它使用,还有比如MODE_WORLD_WRITEABLE则可以全局访问,据官网介绍甚至其它APP都可以使用哦,这个没有尝试。
1.2 读和写
很简单的API所以放一起写了,直接上代码
SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE); preferences.edit().putString("key", "test").commit(); String ret0=preferences.getString("key", "default"); System.out.println("key -------------- " + ret0); SharedPreferences sharedPreferences = getSharedPreferences( "sharedFile", Context.MODE_PRIVATE); sharedPreferences.edit().putString("key", "testShare").commit(); String ret = sharedPreferences.getString("key", "default"); System.out.println("shared key ------------- " + ret);
注意到了吗,是写System.out来输出的,通常android不用这种输出方式,因为文章还没有介绍到如何利用android的Log API来现实日志,所以先用着吧。
二、保存文件
2.1 选择内部还是外部的存储介质
所谓内存储是指机器本来的内存,我们通常叫机身内存。外部存储介质比如SD卡之类的。
为什么分为两部分呢,因为
在内部存储:
1.总是可用的
2.保存的文件通常只能它自己的APP可以访问
3.卸载APP的时候会连带删除APP的文件
外部存储
1.不总是肯定,因为在某些时候你可能取出SD卡
2.因为是全局可读的,所以保存在这里的文件可能会在你的控制之外。
3.卸载APP的时候,系统会删除你用getExternalFilesDir()保存的文件,其它方式保存的不会删除。
2.2 为外部存储设置权限
我们通过在Manifest 文件中设置外部存储的权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.tang.test" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ...... </manifest>这是写的权限,如果是读权限 WRITE_EXTERNAL_STORAGE
2.3 在内部存储中保存文件
可以通过以下两种方式来得到一个文件
getFilesDir()
getCacheDir() 临时的缓存文件,系统内存不够时,可能会删除
//context.getFilesDir() File file = new File(context.getFilesDir(), filename); //context.getCacheDir() File file; try { String fileName = Uri.parse(url).getLastPathSegment(); file = File.createTempFile(fileName, null, context.getCacheDir()); catch (IOException e) { // Error while creating file } return file;
向文件写数据利用到了文件流
String filename = "myfile"; String string = "Hello world!"; FileOutputStream outputStream; try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); }
2.4 在外部存储中保存文件
因为外部存储不一定总可用,所以我们需要判断可用的情况
/* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false; } /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false; }
尽管在外部存储中的文件可以被用户和其它APP修改,android提供了两种类型的文件。
Public files 卸载APP后文件不会删除,比如照片信息
private files 卸载之后文件被删除,比如说额外的下载的资源文件
//公有的文件 public File getAlbumStorageDir(String albumName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; } //私有的文件 public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
2.5 查询空闲空间
在程序运行时知道空闲空间在某些情况下可能会很有用,必须你下载一个较大文件的时候
android提供两个方法
getFreeSpace()
getTotalSpace()
2.6 删除一个文件
myFile.delete(); myContext.deleteFile(fileName);
当app被卸载的时候,系统会按照下面的逻辑删除文件
1. 删除internal storage(内部存储)中保存的文件
2. 删除extenal storage(外部存储)中使用getExternalFilesDir()保存的文件
三、保存到数据库中
android有内嵌的数据库sqlite。
官网的文档关于这方面写的感觉有点啰嗦了,可能想让大家养成一个好的习惯吧。其实有超级简单的例子,可能在现阶段有些人还不想搞那么复杂来个简单的吧,我直接在Activity中写一个方法,然后再onCreate中调用它测试了一下。
private void dbtest() { SQLiteDatabase db = openOrCreateDatabase("my.db", Context.MODE_PRIVATE, null); db.execSQL("drop table if exists test"); db.execSQL("create table test(id varchar,name varchar)"); db.execSQL("insert into test values(?,?)", new Object[] { "id_1", "name_1" }); Cursor cursor = db.rawQuery("select * from test where name =?", new String[] { "name_1" }); while (cursor.moveToNext()) { String id = cursor.getString(cursor.getColumnIndex("id")); System.out.println("id =========== " + id); Log.i("db_target", "id \t" + id); } cursor.close(); db.close(); }爽了一下之后开始跟着官网慢慢来吧。
3.1 定义Schema 和 Contract
所谓schema和Contract 其实是你的数据库和表的定义,他们叫的比较专业,我等通常叫创建数据库和表。
哎,谁谁谁,给创建的sql语句给我发一份....
public final class FeedReaderContract { public FeedReaderContract() {} public static abstract class FeedEntry implements BaseColumns { public static final String TABLE_NAME = "entry"; public static final String COLUMN_NAME_ENTRY_ID = "entryid"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_SUBTITLE = "subtitle"; } }理解为常量类,类中定义了一些数据库定义方面的常量。
3.2 使用SQL Helper 创建数据库
android提供了一个叫做SQLiteOpenHelper的类来帮助我们使用SQLite.
public class FeedReaderDbHelper extends SQLiteOpenHelper { public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db"; public FeedReaderDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } private static final String TEXT_TYPE = " TEXT"; private static final String COMMA_SEP = ","; private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + FeedEntry._ID + " INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + " )"; private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; }根据以前的常量值,在这个类中,拼写了一些常用的SQL语句,为了方便看我放在了最后面。继承SQLiteOpenHelper之后,需要实现onCreate()和onUpdate()方法,这两个方法分别在第一次使用数据库时,和版本号改变后调用,所谓版本号改动就是DATABASE_VERSION值改变了,如果你的APP数据库表发生改变了,修改下这个版本号,系统在下次启动的时候会调用onUpdate()方法,然后我们在方法中做一些适应性的改变,上面例子中比较暴力,直接把以前的表删除了,建的新表。(对于这种情况我表示有点害怕,出问题别怪我,官网上就这么写的)
3.3 put/read/delete/update
增删该查,数据库四大神兵,必须得知道的
private void dbOperationTest() { FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(this); SQLiteDatabase wdb = mDbHelper.getWritableDatabase(); SQLiteDatabase rdb = mDbHelper.getReadableDatabase(); // put ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, "id_" + 0); values.put(FeedEntry.COLUMN_NAME_TITLE, "title_" + 0); long newRowId; newRowId = wdb.insert(FeedEntry.TABLE_NAME, null, values); // query Cursor cursor = rdb.query(FeedEntry.TABLE_NAME, // The table to query new String[] { FeedEntry.COLUMN_NAME_ENTRY_ID, FeedEntry.COLUMN_NAME_TITLE }, // The columns to return FeedEntry.COLUMN_NAME_TITLE +"=?", // The columns for the WHERE clause new String[]{"title_"+0}, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups FeedEntry.COLUMN_NAME_TITLE+" DESC" // The sort order ); while (cursor.moveToNext()) { String id = cursor.getString(cursor.getColumnIndex(FeedEntry.COLUMN_NAME_ENTRY_ID)); Log.i("db_target", "id \t" + id); } cursor.close(); //delete String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; String[] selectionArgs = { "id_"+0 }; wdb.delete(FeedEntry.TABLE_NAME, selection, selectionArgs); //udpate ContentValues updateValues = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, "title_2"); int count = wdb.update( FeedEntry.TABLE_NAME, updateValues, selection, selectionArgs); //close rdb.close(); wdb.close(); }