移动设备需要存储数据,处理数据并输出处理后的信息。

主题一:存储键值对

If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs. 使用SharedPreferences存储键值对

A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. SharedPreferences实例实际上指向的是一个文件。Each SharedPreferences file is managed by the framework and can be private or shared.

Note: The SharedPreferences APIs are only for reading and writing key-value pairs and you should not confuse them with the Preference APIs, which help you build a user interface for your app settings (although they use SharedPreferences as their implementation to save the app settings). SharedPreferences只能用于存储键值对。

获取SharedPreferences实例

You can create a new shared preference file or access an existing one by calling one of two methods:

1. 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 any Context in your app.

2. getPreferences() — Use this from an Activity 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.

其一,为SharedPreferences实例指定了文件名;其二,没有指定文件名,但该SharedPreferences属于当前的Activity。

Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences(
getString(R.string.preference_file_key), Context.MODE_PRIVATE);

获取到以 R.string.preference_file_key 指定的SharedPreferences文件实例,并以Private的方式访问(只能在本App内部访问)。

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

或者是上述情况:

Caution: If you create a shared preferences file with MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE, then any other apps that know the file identifier can access your data.

若以MODE_WORLD_READABLE或者是MODE_WORLD_WRITEABLE创建文件时,其他App也可以获取到该数据;这样可能会引起安全问题。

向SharedPreferences文件写数据

To write to a shared preferences file, create a SharedPreferences.Editor by calling edit() on your SharedPreferences. Pass the keys and values you want to write with methods such as putInt() and putString(). Then call commit() to save the changes.

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(getString(R.string.saved_high_score), newHighScore);
editor.commit();

从SharedPreferences文件中读取数据

To retrieve values from a shared preferences file, call methods such as getInt() and getString(), providing the key for the value you want, and optionally a default value to return if the key isn't present.

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);

主题二:存储文件

Android uses a file system that's similar to disk-based file systems on other platforms. This lesson describes how to work with the Android file system to read and write files with the File APIs.

A File object issuited to reading or writing large amounts of data in start-to-finish order without skipping around. 流式数据特别使用File的方式存储

存储内部还是存储外部?

All Android devices have two file storage areas: "internal" and "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. 所有的Android文件系统都有两个文件存储区域:internal和external,这两个分区来自于早先的Android系统;当时的Android系统内置了不可变的内存internal和一个类似于SD Card的可卸载存储部件external。之后有一些设备将"internal" 与 "external" 都做成了不可卸载的内置存储,虽然如此,但是这一整块还是从逻辑上有被划分为"internal"与"external"的。只是现在不再以是否可卸载进行区分了。

外部存储可以通过物理介质提供(如SD卡),也可以通过将内部存储中的一部分封装而成,设备可以有多个外部存储实例。

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.

获取外部存储权限

To write to the external storage, you must request the WRITE_EXTERNAL_STORAGE permission in your manifest file.

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

Caution: Currently, all apps have the ability to read the external storage without a special permission. However, this will change in a future release. If your app needs to read the external storage (but not write to it), then you will need to declare the READ_EXTERNAL_STORAGE permission. To ensure that your app continues to work as expected, you should declare this permission now, before the change takes effect.

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

对于Internal Storage的读写权限:You don’t need any permissions to save files on the internal storage. Your application always has permission to read and write files in its internal storage directory.

在Internal Storage中存储文件

首先需要获取Internal Storage的目录以及相关的File实例

getFilesDir(): Returns a File representing an internal directory for your app.

getCacheDir(): Returns a File representing an internal directory for your app's temporary cache files. Be sure to delete each file once it is no longer needed and implement a reasonable size limit for the amount of memory you use at any given time, such as 1MB. If the system begins running low on storage, it may delete your cache files without warning.

使用下述方法获取到内部存储的文件实例:上述方法获取到的文件路径--> /data/data/包名...

File file = new File(context.getFilesDir(), filename);

Alternatively, you can call openFileOutput() to get a FileOutputStream that writes to a file in your internal directory.

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();
}

或者是获取到Cached中的文件:

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;
}

对于文件权限的说明:Note: Your app's internal storage directory is specified by your app's package name in a special location of the Android file system. Technically, another app can read your internal files if you set the file mode to be readable. However, the other app would also need to know your app package name and file names. Other apps cannot browse your internal directories and do not have read or write access unless you explicitly set the files to be readable or writable. So as long as you use MODE_PRIVATE for your files on the internal storage, they are never accessible to other apps.

在外部存储中存储文件

You can query the external storage state by calling getExternalStorageState(). If the returned state is equal to MEDIA_MOUNTED, then you can read and write your files. 对于外部存储的可能状态,需要对当前状态进行判断;如果是Unmounted状态,是不能够读写的。

