阅读:https://developer.android.com/training/basics/data-storage/index.html
SharedPreferences适用于存储少量的键值对数据,由框架管理,可以只供本程序使用也可共享。
Note: The
SharedPreferences
APIs are only for reading and writing key-value pairs and you should not confuse them with thePreference
APIs, which help you build a user interface for your app settings (although they useSharedPreferences
as their implementation to save the app settings). For information about using thePreference
APIs, see the Settings guide.
不要跟Preference搞混了,两者是两回事。
getSharedPreferences()
— Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from anyContext
in your app.getPreferences()
— Use this from anActivity
if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name.
以上两种方法都可以获取存储键值对的容器,第一个需要给定参数选择容器,而第二个方法取回默认的容器。
Context context = getActivity(); SharedPreferences sharedPref = context.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE);When naming your shared preference files, you should use a name that's uniquely identifiable to your app, such as
"com.example.myapp.PREFERENCE_FILE_KEY"
名字最好是本程序特有的,比如com.xxxxxx.名字参数后面跟着是模式,可以是私有,但是如果是MODE_WORLD_READABLE
or MODE_WORLD_WRITEABLE
,那么其他程序都能使用。
写数据:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(getString(R.string.saved_high_score), newHighScore); editor.commit();
访问数据:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); int defaultValue = getResources().getInteger(R.string.saved_high_score_default); long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
All Android devices have two file storage areas: "internal" and "external" storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable storage medium such as a micro SD card (external storage). Some devices divide the permanent storage space into "internal" and "external" partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not. The following lists summarize the facts about each storage space.
Internal storage:
- It's always available.
- Files saved here are accessible by only your app by default.
- When the user uninstalls your app, the system removes all your app's files from internal storage.
Internal storage is best when you want to be sure that neither the user nor other apps can access your files.
External storage:
- It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
- It's world-readable, so files saved here may be read outside of your control.
- When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from
getExternalFilesDir()
.External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.
用文件系统存储文件,有内部外部分之分
内部:1、肯定不会消失;2、只有本程序能访问;3、用户删除你程序的时候,该目录也同样被删;
外部:1、不一样存在;2、共享型,谁都可以访问修改;3、程序被卸载时目录一般情况下不会被删除;
Tip: Although apps are installed onto the internal storage by default, you can specify the
android:installLocation
attribute in your manifest so your app may be installed on external storage. Users appreciate this option when the APK size is very large and they have an external storage space that's larger than the internal storage. For more information, see App Install Location.
程序存放的地方可以自己定义。
权限的设置:
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
但是如果是存在内部的话,不用权限。
内部的存储有文件和缓存两种。(缓存最好是手动删除)
File file = new File(context.getFilesDir(), filename);
内部的使用方法。作为选择,还有以下的方法:
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(); }
缓存的写法:
public File getTempFile(Context context, String url) { 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; }
Because the external storage may be unavailable—such as when the user has mounted the storage to a PC or has removed the SD card that provides the external storage—you should always verify that the volume is available before accessing it. You can query the external storage state by calling
getExternalStorageState()
. If the returned state is equal toMEDIA_MOUNTED
, then you can read and write your files.
由于外部可能会有失效的时候,所以每次使用的时候必须getExternalStorageState()检测一下是否为MEDIA_MOUNTED,如果是,则可以读和写。
/* 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; }
外部同样可以存储私有和共有文件。
共有文件的代码写法:主要是Environment.getExternalStoragePublicDirectory()
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; }
If none of the pre-defined sub-directory names suit your files, you can instead call
getExternalFilesDir()
and passnull
. This returns the root directory for your app's private directory on the external storage.
关于存储文件的空间问题:
If you know ahead of time how much data you're saving, you can find out whether sufficient space is available without causing an
IOException
by callinggetFreeSpace()
orgetTotalSpace()
. These methods provide the current available space and the total space in the storage volume, respectively. This information is also useful to avoid filling the storage volume above a certain threshold.However, the system does not guarantee that you can write as many bytes as are indicated by
getFreeSpace()
. If the number returned is a few MB more than the size of the data you want to save, or if the file system is less than 90% full, then it's probably safe to proceed. Otherwise, you probably shouldn't write to storage.Note: You aren't required to check the amount of available space before you save your file. You can instead try writing the file right away, then catch an
IOException
if one occurs. You may need to do this if you don't know exactly how much space you need. For example, if you change the file's encoding before you save it by converting a PNG image to JPEG, you won't know the file's size beforehand.
myFile.delete();//外部文件删除方法 myContext.deleteFile(fileName);//内部文件删除方法
使用数据库
官方的建议是,使用下面的方法建立数据库:
public final class FeedReaderContract { // To prevent someone from accidentally instantiating the contract class, // give it an empty constructor. 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 + ... // Any other options for the CREATE command " )"; private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; public FeedReaderContract() {} /* Inner class that defines the table contents */ 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"; ... } }
就好像在内部存储,程序被卸载的时候数据库就被删了。
使用SQLiteOpenHelper的好处在于,系统会自动在适合的时候创建和更新数据包。使用getWritableDatabase()
or getReadableDatabase()
操作时,记得不要再UI线程操作。
To use
SQLiteOpenHelper
, create a subclass that overrides theonCreate()
,onUpgrade()
andonOpen()
callback methods. You may also want to implementonDowngrade()
, but it's not required.
public class FeedReaderDbHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. 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); } public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } }
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
插入数据:
// Gets the data repository in write mode SQLiteDatabase db = mDbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_CONTENT, content); // Insert the new row, returning the primary key value of the new row long newRowId; newRowId = db.insert( FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values);
insert()的第一参数为表名,第二参数为可以为空的列名。
读取数据库:
SQLiteDatabase db = mDbHelper.getReadableDatabase(); // Define a projection that specifies which columns from the database // you will actually use after this query. String[] projection = { FeedEntry._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_UPDATED, ... }; // How you want the results sorted in the resulting Cursor String sortOrder = FeedEntry.COLUMN_NAME_UPDATED + " DESC"; Cursor c = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order ); cursor.moveToFirst(); long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID) );
删除数据:
// Define 'where' part of query. String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; // Specify arguments in placeholder order. String[] selectionArgs = { String.valueOf(rowId) }; // Issue SQL statement. db.delete(table_name, selection, selectionArgs);
更新数据:
SQLiteDatabase db = mDbHelper.getReadableDatabase(); // New value for one column ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the ID String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(rowId) }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);