Android doc|Getting Started|部分 --转载 保存数据

来源:互联网 发布:华为软件研发面试经验 编辑:程序博客网 时间:2024/05/18 03:26

保存数据

大多数 Android 应用需要保存数据,即使仅保存在 onPause() 过程中与应用状态有关的信息,以便用户进度不会丢失 。 大多数非平凡应用也需要保存用户设置,并且有些应用必须在文件和数据库中管理大量的信息。 本课向您介绍 Android 中的主要数据存储选项,包括:

在共享的

  1. 首选项文件中保存简单数据类型的键值对
  2. 在 Android 的文件系统中保存任意文件
  3. 使用 SQLite 管理的数据库

课程

  • 保存键值集

    学习使用在键值对中存储少量信息的共享首选项文件。(shared preferences)
  • 保存文件

    学习保存基本文件,比如存储一般按顺序读取的较长数据序列 。
  • 在 SQL 数据库中保存数据

    学习使用 SQLite 数据库读写结构化数据。

Saving Key-Value Sets

If you have a relatively small collection of key-values that you’d like to save, you should use the SharedPreferences APIs. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. Each SharedPreferences file is managed by the framework and can be private or shared.

This class shows you how to use the SharedPreferences APIs to store and retrieve simple values.

如果您有想要保存的相对较小键值集合,您应使用 SharedPreferences API。 SharedPreferences 对象指向包含键值对的文件并提供读写这些文件的简单方法。 每个 SharedPreferences 文件由框架进行管理并且可以专用或共享。

本课向您展示如何使用 SharedPreferences API 存储和检索简单的值。

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). For information about using the Preference APIs, see the Settings guide.

注意:SharedPreferences API 仅用于读写键值对,您不得将其与 Preference API 混淆,后者帮助您为您的应用设置构建用户界面(尽管它们使用 SharedPreferences 作为其实现以保存应用设置)。 有关使用 Preference API 的信息,请参阅设置指南。

Get a Handle to a SharedPreferences
获取共享首选项的句柄
You can create a new shared preference file or access an existing one by calling one of two methods:
您可以通过调用以下两种方法之一创建新的共享首选项文件或访问现有的文件:

  • 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.
  • 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.
  • getSharedPreferences() — 如果您需要按照您用第一个参数指定的名称识别的多个共享首选项文件,请使用此方法。
    您可以从您的应用中的任何 Context 调用此方法。
  • getPreferences() — 如果您只需使用Activity的一个共享首选项,请从 Activity 中使用此方法。
    因为此方法会检索属于该Activity的默认共享首选项文件,您无需提供名称。

For example, the following code is executed inside a Fragment. It accesses the shared preferences file that’s identified by the resource string R.string.preference_file_key and opens it using the private mode so the file is accessible by only your app.
例如,以下代码在 Fragment 内执行。它访问通过资源字符串 R.string.preference_file_key 识别的共享首选项文件并且使用专用模式打开它,从而仅允许您的应用访问文件。

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"

Alternatively, if you need just one shared preference file for your activity, you can use the getPreferences() method:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

命名您的共享首选项文件时,您应使用对于您的应用而言唯一可识别的名称,比如 "com.example.myapp.PREFERENCE_FILE_KEY"
或者,如果您只需Activity的一个共享首选项文件,您可以使用 getPreferences() 方法:
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 的共享首选项文件,那么知道文件标识符的任何其他应用都可以访问您的数据。

写入共享首选项
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. For example:
要写入共享首选项文件, 请通过对您的 SharedPreferences 调用 edit() 来创建一个 SharedPreferences.Editor。

传递您想要使用诸如 putInt() 和 putString() 方法写入的键和值。然后调用 commit() 以保存更改。例如:

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

从共享首选项读取信息
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. For example:
要从共享首选项文件中检索值,请调用诸如 getInt() 和 getString() 等方法,为您想要的值提供键,并根据需要提供要在键不存在的情况下返回的默认值。 例如:

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 is suited to reading or writing large amounts of data in start-to-finish order without skipping around. For example, it’s good for image files or anything exchanged over a network.

This lesson shows how to perform basic file-related tasks in your app. The lesson assumes that you are familiar with the basics of the Linux file system and the standard file input/output APIs in java.io.

Android 使用与其他平台上基于磁盘的文件系统类似的文件系统。 本课程讲述如何使用 Android 文件系统通过 File API 读取和写入文件。

File 对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。 例如,它适合于图像文件或通过网络交换的任何内容。