/* 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;
}

对于存放在外部存储设备中的文件,有以下两种情况:

Public files: Files that should be freely available to other apps and to the user. When the user uninstalls your app, these files should remain available to the user. For example, photos captured by your app or other downloaded files.

Private files:Files that rightfully belong to your app and should be deleted when the user uninstalls your app. Although these files are technically accessible by the user and other apps because they are on the external storage, they are files that realistically don't provide value to the user outside your app. When the user uninstalls your app, the system deletes all files in your app's external private directory. For example, additional resources downloaded by your app or temporary media files.

如果想要在外部存储空间中存放public files,use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage. 这个方法会需要带有一个特定的参数来指定这些public的文件类型,以便于与其他public文件进行分类。参数类型包括DIRECTORY_MUSIC 或者 DIRECTORY_PICTURES. getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)--> /storage/sdcard0/Pictures

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;
}

如果想要在外部存储空间存放private files,you can acquire the appropriate directory by calling getExternalFilesDir() and passing it a name indicating the type of directory you'd like. 每一个以这种方式创建的目录都会被添加到封装我们app目录下的参数文件夹external storage中(如下则是albumName)。这下面的文件会在用户卸载我们的app时被系统删除。

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;
}

如果刚开始的时候,没有预定义的子目录存放我们的文件,可以在 getExternalFilesDir()方法中传递null. 它会返回app在external storage下的private的根目录。

Remember that getExternalFilesDir() creates a directory inside a directory that is deleted when the user uninstalls your app. If the files you're saving should remain available after the user uninstalls your app—such as when your app is a camera and the user will want to keep the photos—you should instead use getExternalStoragePublicDirectory().

Regardless of whether you use getExternalStoragePublicDirectory() for files that are shared or getExternalFilesDir() for files that are private to your app, it's important that you use directory names provided by API constants like DIRECTORY_PICTURES. These directory names ensure that the files are treated properly by the system. For instance, files saved in DIRECTORY_RINGTONES are categorized by the system media scanner as ringtones instead of music.

并没有强制要求在写文件之前去检查剩余容量;我们可以尝试先做写的动作,然后通过捕获 IOException。这种做法仅适合于事先并不知道想要写的文件的确切大小。例如,如果在把PNG图片转换成JPEG之前,我们并不知道最终生成的图片大小是多少。

获取存储空间大小

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 calling getFreeSpace() or getTotalSpace(). 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.

删除文件

删除文件方式:

myFile.delete();

如果文件存储在内部存储中:

myContext.deleteFile(fileName);

Note: When the user uninstalls your app, the Android system deletes the following:

All files you saved on internal storage

All files you saved on external storage using getExternalFilesDir().

However, you should manually delete all cached files created with getCacheDir() on a regular basis and also regularly delete other files you no longer need.

主题三:使用SQL Database存储数据

Saving data to a database is ideal for repeating or structured data, such as contact information. The APIs you'll need to use a database on Android are available in the android.database.sqlite package.

建立数据库结构和约定 --> 也就是数据库的“骨架”

Schema是一种DB结构的正式声明,用于表示数据库的组成结构。Schema是从创建DB的SQL语句中生成的。

Contract类用于描述创建的数据库所需要的静态字段信息,指定了Schema样式。Contract Class是一些常量的容器,定义了URIs、表名、列名等。这个Contract类运行在同一个包下与其他类使用同样的常量,并让我们在一个地方修改列明,自动传递到整个全局。

组织Contract类的一种方式是:在类的根层级定义一些全局变量,然后为每一个Table来创建内部类。

下述定义了表名和该表的列名:

public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// give it an empty constructor.
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";
...
}
}

Note: By implementing the BaseColumns interface, your inner class can inherit a primary key field called _ID that some Android classes such as cursor adaptors will expect it to have. It's not required, but this can help your database work harmoniously with the Android framework. 通过实现BaseColumns的接口,内部类可以继承一个名为_ID的主键,对于Android里面的一些类似Cursor Adapter类是很有必要的。这样能够使我们的DB与Android的Framework更好的契合。

创建数据库

实现和创建数据库和对应的数据表

下面是一些典型的创建和删除Table的语句:

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;

类似于保存文件到设备的Internal Storage,Android系统会将DB保存到程序的Private空间中,不可被其他程序访问。

当使用SQLiteOpenHelper类中的APIs时,系统会对哪些有可能比较耗时的操作(例如:创建与更新等),在真正需要执行的时候才去运行,而不是在App刚启动的时候就去运行。我们所需要做的仅仅是执行getWritableDatabase()或者getReadableDatabase()。

Note: Because they can be long-running, be sure that you call getWritableDatabase() or getReadableDatabase() in a background thread, such as with AsyncTask or IntentService.

为了使用SQLiteOpenHelper,需要创建一个子类并重写onCreate()、onUpgrade()、onOpen()等回调。

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);
}
}

为了操作数据库,需要获取到OpenHelper实例:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

写数据

写入数据库中表的数据通过ContentValues对象载入,并执行insert()添加到数据库的对应表中。

// 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);

读数据

查询数据库,使用query(),并传递需要查询的条件,返回Cursor对象。

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中的方法获取Cursor附带的数据:

cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);

删除数据

和查询数据库一样,删除数据同样需要提供一些删除标准。DB的API提供了一个防止SQL注入的机制来创建查询与删除标准。

该机制把删除和查询语句划分为两部分:选项条件和选项参数。条件定义了查询的列的特征,参数用于测试是否符合前面的条件。

// 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);

更新数据

修改数据,使用update();该方法结合了插入和删除语法。

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);

P.S.

SQL Injection:(随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码时没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入)

(数据存储)Android系统存储数据的更多相关文章

  1. Android开发_Android数据的四种存储方式

    Android系统一共提供了四种数据存储方式.分别是:SharePreference.SQLite.Content Provider和File.由于Android系统中,数据基本都是私有的的,都是存放 ...

  2. Android五种数据存储方式

    android 五种数据存储 :SharePreferences.SQLite.Contert Provider.File.网络存储 Android系统提供了四种存储数据方式.分别为:SharePre ...

  3. [Android]Android数据的四种存储方式

    存储方式 Android提供以下四种存储方式: SharePreference SQLite File ContentProvider Android系统中数据基本都是私有的,一般存放在“data/d ...

  4. [转][Android]Android数据的四种存储方式

    android.database.sqlite类 SQLiteQueryBuilder java.lang.Object android.database.sqlite.SQLiteQueryBuil ...

  5. Android的存储方式

    Android常见的四种存储方式: 1. SharePreference 2. File 3. ContentProvider 4. SQLite 第一种: 保存在应用程序私有的文件夹下---- 只有 ...

  6. Android内存解析(二)— 详解内存,内部存储和外部存储

    总述 觉得十分有必要搞清楚内存,内部存储和外部存储的区别,还有我们在开发中真正将数据存在了手机的哪儿. 先提一个问题:手机设置的应用管理中,每个App下都有清除数据和清除缓存,清除的分别是哪里的数据? ...

  7. Android网络之数据解析----SAX方式解析XML数据

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  8. Android系统默认设置

    修改Settings源码,可修改系统设置项,Settings数据被存放于com.android.providers.settings/databases/settings.db中,如果想修改系统启动后 ...

  9. Android系统的五种数据存储形式(二)

    之前介绍了Android系统下三种数据存储形式,今天补充介绍另外两种,分别是内容提供者和网络存储.有些人可能认为内存提供者和网络存储更偏向于对数据的操作而不是数据的存储,但这两种方式确实与数据有关,所 ...

随机推荐

  1. mysql一列相同另一列相加

    select name, sum(number) total from test group by name;

  2. K3 WISE 开发插件《SQL语句WHERE查询-范围查询/模糊查询》

    0.存储过程开头变量定义 @FBeginDate varchar(10), --单据起始日期 @FEndDate varchar(10), --单据截止日期. @FItemID varchar(50) ...

  3. github.com加速节点

    github.com加速节点 192.30.253.118 github.com192.30.253.119 github.com93.46.8.89 github.com

  4. DDD工作流持久化(十六)

    找到对应的sql文件执行sql语句 产生如下的表: 添加引用: 添加命名空间: using System.Activities.DurableInstancing; using System.Runt ...

  5. Leetcode刷题第20天

    一.找树左下角的值 题目:513. Find Bottom Left Tree Value C++ Soution 1: /** * Definition for a binary tree node ...

  6. 根据现有的XML文件生成其对应的实体类

    方法如下: 1.将完整的Xml文本复制一下, 2.在vs2013(或以上版本) .net4.5项目下建立一个类文件, 3.依次选择菜单:编辑->选择性粘贴->将XML粘贴为类.

  7. 无法连接ssh,fatal: daemon() failed: No such device

    今天发现一个服务器的sshd无法启动,查看/var/log/secure里发现:fatal: daemon() failed: No such device 解决办法: rm /dev/null /d ...

  8. [转] Javascript中理解发布--订阅模式

    发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...

  9. HTML LIST 输入框自动查询追加框,自动过滤 HTML5

    <!DOCTYPE HTML> <html> <body> <form action="/example/html5/demo_form.asp&q ...

  10. BZOJ1208 [HNOI2004]宠物收养所 splay

    原文链接http://www.cnblogs.com/zhouzhendong/p/8085803.html 题目传送门 - BZOJ1208 题意概括 有两种数,依次加入. 规则为下: 如果当前剩余 ...