Android开发指南——Data Storage

来源:互联网 发布:太阳线直销软件 编辑:程序博客网 时间:2024/05/22 08:30

Android提供了几种数据存储方案以达到应用中数据的持久化存储,你可以根据自己的需要选择相应的存储方案,例如:对于你的应用来说这些数据是否是私有的、其他用户或应用是否可以访问这些数据以及你的数据大概占多大空间。有以下几种存储方案:

  • Shared Preferences:以键值对的形式存储基本数据类型的数据
  • Internal Storage:在设备的内存中存储私有数据
  • External Storage:在SD卡等外部存储设备中存储公共数据
  • SQLite Databases:在SQLite数据库中存储结构化的数据
  • Network Connection:通过网络存储,也即是将数据存放在你的远程Web服务器端

Android提供了一种方式可以让你将应用中的私有数据暴露给其他的应用程序,那就是content provider

使用Shared Preferences存储:

        SharedPreferences 类提供了一个基本的框架,它允许你保存或读取基本数据类型的键值对,你可以使用它存储任何基本数据类型如:boolean, float, int, long, string,除非应用程序被卸载,否则其存储的数据将一直存在。可以通过以下两种方法获得一个SharePreference对象:

  • getSharedPreferences (String name, int mode):当需要使用多个SharedPreferences时,可用该方法,多个SharedPreferences之间以name标识,而mode表示该SharedPreferences的操作模式。
  • getPreferences(int mode):对于你的Activity来说,当只需要一个SharedPreferences时,由于这是你的Activity唯一的SharedPreferences,所以不需要name标识

SharePreference中写值:

  • 调用Editor()方法,返回一个SharedPreferences.Editor
  • 使用putString()、putInt()等方法,添加数据
  • 使用commit()方法提交更改

SharePreference中取值:

使用SharedPreferences的getInt()、getString()、getBoolean()等方法,请看下面的Demo:

public class Calc extends Activity {    public static final String PREFS_NAME = "MyPrefsFile";    @Override    protected void onCreate(Bundle state){       super.onCreate(state);       . . .       // Restore preferences       SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);       boolean silent = settings.getBoolean("silentMode", false);       setSilent(silent);    }    @Override    protected void onStop(){       super.onStop();      // We need an Editor object to make preference changes.      // All objects are from android.context.Context      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);      SharedPreferences.Editor editor = settings.edit();      editor.putBoolean("silentMode", mSilentMode);      // Commit the edits!      editor.commit();    }}

使用Internal Storage内存存储:

你可以将文件直接存储在设备的内存中,默认情况下,存储在内存中的文件对你的应用来说是私有的,其他应用程序不能访问,当用户卸载应用时,文件也随之被移除。
在内存中创建一个文件并将数据写入其中的步骤如下:
  • 调用openFileOutput(FILENAME,Context.MODE_PRIVATE)方法,获取一个FileOutputStream
  • 使用write()方法向文件输出流中写数据。
  • 调用close()方法关闭文件输出流。

如下所示:

String FILENAME = "hello_file";String string = "hello world!";FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);fos.write(string.getBytes());fos.close();

        MODE_PRIVATE模式表示创建或替代与FILENAME同名的私有文件,其他可用的模式有: MODE_APPEND、MODE_WORLD_READABLE 及MODE_WORLD_WRITEABLE.

从内存中读取一个文件的步骤如下:

  • 调用openFileInput(FILENAME)方法,并将需要读取的文件名作为入参,函数返回一个FileInputStream,
  • 使用read()方法从文件输入流中读取字节
  • 调用close()方法关闭文件输入流

如果在编译期你想在应用里保存一个静态文件,可以将该文件放在res/raw/目录下,你可以调用openRawResource(R.raw.fileName),将该文件资源id作为入参传入,该方法返回一个InputStream,可以用来读取文件,注意:你不能对原始文件执行写操作

保存缓存文件:

如果你想缓存一些数据,而不是持久化存储,你应该使用Context的getCacheDir()方法来打开一个文件目录,该文件目录是用来在内存中存储应用程序缓存文件的。当设备的内存空间很低时,Android会删除这些缓存文件以回收内存,然而,你不应该指望系统去为你清理这些缓存文件,你应该经常维护这些缓存文件以将降低对内存的消耗,通常缓存占用的空间大小不应过高,比如1M左右,当应用程序被卸载时,这些缓存文件也随之被删除。

使用External Storage 外部存储空间:

每一个Android设备都支持一个可共享的“外部存储”,你可以用它来存储文件,它可能是一个可移动存储媒介(SD卡)或者是一个设备内部存储空间(不同于内存,比如手机本身的可用存储空间)存储在外存中的文件是world-readable,可以被用户修改。

访问外部存储空间:

为了读写外部存储空间里的文件,你必须获得 READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限,如下:

<manifest ...>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    ...</manifest>
当你同时需要读写权限时,只需要获得WRITE_EXTERNAL_STORAGE权限就行了,因为该权限默认同时具有可读权限。从Android4.4开始,如果你在读写那些应用程序所私有的文件时,不再需要获取这些权限了。

检查外部存储媒介是否可用:

你可以调用android.os.Environment的getExternalStorageState() 来检查外存是否可用,如下:

/* 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;}
存储可被其他应用共享的文件:

通常新的可被其他应用程序访问的文件应该存放在一个公共的位置,该位置可被其他应用访问,可方便用户复制。当你想创建这样的文件时,你可以使用以下可共享的文件目录:Music/Pictures/,和 Ringtones/,为了获得一个公共目录,调用getExternalStoragePublicDirectory(),传入你想要的目录类型参数,比如:DIRECTORY_MUSICDIRECTORY_PICTURES,DIRECTORY_RINGTONES, 将你的文件保存至相应类型的目录下,系统的媒介浏览器可以在系统中正确的分类你的文件。下面是一个在公共图片目录下创建一个新的相册目录的方法:

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;}
存储应用程序私有的文件:

如果你想创建一个不想被其他应用程序共享的文件,你可以使用外存中的私有存储目录,可以调用getExternalFilesDir()方法来获得外存中的私有存储目录,该方法也接收一个类型参数,来指定子目录的类型,如果你不想指定,传入null即可,然后即可获得你的应用程序私有的存储根目录,私有存储中操作文件的例子如下:

void createExternalStoragePrivateFile() {    // Create a path where we will place our private file on external    // storage.    File file = new File(getExternalFilesDir(null), "DemoFile.jpg");    try {        // Very simple code to copy a picture from the application's        // resource into the external file.  Note that this code does        // no error checking, and assumes the picture is small (does not        // try to copy it in chunks).  Note that if external storage is        // not currently mounted this will silently fail.        InputStream is = getResources().openRawResource(R.drawable.balloons);        OutputStream os = new FileOutputStream(file);        byte[] data = new byte[is.available()];        is.read(data);        os.write(data);        is.close();        os.close();    } catch (IOException e) {        // Unable to create file, likely because external storage is        // not currently mounted.        Log.w("ExternalStorage", "Error writing " + file, e);    }}void deleteExternalStoragePrivateFile() {    // Get path for the file on external storage.  If external    // storage is not currently mounted this will fail.    File file = new File(getExternalFilesDir(null), "DemoFile.jpg");    if (file != null) {        file.delete();    }}boolean hasExternalStoragePrivateFile() {    // Get path for the file on external storage.  If external    // storage is not currently mounted this will fail.    File file = new File(getExternalFilesDir(null), "DemoFile.jpg");    if (file != null) {        return file.exists();    }    return false;}
从Android4.4开始,读写应用的私有目录中的文件不再需要读写权限了,所以你可以声明该权限只在API4.3及以下版本中需要:

<manifest ...>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"                     android:maxSdkVersion="18" />    ...</manifest>
     注意:当用户卸载应用时,所有私有存储目录下的文件都会被删除,所以你不应在私有目录中存储那些最终属于用户的文件,例如用户利用你的应用截取或编辑的图片、用户在你的应用中购买的音乐、电影等文件——所有这样的文件都应该存放在公共目录下。 有时候,一些设备分配内存中的某些区块以作为外存,而同时该设备或许也具有Sd卡,当这种设备运行在Android4.3及以下系统中时, getExternalFilesDir()方法只提供对内存中作为外存使用的那部分区块的可访问权限,此时,你的应用不能读写Sd卡。从Android4.4开始,你可以使用 getExternalFilesDirs()方法,来同时获取这两种存储空间的访问权限,该方法返回一个带有每个位置入口的文件数组,数组中第一个元素代表主外部存储,除非它不可用或已经满了,否则你应该首先使用它。在Android4.3及以下版本中,你可以使用支持库的静态方法ContextCompat.getExternalFilesDirs()来获得对二者的访问,该方法同样返回一个文件数组,但通常只包含一个入口位置。

     另外需注意的一点:尽管通过以上两种方法提供的目录不能被MediaStore content provider所访问,但是拥有 READ_EXTERNAL_STORAGE 权限的其它应用可以访问外部存储空间中的所有文件,也包括它们。如果你需要完全的限制其他应用对你的应用私有文件的访问,那么你应该将你的文件写入内存中。

存储缓存文件:

为了在外存中打开一个作为缓存文件的目录,你可以调用getExternalCacheDir(),如果用户卸载应用,这些文件也会被移除。与以上提到的ContextCompat.getExternalFilesDirs()方法相似,通过调用 ContextCompat.getExternalCacheDirs(),你可以在辅助外部存储空间中获得一个缓存目录。为了预留出空间同时维护应用程序的性能,管理好你的缓存文件,当他们在应用的整个生命周期中不再被使用时,应该将它们删除。

使用Sqlite 数据库存储:

Android提供了对Sqlite的完整支持,你所创建的任何数据库都可以通过名字被应用程序中的所有类访问。通过创建一个 SQLiteOpenHelper的子类并重写 onCreate()方法,在该方法中通过执行Sqlite 命令在数据库中创建表,例如:

public class DictionaryOpenHelper extends SQLiteOpenHelper {    private static final int DATABASE_VERSION = 2;    private static final String DICTIONARY_TABLE_NAME = "dictionary";    private static final String DICTIONARY_TABLE_CREATE =                "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +                KEY_WORD + " TEXT, " +                KEY_DEFINITION + " TEXT);";    DictionaryOpenHelper(Context context) {        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(DICTIONARY_TABLE_CREATE);    }}
然后你可以获得SQLiteOpenHelper的子类的实例,通过调用getWritableDatabase() 和 getReadableDatabase()来获取SQLiteDatabase对象,通过该对象的一些方法来对数据库进行读写。

通过网络连接存储将数据存储在服务器端,或从服务器端读取数据,本篇不再介绍。





0 0