本课程展示如何在您的应用中执行基本的文件相关任务。本课程假定您熟悉 Linux 文件系统的基础知识和 java.io 中的标准文件输入/输出 API。

选择内部或外部存储
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.
所有 Android 设备都有两个文件存储区域:“内部”和“外部”存储。这些名称在 Android 早期产生,当时大多数设备都提供内置的非易失性内存(内部存储),以及移动存储介质,比如微型 SD 卡(外部存储)。一些设备将永久性存储空间划分为“内部”和“外部”分区,即便没有移动存储介质,也始终有两个存储空间,并且无论外部存储设备是否可移动,API 的行为均一致。以下列表汇总了关于各个存储空间的实际信息。

Internal storage:

  • It’s always available.
  • Files saved here are accessible by only your app.
  • 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.

外部存储:

  • 它并非始终可用,因为用户可采用 USB 存储的形式装载外部存储,并在某些情况下会从设备中将其删除。
  • 它是全局可读的,因此此处保存的文件可能不受您控制地被读取。
  • 当用户卸载您的应用时,只有在您通过 getExternalFilesDir()
  • 将您的应用的文件保存在目录中时,系统才会从此处删除您的应用的文件。
    对于无需访问限制以及您希望与其他应用共享或允许用户使用电脑访问的文件,外部存储是最佳位置。

Note: Before Android N, internal files could be made accessible to other apps by means of relaxing file system permissions. This is no longer the case. If you wish to make the content of a private file accessible to other apps, your app may use the FileProvider. See Sharing Files.
注意:在Android N之前,内部文件其他app都可以访问,也就是说文件系统权限比较松散。以后将不会再是这种情况。如果你希望你的私有文件内容被其他apps访问,你的app应该使用FileProvider,详见文件共享。

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.
提示:尽管应用默认安装在内部存储中,但您可在您的宣示说明中指定 android:installLocation 属性,这样您的应用便可安装在在外部存储中。 当 APK 非常大且它们的外部存储空间大于内部存储时,用户更青睐这个选择。 如需了解详细信息,请参阅应用安装位置。

Obtain Permissions for External Storage
获取外部存储的权限
To write to the external storage, you must request the WRITE_EXTERNAL_STORAGE permission in your manifest file:
要向外部存储写入信息,您必须在您的宣示说明文件中请求 WRITE_EXTERNAL_STORAGE 权限。

<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.
注意: 目前,所有应用都可以读取外部存储,而无需特别的权限。 但这在将来版本中会进行更改。如果您的应用需要读取外部存储(但不向其写入信息),那么您将需要声明 READ_EXTERNAL_STORAGE 权限。 要确保您的应用继续正常工作,您应在更改生效前声明此权限。

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

However, if your app uses the WRITE_EXTERNAL_STORAGE permission, then it implicitly has permission to read the external storage as well.
但是,如果您的应用使用 WRITE_EXTERNAL_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.
您无需任何权限,即可在内部存储中保存文件。 您的应用始终具有在其内部存储目录中进行读写的权限。

Save a File on Internal Storage
将文件保存在内部存储中

When saving a file to internal storage, you can acquire the appropriate directory as a File by calling one of two methods:
在内部存储中保存文件时,您可以通过调用以下两种方法之一获取作为 File 的相应目录:

getFilesDir()
Returns a File representing an internal directory for your app.
返回表示您的应用的内部目录的 File 。
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.
返回表示您的应用临时缓存文件的内部目录的 File 。 务必删除所有不再需要的文件并对在指定时间您使用的内存量实现合理大小限制,比如,1MB。 如果在系统即将耗尽存储,它会在不进行警告的情况下删除您的缓存文件。

To create a new file in one of these directories, you can use the File() constructor, passing the File provided by one of the above methods that specifies your internal storage directory. For example:
要在这些目录之一中新建文件,您可以使用 File() 构造函数,传递指定您的内部存储目录的上述方法之一所提供的 File 。例如:

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. For example, here’s how to write some text to a file:
或者,您可以调用 openFileOutput() 获取写入到内部目录中的文件的 FileOutputStream 。例如,此处显示如何向文件写入一些文本:

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

Or, if you need to cache some files, you should instead use createTempFile(). For example, the following method extracts the file name from a URL and creates a file with that name in your app’s internal cache directory:
或者,如果您需要缓存某些文件,您应改用 createTempFile()。例如,以下方法从 URL 提取文件名并在您的应用的内部缓存目录中以该名称创建文件:

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.

注意: 您的应用的内部存储设备目录由您的应用在Android 文件系统特定位置中的软件包名称指定。在技术上,如果您将文件模式设置为可读,另一个应用可以读取您的内部文件。 但是,另一个应用也需要知道您的应用的软件包名称和文件名。 其他应用无法浏览您的内部目录并且没有读写权限,除非您明确将文件设置为可读或可写。 只要您为内部存储上的文件使用 MODE_PRIVATE, 其他应用便从不会访问它们。

Save a File on External Storage
将文件保存在外部存储中
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 to MEDIA_MOUNTED, then you can read and write your files. For example, the following methods are useful to determine the storage availability:
由于外部存储可能不可用—比如,当用户已将存储装载到电脑或已移除提供外部存储的 SD 卡时—因此,在访问它之前,您应始终确认其容量。 您可以通过调用 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;}

Although the external storage is modifiable by the user and other apps, there are two categories of files you might save here:

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.

尽管外部存储可被用户和其他应用进行修改,但您可在此处保存两类文件:

公共文件
应供其他应用和用户自由使用的文件。 当用户卸载您的应用时,用户应仍可以使用这些文件。
例如,您的应用拍摄的照片或其他已下载的文件。

私有文件
本属于您的应用且应在用户卸载您的应用时删除的文件。尽管这些文件在技术上可被用户和其他应用访问(因为它们在外部存储上),它们是实际上不向您的应用之外的用户提供值的文件。当用户卸载您的应用时,系统会删除应用外部专用目录中的所有文件。
例如,您的应用下载的其他资源或临时介质文件。

If you want to save public files on the external storage, use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage. The method takes an argument specifying the type of file you want to save so that they can be logically organized with other public files, such as DIRECTORY_MUSIC or DIRECTORY_PICTURES. For example:
如果您要使用外部存储上的公共文件,请使用 getExternalStoragePublicDirectory() 方法获取表示外部存储上相应目录的 File 。该方法使用指定 您想要保存以便它们可以与其他公共文件在逻辑上组织在一起的文件类型的参数,比如 DIRECTORY_MUSIC 或 DIRECTORY_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;}

If you want to save files that are private to your app, you can acquire the appropriate directory by calling getExternalFilesDir() and passing it a name indicating the type of directory you’d like. Each directory created this way is added to a parent directory that encapsulates all your app’s external storage files, which the system deletes when the user uninstalls your app.

For example, here’s a method you can use to create a directory for an individual photo album:
如果您要保存您的应用专用文件,您可以通过调用 getExternalFilesDir() 获取相应的目录并向其传递指示您想要的目录类型的名称。 通过这种方法创建的各个目录将添加至封装您的应用的所有外部存储文件的父目录,当用户卸载您的应用时,系统会删除这些文件。

例如,您可以使用以下方法来创建个人相册的目录:

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 pass null. This returns the root directory for your app’s private directory on the external storage.

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.

如果没有适合您文件的预定义子目录名称,您可以改为调用 getExternalFilesDir() 并传递 null。这将返回外部存储上您的应用的专用目录 的根目录。

切记,getExternalFilesDir() 在用户卸载您的应用时删除的目录内创建目录。如果您正保存的文件应在用户卸载您的应用后仍然可用—比如,当您的应用是照相机并且用户要保留照片时—您应改用 getExternalStoragePublicDirectory()。

无论您对于共享的文件使用 getExternalStoragePublicDirectory() 还是对您的应用专用文件使用 getExternalFilesDir() ,您使用诸如 DIRECTORY_PICTURES 的 API 常数提供的目录名称非常重要。 这些目录名称可确保系统正确处理文件。 例如,保存在 DIRECTORY_RINGTONES 中的文件由系统介质扫描程序归类为铃声,而不是音乐。

查询可用空间
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.

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.
如果您事先知道您将保存的数据量,您可以查出是否有足够的可用空间,而无需调用 getFreeSpace() 或 getTotalSpace() 引起 IOException 。 这些方法分别提供目前的可用空间和存储卷中的总空间。 此信息也可用来避免填充存储卷以致超出特定阈值。

但是,系统并不保证您可以写入与 getFreeSpace() 指示的一样多的字节。如果返回的数字比您要保存的数据大小大出几 MB,或如果文件系统所占空间不到 90%,则可安全继续操作。否则,您可能不应写入存储。

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.
注意:保存您的文件之前,您无需检查可用空间量。 您可以尝试立刻写入文件,然后在 IOException 出现时将其捕获。 如果您不知道所需的确切空间量,您可能需要这样做。 例如,如果在保存文件之前通过将 PNG 图像转换成JPEG 更改了文件的编码,您事先将不知道文件的大小。

删除文件
You should always delete files that you no longer need. The most straightforward way to delete a file is to have the opened file reference call delete() on itself.
您应始终删除不再需要的文件。删除文件最直接的方法是让打开的文件参考自行调用 delete()。

myFile.delete();

If the file is saved on internal storage, you can also ask the Context to locate and delete a file by calling deleteFile():
如果文件保存在内部存储中,您还可以请求 Context 通过调用 deleteFile() 来定位和删除文件:

myContext.deleteFile(fileName);

注意:当用户卸载您的应用时,Android 系统会删除以下各项:

  • All files you saved on internal storage
  • All files you saved on external storage using getExternalFilesDir().
  • 您保存在内部存储中的所有文件
  • 您使用 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.
但是,您应手动删除使用 getCacheDir() 定期创建的所有缓存文件并且定期删除不再需要的其他文件。

在 SQL 数据库中保存数据

Saving data to a database is ideal for repeating or structured data, such as contact information. This class assumes that you are familiar with SQL databases in general and helps you get started with SQLite databases on Android. The APIs you’ll need to use a database on Android are available in the android.database.sqlite package.

将数据保存到数据库对于重复或结构化数据(比如联系人信息) 而言是理想之选。本课程假定您基本熟悉 SQL 数据库并且可帮助您开始在 Android 中使用 SQLite 数据库。 您在 Android 中使用数据库所需的 API 在 android.database.sqlite 软件包中提供。

Define a Schema and Contract
定义架构和契约
One of the main principles of SQL databases is the schema: a formal declaration of how the database is organized. The schema is reflected in the SQL statements that you use to create your database. You may find it helpful to create a companion class, known as a contract class, which explicitly specifies the layout of your schema in a systematic and self-documenting way.

A contract class is a container for constants that define names for URIs, tables, and columns. The contract class allows you to use the same constants across all the other classes in the same package. This lets you change a column name in one place and have it propagate throughout your code.

A good way to organize a contract class is to put definitions that are global to your whole database in the root level of the class. Then create an inner class for each table that enumerates its columns.
SQL 数据库的主要原则之一是架构:数据库如何组织的正式声明。 架构体现于您用于创建数据库的 SQL 语句。您会发现它有助于创建伴随类,即契约 类,其以一种系统性、自记录的方式明确指定您的架构布局。

契约类是用于定义 URI、表格和列名称的常数的容器。 契约类允许您跨同一软件包中的所有其他类使用相同的常数。 您可以在一个位置更改列名称并使其在您整个代码中传播。

组织契约类的一种良好方法是将对于您的整个数据库而言是全局性的定义放入类的根级别。 然后为枚举其列的每个表格创建内部类。

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 类(比如光标适配器)将需要内部类拥有该字段。 这并非必需项,但可帮助您的数据库与 Android 框架协调工作。

For example, this snippet defines the table name and column names for a single 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";        ...    }}

Create a Database Using a SQL Helper
使用 SQL 辅助工具创建数据库
Once you have defined how your database looks, you should implement methods that create and maintain the database and tables. Here are some typical statements that create and delete a 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;

Just like files that you save on the device’s internal storage, Android stores your database in private disk space that’s associated application. Your data is secure, because by default this area is not accessible to other applications.

A useful set of APIs is available in the SQLiteOpenHelper class. When you use this class to obtain references to your database, the system performs the potentially long-running operations of creating and updating the database only when needed and not during app startup. All you need to do is call getWritableDatabase() or getReadableDatabase().
就像您在设备的内部存储中保存文件那样,Android 将您的数据库保存在私人磁盘空间,即关联的应用。 您的数据是安全的,因为在默认情况下,其他应用无法访问此区域。

SQLiteOpenHelper 类中有一组有用的 API。当您使用此类获取对您数据库的引用时,系统将只在需要之时而不是 应用启动过程中执行可能长期运行的操作:创建和更新数据库。 您只需调用 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.
注意:由于它们可能长期运行,因此请确保您在后台线程中调用 getWritableDatabase() 或 getReadableDatabase() , 比如使用 AsyncTask 或 IntentService。

To use SQLiteOpenHelper, create a subclass that overrides the onCreate(), onUpgrade() and onOpen() callback methods. You may also want to implement onDowngrade(), but it’s not required.

For example, here’s an implementation of SQLiteOpenHelper that uses some of the commands shown above:
要使用 SQLiteOpenHelper,请创建一个 替代 onCreate()、onUpgrade() 和 onOpen() 回调方法的子类。您可能还希望实现 onDowngrade(), 但这并非必需操作。

例如,这里有一个使用如上所示一些命令的 SQLiteOpenHelper 的实现:

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

To access your database, instantiate your subclass of SQLiteOpenHelper:
要访问您的数据库,请实例化 SQLiteOpenHelper 的子类:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Put Information into a Database
将信息输入到数据库
Insert data into the database by passing a ContentValues object to the insert() method:
通过将一个 ContentValues 对象传递至 insert() 方法将数据插入数据库:

// Gets the data repository in write modeSQLiteDatabase db = mDbHelper.getWritableDatabase();// Create a new map of values, where column names are the keysContentValues 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 rowlong newRowId;newRowId = db.insert(         FeedEntry.TABLE_NAME,         FeedEntry.COLUMN_NAME_NULLABLE,         values);

The first argument for insert() is simply the table name.

The second argument tells the framework what to do in the event that the ContentValues is empty (i.e., you did not put any values). If you specify the name of a column, the framework inserts a row and sets the value of that column to null. If you specify null, like in this code sample, the framework does not insert a row when there are no values.

insert() 的第一个参数即为表格名称。第二个参数指定在 ContentValues 为空的情况下框架可在其中插入 NULL 的列的名称(如果您将其设置为 “null”, 那么框架将不会在没有值时插入行。)

Read Information from a Database
从数据库读取信息
To read from a database, use the query() method, passing it your selection criteria and desired columns. The method combines elements of insert() and update(), except the column list defines the data you want to fetch, rather than the data to insert. The results of the query are returned to you in a Cursor object.
要从数据库中读取信息,请使用 query() 方法,将其传递至选择条件和所需列。该方法结合 insert() 和 update() 的元素,除非列列表定义了您希望获取的数据,而不是希望插入的数据。 查询的结果将在 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 CursorString 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    );

To look at a row in the cursor, use one of the Cursor move methods, which you must always call before you begin reading values. Generally, you should start by calling moveToFirst(), which places the “read position” on the first entry in the results. For each row, you can read a column’s value by calling one of the Cursor get methods, such as getString() or getLong(). For each of the get methods, you must pass the index position of the column you desire, which you can get by calling getColumnIndex() or getColumnIndexOrThrow(). For example:
要查看游标中的某一行,请使用 Cursor 移动方法之一,您必须在开始读取值之前始终调用这些方法。 一般情况下,您应通过调用 moveToFirst() 开始,其将“读取位置”置于结果中的第一个条目中。 对于每一行,您可以通过调用 Cursor 获取方法之一读取列的值,比如 getString() 或 getLong()。对于每种获取方法,您必须传递所需列的索引位置,您可以通过调用 getColumnIndex() 或 getColumnIndexOrThrow() 获取。例如:

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

从数据库删除信息
Delete Information from a Database
To delete rows from a table, you need to provide selection criteria that identify the rows. The database API provides a mechanism for creating selection criteria that protects against SQL injection. The mechanism divides the selection specification into a selection clause and selection arguments. The clause defines the columns to look at, and also allows you to combine column tests. The arguments are values to test against that are bound into the clause. Because the result isn’t handled the same as a regular SQL statement, it is immune to SQL injection.
要从表格中删除行,您需要提供识别行的选择条件。 数据库 API 提供了一种机制,用于创建防止 SQL 注入的选择条件。 该机制将选择规范划分为选择子句和选择参数。 该子句定义要查看的列,还允许您合并列测试。 参数是根据捆绑到子句的项进行测试的值。由于结果并未按照与常规 SQL 语句相同的方式进行处理,它不受 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 a Database
更新数据库
When you need to modify a subset of your database values, use the update() method.

Updating the table combines the content values syntax of insert() with the where syntax of delete()
当您需要修改数据库值的子集时,请使用 update() 方法。

更新表格可将insert() 的内容值句法与 delete() 的 where 句法相结合。

SQLiteDatabase db = mDbHelper.getReadableDatabase();// New value for one columnContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);// Which row to update, based on the IDString selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";String[] selectionArgs = { String.valueOf(rowId) };int count = db.update(    FeedReaderDbHelper.FeedEntry.TABLE_NAME,    values,    selection,    selectionArgs);
0 0